2121import java .io .IOException ;
2222import java .net .URI ;
2323import java .net .URISyntaxException ;
24+ import java .util .Locale ;
2425import java .util .Optional ;
2526import java .util .regex .Matcher ;
2627import java .util .regex .Pattern ;
27-
2828import javax .annotation .Nullable ;
2929
3030import org .slf4j .Logger ;
@@ -80,56 +80,67 @@ public class RegionResolution {
8080 private static final Pattern VPC_ENDPOINT_PATTERN =
8181 Pattern .compile ("^(?:.+\\ .)?([a-z0-9-]+)\\ .vpce\\ .amazonaws\\ .(?:com|com\\ .cn)$" );
8282
83- /**
84- * Error message when an endpoint is set with FIPS enabled: {@value}.
85- */
86- @ VisibleForTesting
87- public static final String ERROR_ENDPOINT_WITH_FIPS =
88- "Only S3 central endpoint cannot be set when " + FIPS_ENDPOINT + " is true" ;
83+ /**
84+ * Error message when an endpoint is set with FIPS enabled: {@value}.
85+ */
86+ @ VisibleForTesting
87+ public static final String ERROR_ENDPOINT_WITH_FIPS =
88+ "Only S3 central endpoint cannot be set when " + FIPS_ENDPOINT + " is true" ;
8989
9090 /**
9191 * Virtual hostnames MUST be used when using the FIPS endpoint.
9292 */
93- public static final String FIPS_PATH_ACCESS_INCOMPATIBLE =
94- "Path style access must be disabled when " + FIPS_ENDPOINT + " is true" ;
93+ public static final String FIPS_PATH_ACCESS_INCOMPATIBLE =
94+ "Path style access must be disabled when " + FIPS_ENDPOINT + " is true" ;
95+
96+ /**
97+ * String value for external region: {@value}.
98+ */
99+ public static final String EXTERNAL = "external" ;
100+
101+ /**
102+ * External region, used for third party endpoints.
103+ */
104+ public static final Region EXTERNAL_REGION = Region .of (EXTERNAL );
95105
96106 /**
97107 * How was the region resolved?
98108 */
99- public enum RegionResolutionMechanism {
109+ public enum RegionResolutionMechanism {
100110
101- CalculatedFromEndpoint ("Calculated from endpoint" ),
102- FallbackToCentral ("Fallback to central endpoint" ),
103- ParseVpceEndpoint ("Parse VPCE Endpoint" ),
104- Ec2Metadata ("EC2 Metadata" ),
105- Sdk ("SDK resolution chain" ),
106- Specified ("region specified" );
111+ CalculatedFromEndpoint ("Calculated from endpoint" ),
112+ ExternalEndpoint ("External endpoint" ),
113+ FallbackToCentral ("Fallback to central endpoint" ),
114+ ParseVpceEndpoint ("Parse VPCE Endpoint" ),
115+ Ec2Metadata ("EC2 Metadata" ),
116+ Sdk ("SDK resolution chain" ),
117+ Specified ("region specified" );
107118
108- /**
109- * Text of the mechanism.
110- */
111- private final String mechanism ;
119+ /**
120+ * Text of the mechanism.
121+ */
122+ private final String mechanism ;
112123
113- RegionResolutionMechanism (String mechanism ) {
114- this .mechanism = mechanism ;
115- }
124+ RegionResolutionMechanism (String mechanism ) {
125+ this .mechanism = mechanism ;
126+ }
116127
117128 /**
118129 * String value of the resolution mechanism.
119130 * @return the resolution mechanism.
120131 */
121- public String getMechanism () {
122- return mechanism ;
123- }
124-
125- @ Override
126- public String toString () {
127- final StringBuilder sb = new StringBuilder ("RegionResolutionMechanism{" );
128- sb .append ("mechanism='" ).append (mechanism ).append ('\'' );
129- sb .append ('}' );
130- return sb .toString ();
131- }
132- }
132+ public String getMechanism () {
133+ return mechanism ;
134+ }
135+
136+ @ Override
137+ public String toString () {
138+ final StringBuilder sb = new StringBuilder ("RegionResolutionMechanism{" );
139+ sb .append ("mechanism='" ).append (mechanism ).append ('\'' );
140+ sb .append ('}' );
141+ return sb .toString ();
142+ }
143+ }
133144
134145 /**
135146 * The resolution of a region and endpoint..
@@ -298,7 +309,6 @@ public String toString() {
298309
299310 /**
300311 * Given a endpoint string, create the endpoint URI.
301- *
302312 * @param endpoint possibly null endpoint.
303313 * @param secureConnections use secure HTTPS connection?
304314 * @return an endpoint uri or null if the endpoint was passed in was null/empty
@@ -341,7 +351,8 @@ public static Optional<Resolution> getS3RegionFromEndpoint(
341351 Matcher matcher = VPC_ENDPOINT_PATTERN .matcher (endpoint );
342352 if (matcher .find ()) {
343353 LOG .debug ("Mapping to VPCE" );
344- LOG .debug ("Endpoint {} is vpc endpoint; parsing region as {}" , endpoint , matcher .group (1 ));
354+ LOG .debug ("Endpoint {} is VPC endpoint; parsing region as {}" ,
355+ endpoint , matcher .group (1 ));
345356 return Optional .of (new Resolution (
346357 Region .of (matcher .group (1 )),
347358 RegionResolutionMechanism .ParseVpceEndpoint ));
@@ -357,6 +368,20 @@ public static Optional<Resolution> getS3RegionFromEndpoint(
357368 return Optional .empty ();
358369 }
359370
371+ /**
372+ * Is this an AWS endpoint, that is: has an endpoint been set which matches
373+ * amazon.
374+ * @param endpoint non-null endpoint URL
375+ * @return true if this is amazonaws or amazonaws china
376+ */
377+ public static boolean isAwsEndpoint (final String endpoint ) {
378+ final String h = endpoint .toLowerCase (Locale .ROOT );
379+ // Common AWS partitions: global (.amazonaws.com) and China (.amazonaws.com.cn).
380+ return h .endsWith (".amazonaws.com" )
381+ || h .endsWith (".amazonaws.com.cn" );
382+ }
383+
384+
360385 /**
361386 * Does the region name refer to an SDK region?
362387 * @param configuredRegion region in the configuration
@@ -394,6 +419,7 @@ public static Resolution calculateRegion(
394419
395420 // endpoint; may be null
396421 final String endpointStr = parameters .getEndpoint ();
422+ boolean endpointDeclared = endpointStr != null && !endpointStr .isEmpty ();
397423 // will be null if endpointStr is null/empty
398424 final URI endpoint = buildEndpointUri (endpointStr ,
399425 conf .getBoolean (SECURE_CONNECTIONS , DEFAULT_SECURE_CONNECTIONS ));
@@ -419,8 +445,7 @@ public static Resolution calculateRegion(
419445
420446 // central endpoint if no endpoint has been set, or it is explicitly
421447 // requested
422- boolean endpointEndsWithCentral = endpointStr == null
423- || endpointStr .isEmpty ()
448+ boolean endpointEndsWithCentral = !endpointDeclared
424449 || endpointStr .endsWith (CENTRAL_ENDPOINT );
425450
426451 if (!resolution .isRegionResolved ()) {
@@ -452,10 +477,18 @@ public static Resolution calculateRegion(
452477 FIPS_PATH_ACCESS_INCOMPATIBLE );
453478 }
454479
480+
455481 if (!resolution .isRegionResolved ()) {
456- // still failing to resolve the region
457- // fall back to central
458- resolution .withRegion (US_EAST_2 , RegionResolutionMechanism .FallbackToCentral );
482+ // still not resolved.
483+ if (!endpointDeclared || isAwsEndpoint (endpointStr )) {
484+ // still failing to resolve the region
485+ // fall back to central
486+ resolution .withRegion (US_EAST_2 , RegionResolutionMechanism .FallbackToCentral );
487+ } else {
488+ // we are not resolved and not an aws region.
489+ // set the region to being "external"
490+ resolution .withRegion (EXTERNAL_REGION , RegionResolutionMechanism .ExternalEndpoint );
491+ }
459492 }
460493
461494 // No need to override endpoint with "s3.amazonaws.com".
0 commit comments