From c83715e39867064da855fd9a146824e168cfbd6e Mon Sep 17 00:00:00 2001 From: Baekgyu Kim Date: Sun, 3 May 2026 22:58:50 +0900 Subject: [PATCH] Detect Apache5HttpClient in AWS SDK transport body-method guardrail (#1970) * Detect Apache5HttpClient in AWS SDK transport body-method guardrail Signed-off-by: Baekgyu * ADD CHANGELOG Signed-off-by: Baekgyu --------- Signed-off-by: Baekgyu (cherry picked from commit ca838c784c6cc4faa37ac97348ecfccf3821cb92) Signed-off-by: Andriy Redko --- CHANGELOG.md | 1 + java-client/build.gradle.kts | 1 + .../transport/aws/AwsSdk2Transport.java | 24 ++++++++- .../AwsSdk2TransportApacheDetectionTests.java | 54 +++++++++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 java-client/src/test/java/org/opensearch/client/transport/aws/AwsSdk2TransportApacheDetectionTests.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 00a18d7a0a..b859bbc48a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased 3.x] ### Added +- Detect AWS SDK `Apache5HttpClient` in `AwsSdk2Transport` body-method guardrail ([#1903](https://github.com/opensearch-project/opensearch-java/pull/1970)) ### Dependencies diff --git a/java-client/build.gradle.kts b/java-client/build.gradle.kts index caf16b865d..0a4e3bd2d2 100644 --- a/java-client/build.gradle.kts +++ b/java-client/build.gradle.kts @@ -228,6 +228,7 @@ dependencies { testImplementation("software.amazon.awssdk", "http-auth-aws", "[2.21,3.0)") testImplementation("software.amazon.awssdk", "aws-crt-client", "[2.21,3.0)") testImplementation("software.amazon.awssdk", "apache-client", "[2.21,3.0)") + testImplementation("software.amazon.awssdk", "apache5-client", "[2.34,3.0)") testImplementation("software.amazon.awssdk", "netty-nio-client", "[2.21,3.0)") testImplementation("software.amazon.awssdk", "url-connection-client", "[2.21,3.0)") testImplementation("software.amazon.awssdk", "sts", "[2.21,3.0)") diff --git a/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java b/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java index 5a56042fdc..e7476ec846 100644 --- a/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java +++ b/java-client/src/main/java/org/opensearch/client/transport/aws/AwsSdk2Transport.java @@ -22,10 +22,14 @@ import java.net.URLEncoder; import java.time.Clock; import java.util.AbstractMap; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Function; @@ -85,6 +89,23 @@ public class AwsSdk2Transport implements OpenSearchTransport { public static final Integer DEFAULT_REQUEST_COMPRESSION_SIZE = 8192; private static final byte[] NO_BYTES = new byte[0]; + + /** + * FQCNs of Apache-backed AWS SDK HTTP clients (v4, v5) that share the body-on-GET/DELETE limitation. + */ + private static final Set APACHE_HTTP_CLIENT_CLASS_NAMES = Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList("software.amazon.awssdk.http.apache.ApacheHttpClient", "software.amazon.awssdk.http.apache5.Apache5HttpClient") + ) + ); + + /** + * Package-private to allow assertions in unit tests. + */ + static boolean isAwsSdkApacheHttpClient(SdkAutoCloseable httpClient) { + return httpClient instanceof SdkHttpClient && APACHE_HTTP_CLIENT_CLASS_NAMES.contains(httpClient.getClass().getName()); + } + private final SdkAutoCloseable httpClient; private final boolean isApacheHttpClient; private final String host; @@ -196,8 +217,7 @@ private AwsSdk2Transport( ) { Objects.requireNonNull(host, "Target OpenSearch service host must not be null"); this.httpClient = httpClient; - this.isApacheHttpClient = httpClient instanceof SdkHttpClient - && httpClient.getClass().getName().equals("software.amazon.awssdk.http.apache.ApacheHttpClient"); + this.isApacheHttpClient = isAwsSdkApacheHttpClient(httpClient); this.host = host; this.signingServiceName = signingServiceName; this.signingRegion = signingRegion; diff --git a/java-client/src/test/java/org/opensearch/client/transport/aws/AwsSdk2TransportApacheDetectionTests.java b/java-client/src/test/java/org/opensearch/client/transport/aws/AwsSdk2TransportApacheDetectionTests.java new file mode 100644 index 0000000000..b3d38e583b --- /dev/null +++ b/java-client/src/test/java/org/opensearch/client/transport/aws/AwsSdk2TransportApacheDetectionTests.java @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client.transport.aws; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.http.apache5.Apache5HttpClient; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.utils.SdkAutoCloseable; + +public class AwsSdk2TransportApacheDetectionTests { + + @Test + public void nullReturnsFalse() { + assertFalse(AwsSdk2Transport.isAwsSdkApacheHttpClient(null)); + } + + @Test + public void urlConnectionReturnsFalse() { + try (SdkHttpClient client = UrlConnectionHttpClient.builder().build()) { + assertFalse(AwsSdk2Transport.isAwsSdkApacheHttpClient(client)); + } + } + + @Test + public void apacheV4ReturnsTrue() { + try (SdkHttpClient client = ApacheHttpClient.builder().build()) { + assertTrue(AwsSdk2Transport.isAwsSdkApacheHttpClient(client)); + } + } + + @Test + public void apacheV5ReturnsTrue() { + try (SdkHttpClient client = Apache5HttpClient.builder().build()) { + assertTrue(AwsSdk2Transport.isAwsSdkApacheHttpClient(client)); + } + } + + @Test + public void nonHttpClientWithMatchingNameReturnsFalse() { + SdkAutoCloseable notAnHttpClient = () -> {}; + assertFalse(AwsSdk2Transport.isAwsSdkApacheHttpClient(notAnHttpClient)); + } +}