diff --git a/.changes/next-release/bugfix-AWSCRTbasedS3Client-8185886.json b/.changes/next-release/bugfix-AWSCRTbasedS3Client-8185886.json new file mode 100644 index 000000000000..7e9442b4908b --- /dev/null +++ b/.changes/next-release/bugfix-AWSCRTbasedS3Client-8185886.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "AWS CRT-based S3 Client", + "contributor": "", + "description": "Fixed \"Connection pool shut down\" error thrown when a default AWS CRT-based S3 client is created and closed per request. See [#5881](https://github.com/aws/aws-sdk-java-v2/issues/5881)" +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crt/DefaultS3CrtAsyncClient.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crt/DefaultS3CrtAsyncClient.java index 2e0216760244..b749c262d180 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crt/DefaultS3CrtAsyncClient.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crt/DefaultS3CrtAsyncClient.java @@ -35,6 +35,8 @@ import java.util.concurrent.Executor; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.annotations.SdkTestInternalApi; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; import software.amazon.awssdk.awscore.AwsRequest; import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration; @@ -126,6 +128,10 @@ private static S3AsyncClient initializeS3AsyncClient(DefaultS3CrtClientBuilder b builder.executionInterceptors.forEach(overrideConfigurationBuilder::addExecutionInterceptor); } + if (builder.credentialsProvider == null) { + builder = builder.credentialsProvider(DefaultCredentialsProvider.builder().build()); + } + DefaultS3CrtClientBuilder finalBuilder = resolveChecksumConfiguration(builder); S3AsyncClientBuilder s3AsyncClientBuilder = @@ -228,6 +234,13 @@ public static final class DefaultS3CrtClientBuilder implements S3CrtAsyncClientB private Executor futureCompletionExecutor; private Boolean disableS3ExpressSessionAuth; + + @Override + public DefaultS3CrtClientBuilder credentialsProvider(AwsCredentialsProvider credentialsProvider) { + this.credentialsProvider = credentialsProvider; + return this; + } + @Override public DefaultS3CrtClientBuilder credentialsProvider( IdentityProvider credentialsProvider) { 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 4925a708c818..f43cbf84d46e 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 @@ -21,7 +21,6 @@ import java.net.URI; import java.time.Duration; import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.crt.auth.credentials.CredentialsProvider; import software.amazon.awssdk.crt.http.HttpMonitoringOptions; import software.amazon.awssdk.crt.http.HttpProxyOptions; @@ -36,6 +35,7 @@ import software.amazon.awssdk.services.s3.crt.S3CrtHttpConfiguration; import software.amazon.awssdk.utils.Logger; import software.amazon.awssdk.utils.SdkAutoCloseable; +import software.amazon.awssdk.utils.Validate; /** * Internal client configuration resolver @@ -80,10 +80,8 @@ public S3NativeClientConfiguration(Builder builder) { clientTlsContextOptions.withVerifyPeer(!builder.httpConfiguration.trustAllCertificatesEnabled()); } this.tlsContext = new TlsContext(clientTlsContextOptions); - this.credentialProviderAdapter = - builder.credentialsProvider == null ? - new CrtCredentialsProviderAdapter(DefaultCredentialsProvider.create()) : - new CrtCredentialsProviderAdapter(builder.credentialsProvider); + this.credentialProviderAdapter = new CrtCredentialsProviderAdapter( + Validate.paramNotNull(builder.credentialsProvider, "credentialsProvider")); this.credentialsProvider = credentialProviderAdapter.crtCredentials(); diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/DefaultS3CrtAsyncClientTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/DefaultS3CrtAsyncClientTest.java index 424c823f2d84..56f93c7122bb 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/DefaultS3CrtAsyncClientTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/DefaultS3CrtAsyncClientTest.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.auth.signer.AwsS3V4Signer; import software.amazon.awssdk.core.async.AsyncRequestBody; import software.amazon.awssdk.core.async.AsyncResponseTransformer; @@ -29,6 +30,8 @@ import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; +import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; +import software.amazon.awssdk.identity.spi.IdentityProvider; import software.amazon.awssdk.services.s3.DelegatingS3AsyncClient; import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.endpoints.S3ClientContextParams; @@ -116,4 +119,25 @@ void crtClient_with_crossRegionAccessEnabled_asFalse() { } } + @Test + void defaultClient_credentialsProvidersNotSingleton() { + try (S3AsyncClient client = S3AsyncClient.crtBuilder().build(); + S3AsyncClient anotherClient = S3AsyncClient.crtBuilder().build()) { + + IdentityProvider identityProvider = + client.serviceClientConfiguration().credentialsProvider(); + + IdentityProvider identityProviderFromAnotherClient = + anotherClient.serviceClientConfiguration().credentialsProvider(); + + assertThat(identityProvider) + .isNotEqualTo(identityProviderFromAnotherClient); + assertThat(identityProvider) + .isInstanceOf(DefaultCredentialsProvider.class); + assertThat(identityProvider) + .isNotEqualTo(DefaultCredentialsProvider.create()); + assertThat(identityProviderFromAnotherClient) + .isNotEqualTo(DefaultCredentialsProvider.create()); + } + } } diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/S3CrtAsyncHttpClientTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/S3CrtAsyncHttpClientTest.java index f7c107add488..f9cb55c3fbc9 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/S3CrtAsyncHttpClientTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crt/S3CrtAsyncHttpClientTest.java @@ -287,7 +287,8 @@ public void operationWithResponseAlgorithms_optOutValidationFromClient_shouldHon s3NativeClientConfiguration = S3NativeClientConfiguration.builder() .endpointOverride(DEFAULT_ENDPOINT) - .credentialsProvider(null) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", + "test"))) .build(); asyncHttpClient = new S3CrtAsyncHttpClient(s3Client, S3CrtAsyncHttpClient.builder() @@ -312,7 +313,8 @@ public void operationWithResponseAlgorithms_optInFromRequest_shouldHonor() { s3NativeClientConfiguration = S3NativeClientConfiguration.builder() .endpointOverride(DEFAULT_ENDPOINT) - .credentialsProvider(null) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", + "test"))) .build(); asyncHttpClient = new S3CrtAsyncHttpClient(s3Client, S3CrtAsyncHttpClient.builder() @@ -425,6 +427,8 @@ void build_shouldPassThroughParameters() { .signingRegion(signingRegion) .thresholdInBytes(1024L) .targetThroughputInGbps(3.5) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", + "test"))) .maxNativeMemoryLimitInBytes(5L * 1024 * 1024 * 1024) .standardRetryOptions( new StandardRetryOptions() @@ -466,6 +470,8 @@ void build_partSizeConfigured_shouldApplyToThreshold() { long partSizeInBytes = 1024 * 8L; S3NativeClientConfiguration configuration = S3NativeClientConfiguration.builder() + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", + "test"))) .partSizeInBytes(partSizeInBytes) .build(); try (S3CrtAsyncHttpClient client = @@ -480,6 +486,8 @@ void build_partSizeConfigured_shouldApplyToThreshold() { void build_nullHttpConfiguration() { S3NativeClientConfiguration configuration = S3NativeClientConfiguration.builder() + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", + "test"))) .build(); try (S3CrtAsyncHttpClient client = (S3CrtAsyncHttpClient) S3CrtAsyncHttpClient.builder().s3ClientConfiguration(configuration).build()) { @@ -539,6 +547,8 @@ void build_ProxyConfigurationWithEnvironmentVariables(S3CrtHttpConfiguration s3C S3NativeClientConfiguration configuration = S3NativeClientConfiguration.builder() .httpConfiguration(s3CrtHttpConfiguration) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("test", + "test"))) .build(); try(S3CrtAsyncHttpClient client = (S3CrtAsyncHttpClient) S3CrtAsyncHttpClient.builder().s3ClientConfiguration(configuration).build()) {