diff --git a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java index 92f1e63be..5f2b1357d 100644 --- a/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java +++ b/reactor-netty-http/src/main/java/reactor/netty/http/server/HttpServerOperations.java @@ -826,6 +826,12 @@ else if (msg instanceof HttpRequest) { channel().config().setAutoRead(true); onInboundComplete(); } + else if (!isHttp2() && request.headers().contains(HttpHeaderNames.UPGRADE)) { + // HTTP/1.1 TLS Upgrade (RFC-2817) non-empty requests (GET/HEAD/OPTIONS) + // No need to call onInboundComplete(), it will be triggered by onInboundNext(...) + // since this is the last HTTP content + stopReadTimeout(); + } } } else if (msg instanceof LastHttpContent) { diff --git a/reactor-netty-http/src/test/java/reactor/netty/http/client/HttpClientTest.java b/reactor-netty-http/src/test/java/reactor/netty/http/client/HttpClientTest.java index 1300ac9b6..e5ca0883d 100644 --- a/reactor-netty-http/src/test/java/reactor/netty/http/client/HttpClientTest.java +++ b/reactor-netty-http/src/test/java/reactor/netty/http/client/HttpClientTest.java @@ -72,6 +72,7 @@ import io.netty.channel.unix.DomainSocketAddress; import io.netty.handler.codec.compression.Brotli; import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.DefaultHttpContent; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpContentDecompressor; @@ -1732,6 +1733,39 @@ void testIssue3538GetWithPayload() throws Exception { .verify(Duration.ofSeconds(30)); } + @Test + void testIssue3538GetWithPayloadAndH2cMaxContentLength() throws Exception { + disposableServer = + createServer() + .protocol(HttpProtocol.H2C, HttpProtocol.HTTP11) + .httpRequestDecoder(spec -> spec.h2cMaxContentLength(100)) + .route(r -> r.get("/", (req, res) -> { + final EchoAction action = new EchoAction(); + + req + .receiveContent().switchIfEmpty(Mono.just(LastHttpContent.EMPTY_LAST_CONTENT)) + .subscribe(action); + + return res.sendObject(action); + } + )) + .bindNow(); + assertThat(disposableServer).isNotNull(); + + final ByteBuf content = createHttpClientForContextWithPort() + .protocol(HttpProtocol.HTTP11) + .headers(h -> + h.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) + .add(HttpHeaderNames.UPGRADE, "TLS/1.2")) + .request(HttpMethod.GET) + .send((req, res) -> res.sendString(Mono.just("testIssue3538"))) + .uri("/") + .responseContent() + .blockLast(Duration.ofSeconds(30)); + + assertThat(content).isNotNull(); + } + @Test void testIssue694() { disposableServer = @@ -3594,15 +3628,13 @@ private static class EchoAction implements Publisher, Consumer 0) { + emitter.next(new DefaultHttpContent(message.content().retain())); + } + if (message instanceof LastHttpContent) { - if (message.content().readableBytes() > 0) { - emitter.next(message.retain()); - } emitter.complete(); } - else { - emitter.next(message.retain()); - } } @Override