• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.awscore.util;
17 
18 import java.util.Optional;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21 import software.amazon.awssdk.annotations.SdkProtectedApi;
22 import software.amazon.awssdk.regions.Region;
23 
24 @SdkProtectedApi
25 //TODO This isn't used for as many services as it supports. Can we simplify or remove it?
26 public final class AwsHostNameUtils {
27 
28     private static final Pattern S3_ENDPOINT_PATTERN =
29             Pattern.compile("^(?:.+\\.)?s3[.-]([a-z0-9-]+)$");
30 
31     private static final Pattern STANDARD_CLOUDSEARCH_ENDPOINT_PATTERN =
32             Pattern.compile("^(?:.+\\.)?([a-z0-9-]+)\\.cloudsearch$");
33 
34     private static final Pattern EXTENDED_CLOUDSEARCH_ENDPOINT_PATTERN =
35             Pattern.compile("^(?:.+\\.)?([a-z0-9-]+)\\.cloudsearch\\..+");
36 
AwsHostNameUtils()37     private AwsHostNameUtils() {
38     }
39 
40     /**
41      * Attempts to parse the region name from an endpoint based on conventions
42      * about the endpoint format.
43      *
44      * @param host         the hostname to parse
45      * @param serviceHint  an optional hint about the service for the endpoint
46      * @return the region parsed from the hostname, or
47      *                     null if no region information could be found.
48      */
parseSigningRegion(final String host, final String serviceHint)49     public static Optional<Region> parseSigningRegion(final String host, final String serviceHint) {
50 
51         if (host == null) {
52             throw new IllegalArgumentException("hostname cannot be null");
53         }
54 
55         if (host.endsWith(".amazonaws.com")) {
56             int index = host.length() - ".amazonaws.com".length();
57             return parseStandardRegionName(host.substring(0, index));
58         }
59 
60         if (serviceHint != null) {
61             if (serviceHint.equals("cloudsearch") && !host.startsWith("cloudsearch.")) {
62                 // CloudSearch domains use the nonstandard domain format
63                 // [domain].[region].cloudsearch.[suffix].
64 
65                 Matcher matcher = EXTENDED_CLOUDSEARCH_ENDPOINT_PATTERN.matcher(host);
66                 if (matcher.matches()) {
67                     return Optional.of(Region.of(matcher.group(1)));
68                 }
69             }
70 
71             // If we have a service hint, look for 'service.[region]' or
72             // 'service-[region]' in the endpoint's hostname.
73             Pattern pattern = Pattern.compile("^(?:.+\\.)?" + Pattern.quote(serviceHint) + "[.-]([a-z0-9-]+)\\.");
74 
75             Matcher matcher = pattern.matcher(host);
76             if (matcher.find()) {
77                 return Optional.of(Region.of(matcher.group(1)));
78             }
79         }
80 
81         // Endpoint is non-standard
82         return Optional.empty();
83     }
84 
85     /**
86      * Parses the region name from a standard (*.amazonaws.com) endpoint.
87      *
88      * @param fragment  the portion of the endpoint excluding
89      *                  &quot;.amazonaws.com&quot;
90      * @return the parsed region name (or &quot;us-east-1&quot; as a
91      *                  best guess if we can't tell for sure)
92      */
parseStandardRegionName(final String fragment)93     private static Optional<Region> parseStandardRegionName(final String fragment) {
94         Matcher matcher = S3_ENDPOINT_PATTERN.matcher(fragment);
95         if (matcher.matches()) {
96             // host was 'bucket.s3-[region].amazonaws.com'.
97             return Optional.of(Region.of(matcher.group(1)));
98         }
99 
100         matcher = STANDARD_CLOUDSEARCH_ENDPOINT_PATTERN.matcher(fragment);
101         if (matcher.matches()) {
102             // host was 'domain.[region].cloudsearch.amazonaws.com'.
103             return Optional.of(Region.of(matcher.group(1)));
104         }
105 
106         int index = fragment.lastIndexOf('.');
107         if (index == -1) {
108             // host was 'service.amazonaws.com', assume they're talking about us-east-1.
109             return Optional.of(Region.US_EAST_1);
110         }
111 
112         // host was 'service.[region].amazonaws.com'.
113         String region = fragment.substring(index + 1);
114 
115         // Special case for iam.us-gov.amazonaws.com, which is actually
116         // us-gov-west-1.
117         if ("us-gov".equals(region)) {
118             region = "us-gov-west-1";
119         }
120 
121         if ("s3".equals(region)) {
122             region = fragment.substring(index + 4);
123         }
124 
125         return Optional.of(Region.of(region));
126     }
127 }
128