diff --git a/reactor-netty5-http/src/main/java/reactor/netty5/http/client/HttpClientOperations.java b/reactor-netty5-http/src/main/java/reactor/netty5/http/client/HttpClientOperations.java index d13eb9f79..c14e84649 100644 --- a/reactor-netty5-http/src/main/java/reactor/netty5/http/client/HttpClientOperations.java +++ b/reactor-netty5-http/src/main/java/reactor/netty5/http/client/HttpClientOperations.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2011-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -622,7 +622,7 @@ protected void onOutboundComplete() { log.debug(format(channel(), "No sendHeaders() called before complete, sending " + "zero-length header")); } - channel().writeAndFlush(newFullBodyMessage(channel().bufferAllocator().allocate(0))); + channel().writeAndFlush(newFullBodyMessage()); } else if (markSentBody()) { channel().writeAndFlush(new EmptyLastHttpContent(channel().bufferAllocator())); @@ -845,6 +845,15 @@ protected HttpMessage newFullBodyMessage(Buffer body) { return request; } + HttpMessage newFullBodyMessage() { + HttpRequest request = new DefaultFullHttpRequest(version(), method(), uri(), channel().bufferAllocator().allocate(0)); + + requestHeaders.remove(HttpHeaderNames.TRANSFER_ENCODING); + + request.headers().set(requestHeaders); + return request; + } + @Override protected Throwable wrapInboundError(Throwable err) { if (err instanceof ClosedChannelException) { @@ -862,7 +871,7 @@ final Mono send() { return Mono.error(AbortedException.beforeSend()); } return FutureMono.deferFuture(() -> markSentHeaderAndBody() ? - channel().writeAndFlush(newFullBodyMessage(channel().bufferAllocator().allocate(0))) : + channel().writeAndFlush(newFullBodyMessage()) : channel().newSucceededFuture()); } diff --git a/reactor-netty5-http/src/test/java/reactor/netty5/http/HttpMetricsHandlerTests.java b/reactor-netty5-http/src/test/java/reactor/netty5/http/HttpMetricsHandlerTests.java index cef6d1d9c..c36f4e42a 100644 --- a/reactor-netty5-http/src/test/java/reactor/netty5/http/HttpMetricsHandlerTests.java +++ b/reactor-netty5-http/src/test/java/reactor/netty5/http/HttpMetricsHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2019-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -409,13 +409,13 @@ void testNonExistingEndpoint(HttpProtocol[] serverProtocols, HttpProtocol[] clie List protocols = Arrays.asList(clientProtocols); int[] numWrites = new int[]{5, 7}; int[] numReads = new int[]{1, 2}; - int[] bytesWrite = new int[]{106, 122}; + int[] bytesWrite = new int[]{103, 118}; int[] bytesRead = new int[]{37, 48}; int connIndex = 1; if ((serverProtocols.length == 1 && serverProtocols[0] == HttpProtocol.HTTP11) || (clientProtocols.length == 1 && clientProtocols[0] == HttpProtocol.HTTP11)) { numWrites = new int[]{1, 2}; - bytesWrite = new int[]{123, 246}; + bytesWrite = new int[]{104, 208}; bytesRead = new int[]{64, 128}; connIndex = 2; } @@ -423,7 +423,7 @@ else if (clientProtocols.length == 2 && Arrays.equals(clientProtocols, new HttpProtocol[]{HttpProtocol.H2C, HttpProtocol.HTTP11})) { numWrites = new int[]{4, 6}; numReads = new int[]{2, 3}; - bytesWrite = new int[]{287, 345}; + bytesWrite = new int[]{268, 323}; bytesRead = new int[]{108, 119}; } else if (protocols.contains(HttpProtocol.H2) || protocols.contains(HttpProtocol.H2C)) { @@ -1365,7 +1365,7 @@ private void checkExpectationsBadRequest(String serverAddress, boolean checkTls) assertCounter(registry, CLIENT_ERRORS, summaryTags1).isNull(); assertDistributionSummary(registry, CLIENT_DATA_SENT, summaryTags2) .hasCountGreaterThanOrEqualTo(1) - .hasTotalAmountGreaterThanOrEqualTo(118); + .hasTotalAmountGreaterThanOrEqualTo(99); assertCounter(registry, CLIENT_ERRORS, summaryTags2).isNull(); } diff --git a/reactor-netty5-http/src/test/java/reactor/netty5/http/HttpProtocolsTests.java b/reactor-netty5-http/src/test/java/reactor/netty5/http/HttpProtocolsTests.java index a175a44c7..87e281b21 100644 --- a/reactor-netty5-http/src/test/java/reactor/netty5/http/HttpProtocolsTests.java +++ b/reactor-netty5-http/src/test/java/reactor/netty5/http/HttpProtocolsTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2020-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ import io.netty5.handler.codec.http.HttpRequest; import io.netty5.handler.codec.http.HttpServerCodec; import io.netty5.handler.codec.http.LastHttpContent; +import io.netty5.handler.codec.http.headers.HttpHeaders; import io.netty5.handler.codec.http2.Http2Connection; import io.netty5.handler.codec.http2.Http2DataFrame; import io.netty5.handler.codec.http2.Http2FrameCodec; @@ -1051,13 +1052,13 @@ void testIssue3524Mono(HttpServer server, HttpClient client) { @ParameterizedCompatibleCombinationsTest void testIssue3524MonoEmpty(HttpServer server, HttpClient client) { // sends "full" request - testRequestBody(server, client, sender -> sender.send((req, out) -> Mono.empty()), 1); + testRequestBody(server, client, sender -> sender.send((req, out) -> Mono.empty()), 1, true); } @ParameterizedCompatibleCombinationsTest void testIssue3524NoBody(HttpServer server, HttpClient client) { // sends "full" request - testRequestBody(server, client, sender -> sender.send((req, out) -> out), 1); + testRequestBody(server, client, sender -> sender.send((req, out) -> out), 1, true); } @ParameterizedCompatibleCombinationsTest @@ -1069,14 +1070,22 @@ void testIssue3524Object(HttpServer server, HttpClient client) { private void testRequestBody(HttpServer server, HttpClient client, Function> sendFunction, int expectedMsg) { + testRequestBody(server, client, sendFunction, expectedMsg, false); + } + + @SuppressWarnings("FutureReturnValueIgnored") + private void testRequestBody(HttpServer server, HttpClient client, + Function> sendFunction, int expectedMsg, boolean contentHeadersDoNotExist) { disposableServer = server.handle((req, res) -> req.receive() .then(res.send())) .bindNow(Duration.ofSeconds(30)); AtomicInteger counter = new AtomicInteger(); + AtomicReference requestHeaders = new AtomicReference<>(); sendFunction.apply( client.port(disposableServer.port()) + .doAfterRequest((req, conn) -> requestHeaders.set(req.requestHeaders())) .doOnRequest((req, conn) -> { ChannelPipeline pipeline = conn.channel() instanceof Http2StreamChannel ? conn.channel().parent().pipeline() : conn.channel().pipeline(); @@ -1123,6 +1132,10 @@ else if (msg instanceof Buffer) { .block(Duration.ofSeconds(30)); assertThat(counter.get()).isEqualTo(expectedMsg); + if (contentHeadersDoNotExist) { + assertThat(requestHeaders.get().get(HttpHeaderNames.CONTENT_LENGTH)).isNull(); + assertThat(requestHeaders.get().get(HttpHeaderNames.TRANSFER_ENCODING)).isNull(); + } } static final class IdleTimeoutTestChannelInboundHandler extends ChannelHandlerAdapter { diff --git a/reactor-netty5-http/src/test/java/reactor/netty5/http/client/HttpClientTest.java b/reactor-netty5-http/src/test/java/reactor/netty5/http/client/HttpClientTest.java index b7d12e694..2143351a5 100644 --- a/reactor-netty5-http/src/test/java/reactor/netty5/http/client/HttpClientTest.java +++ b/reactor-netty5-http/src/test/java/reactor/netty5/http/client/HttpClientTest.java @@ -3639,7 +3639,8 @@ void testDeleteMethod(String requestBodyType) { createServer() .handle((req, res) -> res.sendString( Flux.concat(req.receive().aggregate().asString().defaultIfEmpty("empty"), - Mono.just(" " + req.requestHeaders().get(HttpHeaderNames.CONTENT_LENGTH))))) + Mono.just(" " + req.requestHeaders().get(HttpHeaderNames.CONTENT_LENGTH) + + " " + req.requestHeaders().get(HttpHeaderNames.TRANSFER_ENCODING))))) .bindNow(); Publisher body = "flux".equals(requestBodyType) ? @@ -3652,7 +3653,7 @@ void testDeleteMethod(String requestBodyType) { .send(body) .responseSingle((res, bytes) -> bytes.asString()) .as(StepVerifier::create) - .expectNext("empty".equals(requestBodyType) ? "empty 0" : "delete 6") + .expectNext("empty".equals(requestBodyType) ? "empty null null" : "delete 6 null") .expectComplete() .verify(Duration.ofSeconds(5)); } diff --git a/reactor-netty5-http/src/test/java/reactor/netty5/http/client/HttpClientWithTomcatTest.java b/reactor-netty5-http/src/test/java/reactor/netty5/http/client/HttpClientWithTomcatTest.java index faac70a3f..42342c882 100644 --- a/reactor-netty5-http/src/test/java/reactor/netty5/http/client/HttpClientWithTomcatTest.java +++ b/reactor-netty5-http/src/test/java/reactor/netty5/http/client/HttpClientWithTomcatTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2024 VMware, Inc. or its affiliates, All Rights Reserved. + * Copyright (c) 2019-2025 VMware, Inc. or its affiliates, All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -233,7 +233,7 @@ void disableChunkForced2() { assertThat(r).isNotNull(); assertThat(r.getT1()).isEqualTo(HttpResponseStatus.NOT_FOUND); - assertThat(headers.get().get("Content-Length")).isEqualTo("0"); + assertThat(headers.get().get("Content-Length")).isNull(); assertThat(headers.get().get("Transfer-Encoding")).isNull(); }