Summary: | Async servlet over HTTP/2 WriteListener does not work because onWritePossible is never called back again | ||
---|---|---|---|
Product: | Tomcat 9 | Reporter: | Dapeng Zhang <zdapeng> |
Component: | Servlet | Assignee: | Tomcat Developers Mailing List <dev> |
Status: | RESOLVED FIXED | ||
Severity: | normal | ||
Priority: | P2 | ||
Version: | 9.0.10 | ||
Target Milestone: | ----- | ||
Hardware: | PC | ||
OS: | Linux |
Description
Dapeng Zhang
2018-08-10 00:16:39 UTC
There is no Tomcat bug here. The test case makes the (invalid) assumption that the network buffers will fill up and isReady() will return false. They don't. Essentially the code enters an infinite loop here: while(output.isReady()) { output.write(bytes); } There are multiple ways you van observe this: - enable debug logging for the HTTP/2 (conf/logging.propeties) - watch network traffic with Wireshark or similar - debug Tomcat My fault again, sorry about that. Update: I couldn't produce the bug with the "curl --output -" command because the output stream is always ready and enters an infinite loop, my bad; However, I can produce it using nghttp (see https://github.com/http2/http2-spec/wiki/Tools) client command, and this does not make it enter infinite loop. I added more debug info in the code to make sure we can see if it's looping or not: @Override public void onWritePossible() throws IOException { i++; System.out.println("onWritePossible called " + i + " times"); if (i > 3) { System.out.println("complete"); asyncContext.complete(); return; } int j = 0; while(output.isReady()) { System.out.println("start write j = " + j); output.write(bytes); System.out.println("write complete j = " + j); j++; } System.out.println("output.isReady() = " + false); } Deploy it to Tomcat and call the nghttp client command $ nghttp "http://127.0.0.1:8080/asyncwrite" -v It hangs. Check "logs/catalina.out", onWritePossible called only 1 time, and never called back again and clearly it's not in an infinite loop this time. onWritePossible called 1 times start write j = 0 write complete j = 0 start write j = 1 write complete j = 1 output.isReady() = false I also tried the same nghttp client to Jetty and Undertow, the onWritePossible callback can be called 4 times and finish. Fixed in: - trunk for 9.0.11 onwards - 8.5.x for 8.5.33 onwards Whoops. Processing of an increase in window size from 0 to >0 was only triggering a write for blocking I/O. The notification for non-blocking was missing. I've added it and the test passes now. Thanks for the report. |