Hi Greg,
Thanks for the insights!
On the question of whether or not the handler should even be invoked when there is a timeout and delayDispatchUntilContent = true, I'd look at it through the lens of the "principle of least surprise"... but unfortunately, you're probably going to surprise someone either way, as you and Simone are already aware (based on the very nice github issue discussion).
All I can add is that I personally was surprised by this change in behavior, and probably would *not* be surprised if already-timed-out requests made it to the handler, because the fact that they're already timed out is only known because of the delayDispatchUntilContent optimization, and they would have timed out inside the handler anyway without the optimization.
Regarding the workaround (setting delayDispatchUntilContent = false), the only thing that worries me is that it still doesn't behave the same as Jetty 9.2.x unless you comment out the three client-side lines that were commented out in the github issue (Thread.sleep() + last output.write() + last output.flush()). If those lines are commented out, then the tested scenario is "a client sends a request without sending the entire body and then tries to read the response"; but with the original lines uncommented, the tested scenario is "a client sends a full request, but pauses temporarily somewhere in the middle long enough to trigger the timeout, finishes sending the full request, and then reads the response".
The second scenario (where the full request is actually sent to the server) is the one we were trying to test, and with Jetty 9.4.x and delayDispatchUntilContent = false you get a SocketException [1] when the client tries to read the response from the socket input stream. Do you know why this would be? Is Jetty 9.4 somehow more aggressive about closing sockets than 9.2 was? The key seems to be that final client-side flush to the socket output stream: without it, the client is able to read back the 408 response; with it, the client is not able to read anything from the socket.
Take care,
Daniel
[1] Exception in thread "main" java.net.SocketException: Software caused connection abort: recv failed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.Reader.read(Reader.java:140)
at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:2537)
at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:2516)
at org.apache.commons.io.IOUtils.copy(IOUtils.java:2493)
at org.apache.commons.io.IOUtils.copy(IOUtils.java:2441)
at org.apache.commons.io.IOUtils.toString(IOUtils.java:1084)
at JettyTimeoutTest.main(JettyTimeoutTest.java:53)