Problem
A netty server never answers a request that carries Expect: 100-continue. The client gets the interim 100 Continue response, then the connection is closed. It never gets the real response, and no timeout fires either.
How to reproduce
Start any netty server (this uses NettyFutureServer, but the code path is shared by all netty backends) with a PUT endpoint that echoes a string body, then send a raw request:
PUT / HTTP/1.1
Host: localhost:<port>
Content-Type: text/plain
Content-Length: 4
Expect: 100-continue
wait a moment, then send test.
Response received:
HTTP/1.1 100 Continue
connection: close
and then EOF. Expected: 100 Continue, then 200 OK with the echoed body.
Why
NettyServerHandler.channelRead0 has this branch:
} else if (HttpUtil.is100ContinueExpected(request)) {
ctx.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE))
()
}
The route is never run, and the per-request IdleStateHandler (which produces the request timeout) is never installed. On top of that, the write goes through HttpStreamsServerHandler.unbufferedWrite while inFlight == 1 && continueExpected, which sets close = true, so the connection is closed once the 100 Continue is out.
The branch was added in #3337.
Suggested fix
Drop the branch and let the request fall through to runRoute. HttpStreamsServerHandler already handles Expect: 100-continue on its own: it sends the 100 Continue when the body publisher first asks for data, and closes the connection if a response is produced without the body ever being read. That would also give these requests a request timeout, like every other request.
Found while reviewing #5466, which documents the request timeout as starting when the request headers are received — which is not true for these requests.
Problem
A netty server never answers a request that carries
Expect: 100-continue. The client gets the interim100 Continueresponse, then the connection is closed. It never gets the real response, and no timeout fires either.How to reproduce
Start any netty server (this uses
NettyFutureServer, but the code path is shared by all netty backends) with aPUTendpoint that echoes a string body, then send a raw request:wait a moment, then send
test.Response received:
and then EOF. Expected:
100 Continue, then200 OKwith the echoed body.Why
NettyServerHandler.channelRead0has this branch:The route is never run, and the per-request
IdleStateHandler(which produces the request timeout) is never installed. On top of that, the write goes throughHttpStreamsServerHandler.unbufferedWritewhileinFlight == 1 && continueExpected, which setsclose = true, so the connection is closed once the100 Continueis out.The branch was added in #3337.
Suggested fix
Drop the branch and let the request fall through to
runRoute.HttpStreamsServerHandleralready handlesExpect: 100-continueon its own: it sends the100 Continuewhen the body publisher first asks for data, and closes the connection if a response is produced without the body ever being read. That would also give these requests a request timeout, like every other request.Found while reviewing #5466, which documents the request timeout as starting when the request headers are received — which is not true for these requests.