https://bz.apache.org/bugzilla/show_bug.cgi?id=62620
Bug ID: 62620
Summary: Async servlet over HTTP/2 large write data corrupted
Product: Tomcat 9
Version: 9.0.10
Hardware: PC
OS: Linux
Status: NEW
Severity: normal
Priority: P2
Component: Servlet
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: -----
Related to the fix of https://bz.apache.org/bugzilla/show_bug.cgi?id=62614
Test case 1.
import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = {"/largeasyncwrite"}, asyncSupported = true)
public class LargeAsyncWrite extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse
response)
throws IOException {
final AsyncContext asyncContext = request.startAsync();
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/binary");
final ServletOutputStream output = response.getOutputStream();
output.setWriteListener(new WriteListener() {
int i;
byte[] bytes = new byte[65536];
int expectedBytesSentOut;
@Override
public void onWritePossible() throws IOException {
i++;
System.out.println("onWritePossible called " + i + " times");
if (i > 3) {
System.out.println("complete");
System.out.println("expected bytes sent out: " +
expectedBytesSentOut);
asyncContext.complete();
return;
}
int j = 0;
while(output.isReady()) {
System.out.println("start write j = " + j);
output.write(bytes);
expectedBytesSentOut += 65536;
System.out.println("write complete j = " + j);
j++;
}
System.out.println("output.isReady() = " + false);
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
}
});
}
}
Client side, run the command:
$ nghttp "http://localhost:8080/largeasyncwrite" -v
>From the output, sum up the length of DATA frames it received, that's the total
number of bytes actually received.
Check "logs/catalina.out" for the expected bytes sent out, it should equal to
the actual bytes received but not.
Test case 2.
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.AsyncContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = {"/largeasyncwrite2"}, asyncSupported = true)
public class LargeAsynWrite2 extends HttpServlet {
static final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(1);
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse
response)
throws IOException {
final AsyncContext asyncContext = request.startAsync();
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/binary");
final ServletOutputStream output = response.getOutputStream();
output.setWriteListener(
new WriteListener() {
final AtomicInteger i = new AtomicInteger();
final byte[] bytes = new byte[65536];
volatile Future<?> future;
volatile int expectedBytesSentOut;
@Override
public void onWritePossible() {
i.incrementAndGet();
System.out.println("onWritePossible");
if (i.get() > 3) {
System.out.println("complete");
System.out.println("expected bytes sent out: " +
expectedBytesSentOut);
asyncContext.complete();
return;
}
System.out.println(
"I don't have data to write right now, and have to exit
onWritePossible()");
future =
scheduler.schedule(
() -> {
boolean ready;
int j = 0;
while (ready = output.isReady() && i.get() <= 3) {
try {
System.out.println("start write, j = " + j);
output.write(bytes);
expectedBytesSentOut += 65536;
System.out.println("end write, j = " + j);
j++;
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("output.isReady() = " + ready);
if (ready && i.get() > 3) {
System.out.println("complete");
System.out.println("expected bytes sent out: " +
expectedBytesSentOut);
asyncContext.complete();
return;
}
},
2,
TimeUnit.SECONDS);
}
@Override
public void onError(Throwable t) {
if (future != null) {
future.cancel(false);
}
t.printStackTrace();
}
});
}
}
Client side, run the command
$ nghttp "http://localhost:8080/largeasyncwrite2" -v
Also check the actual bytes the client received. Run multiple times, the result
should be reliable and stable.
--
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]