diff --git a/.changes/next-release/bugfix-AWSCRTbasedS3Client-7a3c1e2.json b/.changes/next-release/bugfix-AWSCRTbasedS3Client-7a3c1e2.json new file mode 100644 index 000000000000..78233be09295 --- /dev/null +++ b/.changes/next-release/bugfix-AWSCRTbasedS3Client-7a3c1e2.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "AWS CRT-based S3 Client", + "contributor": "MingWangSong", + "description": "Fixed an issue where the CRT-based S3 client always established proxy connections through a `CONNECT` tunnel, even when `endpointOverride` used the plaintext `http://` scheme. Proxies that only support forwarding mode rejected these requests. The proxy connection type is now pinned to forwarding for plaintext endpoints, matching the behavior of `ApacheHttpClient`. See [#7320](https://github.com/aws/aws-sdk-java-v2/issues/7320)." +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crt/S3NativeClientConfiguration.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crt/S3NativeClientConfiguration.java index b3c0742a272c..47b428699dcc 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crt/S3NativeClientConfiguration.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crt/S3NativeClientConfiguration.java @@ -47,6 +47,7 @@ public class S3NativeClientConfiguration implements SdkAutoCloseable { static final long DEFAULT_PART_SIZE_IN_BYTES = 8L * 1024 * 1024; private static final Logger log = Logger.loggerFor(S3NativeClientConfiguration.class); private static final long DEFAULT_TARGET_THROUGHPUT_IN_GBPS = 10; + private static final String HTTP_SCHEME = "http"; private final String signingRegion; private final StandardRetryOptions standardRetryOptions; @@ -106,6 +107,7 @@ public S3NativeClientConfiguration(Builder builder) { if (builder.httpConfiguration != null) { this.proxyOptions = resolveProxy(builder.httpConfiguration.proxyConfiguration(), tlsContext).orElse(null); + applyPlaintextEndpointProxyConnectionType(this.proxyOptions, this.endpointOverride); this.connectionTimeout = builder.httpConfiguration.connectionTimeout(); this.httpMonitoringOptions = resolveHttpMonitoringOptions(builder.httpConfiguration.healthConfiguration()).orElse(null); @@ -135,6 +137,24 @@ private long resolveThresholdInBytes(Builder builder) { return this.partSizeInBytes == null ? DEFAULT_PART_SIZE_IN_BYTES : this.partSizeInBytes; } + /** + * Pins the proxy connection type to forwarding when the endpoint is plaintext. + * + *
The CRT derives the proxy connection type from whether TLS options were supplied for the main connection,
+ * not from the scheme actually in use. Because this client is always constructed with a {@link TlsContext}, a
+ * plaintext {@code http://} endpoint would otherwise be reached through a {@code CONNECT} tunnel, which
+ * forwarding-only proxies reject. This mirrors the scheme check that the CRT's own
+ * {@code HttpClientConnectionManager} performs before handing a TLS context to the native layer.
+ */
+ private static void applyPlaintextEndpointProxyConnectionType(HttpProxyOptions proxyOptions, URI endpointOverride) {
+ if (proxyOptions == null || endpointOverride == null) {
+ return;
+ }
+ if (HTTP_SCHEME.equalsIgnoreCase(endpointOverride.getScheme())) {
+ proxyOptions.setConnectionType(HttpProxyOptions.HttpProxyConnectionType.Forwarding);
+ }
+ }
+
private static Boolean resolveUseEnvironmentVariableValues(Builder builder) {
if (builder != null && builder.httpConfiguration != null && builder.httpConfiguration.proxyConfiguration() != null) {
return builder.httpConfiguration.proxyConfiguration().isUseEnvironmentVariableValues();
diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/S3NativeClientConfigurationTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/S3NativeClientConfigurationTest.java
index 0fdb91c0e6a5..5662d6b8237c 100644
--- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/S3NativeClientConfigurationTest.java
+++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/S3NativeClientConfigurationTest.java
@@ -17,6 +17,7 @@
import static org.assertj.core.api.Assertions.assertThat;
+import java.net.URI;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@@ -24,6 +25,8 @@
import org.junit.jupiter.params.provider.MethodSource;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.crt.http.HttpProxyOptions;
+import software.amazon.awssdk.services.s3.crt.S3CrtProxyConfiguration;
import software.amazon.awssdk.services.s3.crt.S3CrtHttpConfiguration;
import software.amazon.awssdk.testutils.LogCaptor;
@@ -61,7 +64,49 @@ private static Stream