attemptAsync(HttpRequest request, boolean eagerlyRead
new AzureNettyHttpClientContext(responseTimeout, progressReporter)));
}
+ if (request.getHeaders().get(HttpHeaderName.CONTENT_LENGTH) == null) {
+ nettyRequest
+ = nettyRequest.contextWrite(ctx -> ctx.put(NettyUtility.DID_NOT_SET_CONTENT_LENGTH_CONTEXT_KEY, true));
+ }
+
return nettyRequest.single().flatMap(responseAndHeaders -> {
HttpResponse response = responseAndHeaders.getT1();
if (addProxyHandler && response.getStatusCode() == 407) {
diff --git a/sdk/core/azure-core-http-netty/src/main/java/com/azure/core/http/netty/NettyAsyncHttpClientBuilder.java b/sdk/core/azure-core-http-netty/src/main/java/com/azure/core/http/netty/NettyAsyncHttpClientBuilder.java
index eb7ba4b65fed..a6ee2f26063f 100644
--- a/sdk/core/azure-core-http-netty/src/main/java/com/azure/core/http/netty/NettyAsyncHttpClientBuilder.java
+++ b/sdk/core/azure-core-http-netty/src/main/java/com/azure/core/http/netty/NettyAsyncHttpClientBuilder.java
@@ -22,19 +22,20 @@
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.http.HttpHeaderNames;
-import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.logging.LoggingHandler;
import io.netty.resolver.AddressResolverGroup;
import io.netty.resolver.DefaultAddressResolverGroup;
import io.netty.resolver.NoopAddressResolverGroup;
import reactor.netty.Connection;
import reactor.netty.NettyPipeline;
+import reactor.netty.ReactorNetty;
import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpClientRequest;
import reactor.netty.http.client.HttpResponseDecoderSpec;
import reactor.netty.resources.ConnectionProvider;
import reactor.netty.transport.AddressUtils;
import reactor.netty.transport.ProxyProvider;
+import reactor.util.context.ContextView;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@@ -253,10 +254,12 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
if (msg instanceof io.netty.handler.codec.http.HttpRequest) {
io.netty.handler.codec.http.HttpRequest nettyRequest
= (io.netty.handler.codec.http.HttpRequest) msg;
- if ((nettyRequest.method() == HttpMethod.GET || nettyRequest.method() == HttpMethod.HEAD)
- && !nettyRequest.headers().contains(HttpHeaderNames.CONTENT_TYPE)
+ ContextView channelContext = ReactorNetty.getChannelContext(ctx.channel());
+ if (channelContext != null
+ && Boolean.TRUE.equals(
+ channelContext.getOrDefault(NettyUtility.DID_NOT_SET_CONTENT_LENGTH_CONTEXT_KEY, false))
&& "0".equals(nettyRequest.headers().get(HttpHeaderNames.CONTENT_LENGTH))) {
- // Remove the content-length header if it is 0 and the method is GET or HEAD.
+ // Remove the content-length header if it is 0 and the SDK did not set it.
nettyRequest.headers().remove(HttpHeaderNames.CONTENT_LENGTH);
}
}
diff --git a/sdk/core/azure-core-http-netty/src/main/java/com/azure/core/http/netty/implementation/NettyUtility.java b/sdk/core/azure-core-http-netty/src/main/java/com/azure/core/http/netty/implementation/NettyUtility.java
index 5178bc5ccfb6..5b646c5916cf 100644
--- a/sdk/core/azure-core-http-netty/src/main/java/com/azure/core/http/netty/implementation/NettyUtility.java
+++ b/sdk/core/azure-core-http-netty/src/main/java/com/azure/core/http/netty/implementation/NettyUtility.java
@@ -43,6 +43,11 @@ public final class NettyUtility {
// Netty artifact that should match the 'netty-tcnative.version' property in the pom.xml file.
private static final String NETTY_TCNATIVE_VERSION_ARTIFACT = "netty-tcnative-boringssl-static";
+ /**
+ * Key for the context to indicate that the content length was not set by the SDK.
+ */
+ public static final String DID_NOT_SET_CONTENT_LENGTH_CONTEXT_KEY = "sdk-did-not-set-content-length";
+
/**
* Deep copies the passed {@link ByteBuf} into a {@link ByteBuffer}.
*
diff --git a/sdk/core/azure-core-http-netty/src/test/java/com/azure/core/http/netty/NettyAsyncHttpClientTests.java b/sdk/core/azure-core-http-netty/src/test/java/com/azure/core/http/netty/NettyAsyncHttpClientTests.java
index f31c6ef2913c..43ff3152a381 100644
--- a/sdk/core/azure-core-http-netty/src/test/java/com/azure/core/http/netty/NettyAsyncHttpClientTests.java
+++ b/sdk/core/azure-core-http-netty/src/test/java/com/azure/core/http/netty/NettyAsyncHttpClientTests.java
@@ -79,6 +79,7 @@
import static com.azure.core.http.netty.implementation.NettyHttpClientLocalTestServer.ERROR_BODY_PATH;
import static com.azure.core.http.netty.implementation.NettyHttpClientLocalTestServer.EXPECTED_HEADER;
import static com.azure.core.http.netty.implementation.NettyHttpClientLocalTestServer.HTTP_HEADERS_PATH;
+import static com.azure.core.http.netty.implementation.NettyHttpClientLocalTestServer.INVALID_CONTENT_LENGTH_ZERO;
import static com.azure.core.http.netty.implementation.NettyHttpClientLocalTestServer.IO_EXCEPTION_PATH;
import static com.azure.core.http.netty.implementation.NettyHttpClientLocalTestServer.LONG_BODY;
import static com.azure.core.http.netty.implementation.NettyHttpClientLocalTestServer.LONG_BODY_PATH;
@@ -92,6 +93,7 @@
import static com.azure.core.http.netty.implementation.NettyHttpClientLocalTestServer.SHORT_POST_BODY_WITH_VALIDATION_PATH;
import static com.azure.core.http.netty.implementation.NettyHttpClientLocalTestServer.TEST_HEADER;
import static com.azure.core.http.netty.implementation.NettyHttpClientLocalTestServer.TIMEOUT;
+import static com.azure.core.http.netty.implementation.NettyHttpClientLocalTestServer.VALID_CONTENT_LENGTH_ZERO;
import static com.azure.core.validation.http.HttpValidatonUtils.assertArraysEqual;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
@@ -635,6 +637,48 @@ public void perCallTimeoutSync() {
}
}
+ @Test
+ public void sdkCanSendContentLengthZeroAsync() {
+ HttpClient client = new NettyAsyncHttpClientProvider().createInstance();
+ HttpRequest request = new HttpRequest(HttpMethod.GET, url(VALID_CONTENT_LENGTH_ZERO))
+ .setHeader(HttpHeaderName.CONTENT_LENGTH, "0");
+
+ StepVerifier.create(client.send(request))
+ .assertNext(response -> assertEquals(200, response.getStatusCode()))
+ .verifyComplete();
+ }
+
+ @Test
+ public void sdkCanSendContentLengthZeroSync() {
+ HttpClient client = new NettyAsyncHttpClientProvider().createInstance();
+ HttpRequest request = new HttpRequest(HttpMethod.GET, url(VALID_CONTENT_LENGTH_ZERO))
+ .setHeader(HttpHeaderName.CONTENT_LENGTH, "0");
+
+ try (HttpResponse response = client.sendSync(request, Context.NONE)) {
+ assertEquals(200, response.getStatusCode());
+ }
+ }
+
+ @Test
+ public void reactorNettySettingContentLengthZeroIsInvalidAsync() {
+ HttpClient client = new NettyAsyncHttpClientProvider().createInstance();
+ HttpRequest request = new HttpRequest(HttpMethod.GET, url(INVALID_CONTENT_LENGTH_ZERO));
+
+ StepVerifier.create(client.send(request))
+ .assertNext(response -> assertEquals(200, response.getStatusCode()))
+ .verifyComplete();
+ }
+
+ @Test
+ public void reactorNettySettingContentLengthZeroIsInvalidSync() {
+ HttpClient client = new NettyAsyncHttpClientProvider().createInstance();
+ HttpRequest request = new HttpRequest(HttpMethod.GET, url(INVALID_CONTENT_LENGTH_ZERO));
+
+ try (HttpResponse response = client.sendSync(request, Context.NONE)) {
+ assertEquals(200, response.getStatusCode());
+ }
+ }
+
private static Stream requestHeaderSupplier() {
return Stream.of(Arguments.of(null, NULL_REPLACEMENT), Arguments.of("", ""), Arguments.of("aValue", "aValue"));
}
diff --git a/sdk/core/azure-core-http-netty/src/test/java/com/azure/core/http/netty/implementation/NettyHttpClientLocalTestServer.java b/sdk/core/azure-core-http-netty/src/test/java/com/azure/core/http/netty/implementation/NettyHttpClientLocalTestServer.java
index b72dca716552..7a40001e4abb 100644
--- a/sdk/core/azure-core-http-netty/src/test/java/com/azure/core/http/netty/implementation/NettyHttpClientLocalTestServer.java
+++ b/sdk/core/azure-core-http-netty/src/test/java/com/azure/core/http/netty/implementation/NettyHttpClientLocalTestServer.java
@@ -37,6 +37,8 @@ public final class NettyHttpClientLocalTestServer {
public static final String RETURN_HEADERS_AS_IS_PATH = "/returnHeadersAsIs";
public static final String PROXY_TO_ADDRESS = "/proxyToAddress";
public static final String TIMEOUT = "/timeout";
+ public static final String VALID_CONTENT_LENGTH_ZERO = "/validContentLengthZero";
+ public static final String INVALID_CONTENT_LENGTH_ZERO = "/invalidContentLengthZero";
public static final byte[] SHORT_BODY = "hi there".getBytes(StandardCharsets.UTF_8);
public static final byte[] LONG_BODY = createLongBody();
@@ -145,6 +147,20 @@ private static LocalTestServer initializeServer() {
} catch (InterruptedException e) {
throw new ServletException(e);
}
+ } else if (VALID_CONTENT_LENGTH_ZERO.equals(path)) {
+ String contentLength = req.getHeader("Content-Length");
+ if ("0".equals(contentLength)) {
+ resp.setStatus(200);
+ } else {
+ resp.setStatus(400);
+ }
+ } else if (INVALID_CONTENT_LENGTH_ZERO.equals(path)) {
+ String contentLength = req.getHeader("Content-Length");
+ if ("0".equals(contentLength)) {
+ resp.setStatus(400);
+ } else {
+ resp.setStatus(200);
+ }
} else {
throw new ServletException("Unexpected request: " + req.getMethod() + " " + path);
}
From fdd223052c652bcb9556cb8b1628ceaaffc9e2aa Mon Sep 17 00:00:00 2001
From: alzimmermsft <48699787+alzimmermsft@users.noreply.github.com>
Date: Mon, 21 Apr 2025 11:07:45 -0400
Subject: [PATCH 10/13] Update to latest GA
---
common/perf-test-core/pom.xml | 2 +-
eng/versioning/external_dependencies.txt | 6 +++---
sdk/communication/azure-communication-common/pom.xml | 2 +-
sdk/core/azure-core-amqp-experimental/pom.xml | 2 +-
sdk/core/azure-core-experimental/pom.xml | 2 +-
sdk/core/azure-core-http-jdk-httpclient/pom.xml | 2 +-
sdk/core/azure-core-http-netty/pom.xml | 6 +++---
sdk/core/azure-core-http-okhttp/pom.xml | 2 +-
sdk/core/azure-core-http-vertx/pom.xml | 2 +-
sdk/core/azure-core-management/pom.xml | 2 +-
sdk/core/azure-core-perf/pom.xml | 2 +-
sdk/core/azure-core-serializer-avro-apache/pom.xml | 2 +-
sdk/core/azure-core-serializer-avro-jackson/pom.xml | 2 +-
sdk/core/azure-core-serializer-json-gson/pom.xml | 2 +-
sdk/core/azure-core-serializer-json-jackson/pom.xml | 2 +-
sdk/core/azure-core-test/pom.xml | 4 ++--
sdk/core/azure-core-tracing-opentelemetry/pom.xml | 2 +-
sdk/core/azure-core-version-tests/pom.xml | 4 ++--
sdk/core/azure-core/pom.xml | 6 +++---
sdk/cosmos/azure-cosmos-encryption/pom.xml | 2 +-
.../azure-cosmos-spark-account-data-resolver-sample/pom.xml | 2 +-
sdk/cosmos/azure-cosmos-spark_3_2-12/pom.xml | 2 +-
sdk/cosmos/azure-cosmos-tests/pom.xml | 2 +-
sdk/e2e/pom.xml | 2 +-
.../azure-messaging-eventhubs-checkpointstore-jedis/pom.xml | 2 +-
sdk/eventhubs/azure-messaging-eventhubs/docs/pom.xml | 2 +-
.../azure-resourcemanager-resources/pom.xml | 2 +-
27 files changed, 35 insertions(+), 35 deletions(-)
diff --git a/common/perf-test-core/pom.xml b/common/perf-test-core/pom.xml
index 8d883747699b..f6ceea2ecdc3 100644
--- a/common/perf-test-core/pom.xml
+++ b/common/perf-test-core/pom.xml
@@ -56,7 +56,7 @@
com.beust:jcommander:[1.82]
- io.projectreactor:reactor-core:[3.7.4]
+ io.projectreactor:reactor-core:[3.7.5]
io.vertx:vertx-codegen:[4.5.13]