diff --git a/.changes/next-release/bugfix-AmazonSimpleStorageService-7648336.json b/.changes/next-release/bugfix-AmazonSimpleStorageService-7648336.json new file mode 100644 index 000000000000..ccfba3083092 --- /dev/null +++ b/.changes/next-release/bugfix-AmazonSimpleStorageService-7648336.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "Amazon Simple Storage Service", + "contributor": "", + "description": "Fix for Issue [#4912](https://github.com/aws/aws-sdk-java-v2/issues/4912) where client region with AWS_GLOBAL calls failed for cross region access." +} diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/crossregion/S3CrossRegionCrtIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/crossregion/S3CrossRegionCrtIntegrationTest.java index 9749b4920539..e6a47bc948d6 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/crossregion/S3CrossRegionCrtIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/crossregion/S3CrossRegionCrtIntegrationTest.java @@ -46,7 +46,7 @@ static void clearClass() { @BeforeEach public void initialize() { crossRegionS3Client = S3AsyncClient.crtBuilder() - .region(CROSS_REGION) + .region(Region.AWS_GLOBAL) .crossRegionAccessEnabled(true) .build(); } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crossregion/endpointprovider/BucketEndpointProvider.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crossregion/endpointprovider/BucketEndpointProvider.java index 84f1e69abf8a..fd9d5260a5ad 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crossregion/endpointprovider/BucketEndpointProvider.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crossregion/endpointprovider/BucketEndpointProvider.java @@ -43,9 +43,16 @@ public static BucketEndpointProvider create(S3EndpointProvider delegateEndPointP @Override public CompletableFuture resolveEndpoint(S3EndpointParams endpointParams) { Region crossRegion = regionSupplier.get(); - return delegateEndPointProvider.resolveEndpoint( - endpointParams.copy(c -> c.region(crossRegion == null ? endpointParams.region() : crossRegion) - .useGlobalEndpoint(false))); + S3EndpointParams.Builder endpointParamsBuilder = endpointParams.toBuilder(); + if (crossRegion != null) { + endpointParamsBuilder.region(crossRegion); + } else { + if (Region.AWS_GLOBAL.equals(endpointParams.region())) { + endpointParamsBuilder.region(Region.US_EAST_1); + } + endpointParamsBuilder.useGlobalEndpoint(false); + } + return delegateEndPointProvider.resolveEndpoint(endpointParamsBuilder.build()); } } diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionAsyncClientTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionAsyncClientTest.java index f4139c883bd6..ea91789c725f 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionAsyncClientTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionAsyncClientTest.java @@ -429,7 +429,7 @@ void given_US_EAST_1_Client_resolveToRegionalEndpoints_when_crossRegion_is_True( } @ParameterizedTest - @ValueSource(strings = {"us-east-1", "us-east-2", "us-west-1", "aws-global"}) + @ValueSource(strings = {"us-east-1", "us-east-2", "us-west-1"}) void given_AnyRegion_Client_Updates_the_useGlobalEndpointFlag_asFalse(String region) { mockAsyncHttpClient.stubResponses(successHttpResponse()); S3EndpointProvider mockEndpointProvider = Mockito.mock(S3EndpointProvider.class); @@ -450,6 +450,31 @@ void given_AnyRegion_Client_Updates_the_useGlobalEndpointFlag_asFalse(String reg }); } + + + @Test + void given_global_Client_Updates_the_regionAnduseGlobalEndpointFlag_asFalse() { + String region = Region.AWS_GLOBAL.id(); + mockAsyncHttpClient.stubResponses(successHttpResponse()); + S3EndpointProvider mockEndpointProvider = Mockito.mock(S3EndpointProvider.class); + + when(mockEndpointProvider.resolveEndpoint(ArgumentMatchers.any(S3EndpointParams.class))) + .thenReturn(CompletableFuture.completedFuture(Endpoint.builder().url(URI.create("https://bucket.s3.amazonaws.com")).build())); + + S3AsyncClient s3Client = clientBuilder().crossRegionAccessEnabled(true) + .region(Region.of(region)) + .endpointProvider(mockEndpointProvider).build(); + s3Client.getObject(r -> r.bucket(BUCKET).key(KEY), AsyncResponseTransformer.toBytes()).join(); + assertThat(captureInterceptor.endpointProvider).isInstanceOf(BucketEndpointProvider.class); + ArgumentCaptor collectionCaptor = ArgumentCaptor.forClass(S3EndpointParams.class); + verify(mockEndpointProvider, atLeastOnce()).resolveEndpoint(collectionCaptor.capture()); + collectionCaptor.getAllValues().forEach(resolvedParams -> { + assertThat(resolvedParams.region()).isEqualTo(Region.US_EAST_1); + assertThat(resolvedParams.useGlobalEndpoint()).isFalse(); + }); + } + + private S3AsyncClientBuilder clientBuilder() { return S3AsyncClient.builder() .httpClient(mockAsyncHttpClient) diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionSyncClientTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionSyncClientTest.java index a17ded1bdb09..ce7b393df620 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionSyncClientTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionSyncClientTest.java @@ -256,7 +256,7 @@ void given_US_EAST_1_Client_resolveToRegionalEndpoints_when_crossRegion_is_True( } @ParameterizedTest - @ValueSource(strings = {"us-east-1", "us-east-2", "us-west-1", "aws-global"}) + @ValueSource(strings = {"us-east-1", "us-east-2", "us-west-1"}) void given_AnyRegion_Client_Updates_the_useGlobalEndpointFlag_asFalse(String region) { mockSyncHttpClient.stubResponses(successHttpResponse()); S3EndpointProvider mockEndpointProvider = Mockito.mock(S3EndpointProvider.class); @@ -277,6 +277,28 @@ void given_AnyRegion_Client_Updates_the_useGlobalEndpointFlag_asFalse(String reg }); } + @Test + void globalRegion() { + String region = Region.AWS_GLOBAL.id(); + mockSyncHttpClient.stubResponses(successHttpResponse()); + S3EndpointProvider mockEndpointProvider = Mockito.mock(S3EndpointProvider.class); + + when(mockEndpointProvider.resolveEndpoint(ArgumentMatchers.any(S3EndpointParams.class))) + .thenReturn(CompletableFuture.completedFuture(Endpoint.builder().url(URI.create("https://bucket.s3.amazonaws.com")).build())); + + S3Client s3Client = clientBuilder().crossRegionAccessEnabled(true) + .region(Region.of(region)) + .endpointProvider(mockEndpointProvider).build(); + s3Client.getObject(getObjectBuilder().build()); + assertThat(captureInterceptor.endpointProvider).isInstanceOf(BucketEndpointProvider.class); + ArgumentCaptor collectionCaptor = ArgumentCaptor.forClass(S3EndpointParams.class); + verify(mockEndpointProvider, atLeastOnce()).resolveEndpoint(collectionCaptor.capture()); + collectionCaptor.getAllValues().forEach(resolvedParams ->{ + assertThat(resolvedParams.region()).isEqualTo(Region.US_EAST_1); + assertThat(resolvedParams.useGlobalEndpoint()).isFalse(); + }); + } + private static GetObjectRequest.Builder getObjectBuilder() { return GetObjectRequest.builder() .bucket(BUCKET)