2626import static software .amazon .awssdk .services .s3 .internal .crossregion .S3CrossRegionRedirectTestBase .X_AMZ_BUCKET_REGION ;
2727
2828import java .net .URI ;
29+ import java .time .Duration ;
2930import java .util .Arrays ;
3031import java .util .Collections ;
3132import java .util .List ;
3233import java .util .concurrent .CompletableFuture ;
3334import java .util .concurrent .CompletionException ;
35+ import java .util .concurrent .ExecutionException ;
3436import java .util .function .Consumer ;
3537import java .util .stream .Collectors ;
3638import java .util .stream .Stream ;
8082
8183class S3CrossRegionAsyncClientTest {
8284
85+ private static final String ILLEGAL_LOCATION_CONSTRAINT_EXCEPTION_ERROR_CODE = "IllegalLocationConstraintException" ;
8386 private static final String ERROR_RESPONSE_FORMAT = "<Error>\\ n\\ t<Code>%s</Code>\\ n</Error>" ;
8487 private static final String BUCKET = "bucket" ;
8588 private static final String KEY = "key" ;
@@ -97,26 +100,38 @@ void setUp() {
97100
98101 private static Stream <Arguments > stubSuccessfulRedirectResponses () {
99102 Consumer <MockAsyncHttpClient > redirectStubConsumer = mockAsyncHttpClient ->
100- mockAsyncHttpClient .stubResponses (customHttpResponseWithUnknownErrorCode (301 , CROSS_REGION .id ()), successHttpResponse ());
103+ mockAsyncHttpClient .stubResponses (customHttpResponseWithUnknownErrorCode (301 , CROSS_REGION .id ()),
104+ successHttpResponse ());
101105
102106 Consumer <MockAsyncHttpClient > successStubConsumer = mockAsyncHttpClient ->
103107 mockAsyncHttpClient .stubResponses (successHttpResponse (), successHttpResponse ());
104108
105109 Consumer <MockAsyncHttpClient > tempRedirectStubConsumer = mockAsyncHttpClient ->
106- mockAsyncHttpClient .stubResponses (customHttpResponseWithUnknownErrorCode (307 , CROSS_REGION .id ()), successHttpResponse ());
110+ mockAsyncHttpClient .stubResponses (customHttpResponseWithUnknownErrorCode (307 , CROSS_REGION .id ()),
111+ successHttpResponse ());
112+
113+ Consumer <MockAsyncHttpClient > locationConstraintExceptionConsumer = mockAsyncHttpClient ->
114+ mockAsyncHttpClient .stubResponses (customHttpResponse (400 ,
115+ ILLEGAL_LOCATION_CONSTRAINT_EXCEPTION_ERROR_CODE ,
116+ CROSS_REGION .id ()), successHttpResponse ());
117+
107118
108119 return Stream .of (
109120 Arguments .of (redirectStubConsumer , BucketEndpointProvider .class , "Redirect Error with region in x-amz-bucket-header" ),
110- Arguments .of (successStubConsumer , BucketEndpointProvider .class , "Success response" ),
111- Arguments .of (tempRedirectStubConsumer , BucketEndpointProvider .class , "Permanent redirect Error with region in x-amz-bucket-header" )
121+ Arguments .of (successStubConsumer , BucketEndpointProvider .class , "Success response" ),
122+ Arguments .of (tempRedirectStubConsumer , BucketEndpointProvider .class ,
123+ "Permanent redirect Error with region in x-amz-bucket-header" ),
124+ Arguments .of (locationConstraintExceptionConsumer , BucketEndpointProvider .class ,
125+ "Redirect error: 400 IllegalLocationConstraintException with region in x-amz-bucket-header" )
112126 );
113127 }
114128
115129
116130 private static Stream <Arguments > stubFailureResponses () {
117131
118132 List <SdkHttpMethod > noregionOnHeadBucketHttpMethodListMethodList = Arrays .asList (SdkHttpMethod .GET , SdkHttpMethod .HEAD );
119- List <SdkHttpMethod > regionOnHeadBucketHttpMethodList = Arrays .asList (SdkHttpMethod .GET , SdkHttpMethod .HEAD , SdkHttpMethod .GET );
133+ List <SdkHttpMethod > regionOnHeadBucketHttpMethodList = Arrays .asList (SdkHttpMethod .GET , SdkHttpMethod .HEAD ,
134+ SdkHttpMethod .GET );
120135 List <String > noRegionOnHeadBucket = Arrays .asList (OVERRIDE_CONFIGURED_REGION .toString (),
121136 OVERRIDE_CONFIGURED_REGION .toString ());
122137
@@ -129,7 +144,8 @@ private static Stream<Arguments> stubFailureResponses() {
129144 customHttpResponseWithUnknownErrorCode (301 , null ),
130145 successHttpResponse (), successHttpResponse ());
131146 return Stream .of (
132- Arguments .of (redirectFailedWithNoRegionFailure , 301 , 2 , noRegionOnHeadBucket , noregionOnHeadBucketHttpMethodListMethodList )
147+ Arguments .of (redirectFailedWithNoRegionFailure , 301 , 2 , noRegionOnHeadBucket ,
148+ noregionOnHeadBucketHttpMethodListMethodList )
133149 );
134150 }
135151
@@ -159,8 +175,8 @@ public static HttpExecuteResponse customHttpResponse(int statusCode, String erro
159175 @ ParameterizedTest (name = "{index} - {2}." )
160176 @ MethodSource ("stubSuccessfulRedirectResponses" )
161177 void given_CrossRegionClientWithNoOverrideConfig_when_StandardOperationIsPerformed_then_SuccessfullyIntercepts (Consumer <MockAsyncHttpClient > stubConsumer ,
162- Class <?> endpointProviderType ,
163- String testCase ) {
178+ Class <?> endpointProviderType ,
179+ String testCase ) {
164180 stubConsumer .accept (mockAsyncHttpClient );
165181 S3AsyncClient crossRegionClient = new S3CrossRegionAsyncClient (s3Client );
166182 crossRegionClient .getObject (r -> r .bucket (BUCKET ).key (KEY ), AsyncResponseTransformer .toBytes ()).join ();
@@ -170,8 +186,9 @@ void given_CrossRegionClientWithNoOverrideConfig_when_StandardOperationIsPerform
170186 @ ParameterizedTest (name = "{index} - {2}." )
171187 @ MethodSource ("stubSuccessfulRedirectResponses" )
172188 void given_CrossRegionClientWithExistingOverrideConfig_when_StandardOperationIsPerformed_then_SuccessfullyIntercepts (Consumer <MockAsyncHttpClient > stubConsumer ,
173- Class <?> endpointProviderType ,
174- String testCase ) {
189+ Class <
190+ ?> endpointProviderType ,
191+ String testCase ) {
175192 stubConsumer .accept (mockAsyncHttpClient );
176193 S3AsyncClient crossRegionClient = new S3CrossRegionAsyncClient (s3Client );
177194 GetObjectRequest request = GetObjectRequest .builder ()
@@ -187,8 +204,8 @@ void given_CrossRegionClientWithExistingOverrideConfig_when_StandardOperationIsP
187204 @ ParameterizedTest (name = "{index} - {2}." )
188205 @ MethodSource ("stubSuccessfulRedirectResponses" )
189206 void given_CrossRegionClient_when_PaginatedOperationIsPerformed_then_DoesNotIntercept (Consumer <MockAsyncHttpClient > stubConsumer ,
190- Class <?> endpointProviderType ,
191- String testCase ) throws Exception {
207+ Class <?> endpointProviderType ,
208+ String testCase ) throws Exception {
192209 stubConsumer .accept (mockAsyncHttpClient );
193210 S3AsyncClient crossRegionClient = new S3CrossRegionAsyncClient (s3Client );
194211 ListObjectsV2Publisher publisher =
@@ -201,8 +218,8 @@ void given_CrossRegionClient_when_PaginatedOperationIsPerformed_then_DoesNotInte
201218 @ ParameterizedTest (name = "{index} - {2}." )
202219 @ MethodSource ("stubSuccessfulRedirectResponses" )
203220 void given_CrossRegionClientCreatedWithWrapping_when_OperationIsPerformed_then_SuccessfullyIntercepts (Consumer <MockAsyncHttpClient > stubConsumer ,
204- Class <?> endpointProviderType ,
205- String testCase ) {
221+ Class <?> endpointProviderType ,
222+ String testCase ) {
206223 stubConsumer .accept (mockAsyncHttpClient );
207224 S3AsyncClient crossRegionClient = clientBuilder ().crossRegionAccessEnabled (true ).build ();
208225 crossRegionClient .getObject (r -> r .bucket (BUCKET ).key (KEY ), AsyncResponseTransformer .toBytes ()).join ();
@@ -264,7 +281,7 @@ void given_CrossRegionClient_when_CallsHeadObjectErrors_then_ShouldTerminateTheA
264281
265282 String errorMessage = String .format ("software.amazon.awssdk.services.s3.model.S3Exception: "
266283 + "(Service: S3, Status Code: %d, Request ID: null)"
267- , statusCode );
284+ , statusCode );
268285 assertThatExceptionOfType (CompletionException .class )
269286 .isThrownBy (() -> crossRegionClient .getObject (r -> r .bucket (BUCKET ).key (KEY ), AsyncResponseTransformer .toBytes ()).join ())
270287 .withMessageContaining (errorMessage );
@@ -395,7 +412,6 @@ void given_CrossRegionClient_when_StandardOperation_then_ContainsUserAgent() {
395412 }
396413
397414
398-
399415 @ ParameterizedTest (name = "{index} - {2}." )
400416 @ MethodSource ("stubSuccessfulRedirectResponses" )
401417 void given_CrossRegionAccessEnabled_when_SuccessfulResponse_then_EndpointIsUpdated (Consumer <MockAsyncHttpClient > stubConsumer ,
@@ -416,7 +432,7 @@ void given_SimpleClient_when_StandardOperation_then_DoesNotContainCrossRegionUse
416432 }
417433
418434 @ Test
419- void given_US_EAST_1_Client_resolvesToGlobalEndpoints_when_crossRegion_is_False (){
435+ void given_US_EAST_1_Client_resolvesToGlobalEndpoints_when_crossRegion_is_False () {
420436 mockAsyncHttpClient .stubResponses (successHttpResponse ());
421437 S3AsyncClient s3Client = clientBuilder ().region (Region .US_EAST_1 ).build ();
422438 s3Client .getObject (r -> r .bucket (BUCKET ).key (KEY ), AsyncResponseTransformer .toBytes ()).join ();
@@ -425,7 +441,7 @@ void given_US_EAST_1_Client_resolvesToGlobalEndpoints_when_crossRegion_is_False(
425441 }
426442
427443 @ Test
428- void given_US_EAST_1_Client_resolveToRegionalEndpoints_when_crossRegion_is_True (){
444+ void given_US_EAST_1_Client_resolveToRegionalEndpoints_when_crossRegion_is_True () {
429445 mockAsyncHttpClient .stubResponses (successHttpResponse ());
430446 S3AsyncClient s3Client = clientBuilder ().crossRegionAccessEnabled (true ).region (Region .US_EAST_1 ).build ();
431447 s3Client .getObject (r -> r .bucket (BUCKET ).key (KEY ), AsyncResponseTransformer .toBytes ()).join ();
@@ -478,6 +494,30 @@ void given_globalRegion_Client_Updates_region_to_useast1_and_useGlobalEndpointFl
478494
479495 }
480496
497+ @ Test
498+ void given_CrossRegionClient_when400Error_WithoutIllegalLocationConstraint_DoesNotRedirect () {
499+ String region = Region .AWS_GLOBAL .id ();
500+ mockAsyncHttpClient .stubResponses (customHttpResponse (400 , "UnknownError" , null ));
501+ S3EndpointProvider mockEndpointProvider = Mockito .mock (S3EndpointProvider .class );
502+ when (mockEndpointProvider .resolveEndpoint (ArgumentMatchers .any (S3EndpointParams .class )))
503+ .thenReturn (CompletableFuture .completedFuture (Endpoint .builder ().url (URI .create ("https://bucket.s3.amazonaws.com" )).build ()));
504+
505+ S3AsyncClient s3Client = clientBuilder ().crossRegionAccessEnabled (true )
506+ .region (Region .of (region ))
507+ .endpointProvider (mockEndpointProvider ).build ();
508+
509+ CompletableFuture <ResponseBytes <GetObjectResponse >> f =
510+ s3Client .getObject (r -> r .bucket (BUCKET ).key (KEY ), AsyncResponseTransformer .toBytes ());
511+
512+ assertThat (f ).failsWithin (Duration .ofSeconds (5 ))
513+ .withThrowableOfType (ExecutionException .class )
514+ .withCauseInstanceOf (S3Exception .class )
515+ .withMessageContaining ("Status Code: 400" );
516+
517+ assertThat (mockAsyncHttpClient .getRequests ()).hasSize (1 );
518+ }
519+
520+
481521 private S3AsyncClientBuilder clientBuilder () {
482522 return S3AsyncClient .builder ()
483523 .httpClient (mockAsyncHttpClient )
0 commit comments