Skip to content

Commit f42ebdd

Browse files
authored
Fix for Issue #4912 where Cross Region S3 Access using AWS_GLOBAL Endpoint failed. (#4921)
1 parent 918657c commit f42ebdd

File tree

5 files changed

+67
-6
lines changed

5 files changed

+67
-6
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "Amazon Simple Storage Service",
4+
"contributor": "",
5+
"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."
6+
}

services/s3/src/it/java/software/amazon/awssdk/services/s3/crossregion/S3CrossRegionCrtIntegrationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static void clearClass() {
4646
@BeforeEach
4747
public void initialize() {
4848
crossRegionS3Client = S3AsyncClient.crtBuilder()
49-
.region(CROSS_REGION)
49+
.region(Region.AWS_GLOBAL)
5050
.crossRegionAccessEnabled(true)
5151
.build();
5252
}

services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/crossregion/endpointprovider/BucketEndpointProvider.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,20 @@ public static BucketEndpointProvider create(S3EndpointProvider delegateEndPointP
4343
@Override
4444
public CompletableFuture<Endpoint> resolveEndpoint(S3EndpointParams endpointParams) {
4545
Region crossRegion = regionSupplier.get();
46-
return delegateEndPointProvider.resolveEndpoint(
47-
endpointParams.copy(c -> c.region(crossRegion == null ? endpointParams.region() : crossRegion)
48-
.useGlobalEndpoint(false)));
46+
S3EndpointParams.Builder endpointParamsBuilder = endpointParams.toBuilder();
47+
// Check if cross-region resolution has already occurred.
48+
if (crossRegion != null) {
49+
endpointParamsBuilder.region(crossRegion);
50+
} else {
51+
// For global regions, set the region to "us-east-1" to use regional endpoints.
52+
if (Region.AWS_GLOBAL.equals(endpointParams.region())) {
53+
endpointParamsBuilder.region(Region.US_EAST_1);
54+
}
55+
// Disable the global endpoint as S3 can properly redirect regions in the 'x-amz-bucket-region' header
56+
// only for regional endpoints.
57+
endpointParamsBuilder.useGlobalEndpoint(false);
58+
}
59+
return delegateEndPointProvider.resolveEndpoint(endpointParamsBuilder.build());
4960
}
5061
}
5162

services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionAsyncClientTest.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ void given_US_EAST_1_Client_resolveToRegionalEndpoints_when_crossRegion_is_True(
429429
}
430430

431431
@ParameterizedTest
432-
@ValueSource(strings = {"us-east-1", "us-east-2", "us-west-1", "aws-global"})
432+
@ValueSource(strings = {"us-east-1", "us-east-2", "us-west-1"})
433433
void given_AnyRegion_Client_Updates_the_useGlobalEndpointFlag_asFalse(String region) {
434434
mockAsyncHttpClient.stubResponses(successHttpResponse());
435435
S3EndpointProvider mockEndpointProvider = Mockito.mock(S3EndpointProvider.class);
@@ -450,6 +450,28 @@ void given_AnyRegion_Client_Updates_the_useGlobalEndpointFlag_asFalse(String reg
450450
});
451451
}
452452

453+
@Test
454+
void given_globalRegion_Client_Updates_region_to_useast1_and_useGlobalEndpointFlag_as_False() {
455+
String region = Region.AWS_GLOBAL.id();
456+
mockAsyncHttpClient.stubResponses(successHttpResponse());
457+
S3EndpointProvider mockEndpointProvider = Mockito.mock(S3EndpointProvider.class);
458+
459+
when(mockEndpointProvider.resolveEndpoint(ArgumentMatchers.any(S3EndpointParams.class)))
460+
.thenReturn(CompletableFuture.completedFuture(Endpoint.builder().url(URI.create("https://bucket.s3.amazonaws.com")).build()));
461+
462+
S3AsyncClient s3Client = clientBuilder().crossRegionAccessEnabled(true)
463+
.region(Region.of(region))
464+
.endpointProvider(mockEndpointProvider).build();
465+
s3Client.getObject(r -> r.bucket(BUCKET).key(KEY), AsyncResponseTransformer.toBytes()).join();
466+
assertThat(captureInterceptor.endpointProvider).isInstanceOf(BucketEndpointProvider.class);
467+
ArgumentCaptor<S3EndpointParams> collectionCaptor = ArgumentCaptor.forClass(S3EndpointParams.class);
468+
verify(mockEndpointProvider, atLeastOnce()).resolveEndpoint(collectionCaptor.capture());
469+
collectionCaptor.getAllValues().forEach(resolvedParams -> {
470+
assertThat(resolvedParams.region()).isEqualTo(Region.US_EAST_1);
471+
assertThat(resolvedParams.useGlobalEndpoint()).isFalse();
472+
});
473+
}
474+
453475
private S3AsyncClientBuilder clientBuilder() {
454476
return S3AsyncClient.builder()
455477
.httpClient(mockAsyncHttpClient)

services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/crossregion/S3CrossRegionSyncClientTest.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ void given_US_EAST_1_Client_resolveToRegionalEndpoints_when_crossRegion_is_True(
256256
}
257257

258258
@ParameterizedTest
259-
@ValueSource(strings = {"us-east-1", "us-east-2", "us-west-1", "aws-global"})
259+
@ValueSource(strings = {"us-east-1", "us-east-2", "us-west-1"})
260260
void given_AnyRegion_Client_Updates_the_useGlobalEndpointFlag_asFalse(String region) {
261261
mockSyncHttpClient.stubResponses(successHttpResponse());
262262
S3EndpointProvider mockEndpointProvider = Mockito.mock(S3EndpointProvider.class);
@@ -277,6 +277,28 @@ void given_AnyRegion_Client_Updates_the_useGlobalEndpointFlag_asFalse(String reg
277277
});
278278
}
279279

280+
@Test
281+
void given_globalRegion_Client_Updates_region_to_useast1_and_useGlobalEndpointFlag_as_False() {
282+
String region = Region.AWS_GLOBAL.id();
283+
mockSyncHttpClient.stubResponses(successHttpResponse());
284+
S3EndpointProvider mockEndpointProvider = Mockito.mock(S3EndpointProvider.class);
285+
286+
when(mockEndpointProvider.resolveEndpoint(ArgumentMatchers.any(S3EndpointParams.class)))
287+
.thenReturn(CompletableFuture.completedFuture(Endpoint.builder().url(URI.create("https://bucket.s3.amazonaws.com")).build()));
288+
289+
S3Client s3Client = clientBuilder().crossRegionAccessEnabled(true)
290+
.region(Region.of(region))
291+
.endpointProvider(mockEndpointProvider).build();
292+
s3Client.getObject(getObjectBuilder().build());
293+
assertThat(captureInterceptor.endpointProvider).isInstanceOf(BucketEndpointProvider.class);
294+
ArgumentCaptor<S3EndpointParams> collectionCaptor = ArgumentCaptor.forClass(S3EndpointParams.class);
295+
verify(mockEndpointProvider, atLeastOnce()).resolveEndpoint(collectionCaptor.capture());
296+
collectionCaptor.getAllValues().forEach(resolvedParams ->{
297+
assertThat(resolvedParams.region()).isEqualTo(Region.US_EAST_1);
298+
assertThat(resolvedParams.useGlobalEndpoint()).isFalse();
299+
});
300+
}
301+
280302
private static GetObjectRequest.Builder getObjectBuilder() {
281303
return GetObjectRequest.builder()
282304
.bucket(BUCKET)

0 commit comments

Comments
 (0)