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.regions.servicemetadata;
17
18 import java.net.URI;
19 import java.util.List;
20 import java.util.function.Supplier;
21 import software.amazon.awssdk.annotations.SdkPublicApi;
22 import software.amazon.awssdk.core.SdkSystemSetting;
23 import software.amazon.awssdk.profiles.ProfileFile;
24 import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
25 import software.amazon.awssdk.profiles.ProfileProperty;
26 import software.amazon.awssdk.regions.Region;
27 import software.amazon.awssdk.regions.ServiceMetadata;
28 import software.amazon.awssdk.regions.ServiceMetadataConfiguration;
29 import software.amazon.awssdk.regions.ServicePartitionMetadata;
30 import software.amazon.awssdk.utils.Lazy;
31 import software.amazon.awssdk.utils.Logger;
32
33 /**
34  * Decorator metadata class for S3 to allow customers to opt in to using the
35  * regional S3 us-east-1 endpoint instead of the legacy
36  * {@code s3.amazonaws.com} when specifying the us-east-1 region.
37  */

38 @SdkPublicApi
39 public final class EnhancedS3ServiceMetadata implements ServiceMetadata {
40     private static final Logger log = Logger.loggerFor(EnhancedS3ServiceMetadata.class);
41     private static final String REGIONAL_SETTING = "regional";
42
43     private final Lazy<Boolean> useUsEast1RegionalEndpoint;
44     private final ServiceMetadata s3ServiceMetadata;
45
46     public EnhancedS3ServiceMetadata() {
47         this(ServiceMetadataConfiguration.builder().build());
48     }
49
50     private EnhancedS3ServiceMetadata(ServiceMetadataConfiguration config) {
51         Supplier<ProfileFile> profileFile = config.profileFile() != null ? config.profileFile()
52                                                                          : ProfileFile::defaultProfileFile;
53         Supplier<String> profileName = config.profileName() != null ? () -> config.profileName()
54                                                                     : ProfileFileSystemSetting.AWS_PROFILE::getStringValueOrThrow;
55
56         this.useUsEast1RegionalEndpoint = new Lazy<>(() -> useUsEast1RegionalEndpoint(profileFile, profileName));
57         this.s3ServiceMetadata = new S3ServiceMetadata().reconfigure(config);
58     }
59
60     @Override
61     public URI endpointFor(Region region) {
62         if (Region.US_EAST_1.equals(region) && !useUsEast1RegionalEndpoint.getValue()) {
63             return URI.create("s3.amazonaws.com");
64         }
65         return s3ServiceMetadata.endpointFor(region);
66     }
67
68     @Override
69     public Region signingRegion(Region region) {
70         return s3ServiceMetadata.signingRegion(region);
71     }
72
73     @Override
74     public List<Region> regions() {
75         return s3ServiceMetadata.regions();
76     }
77
78     @Override
79     public List<ServicePartitionMetadata> servicePartitions() {
80         return s3ServiceMetadata.servicePartitions();
81     }
82
83     private boolean useUsEast1RegionalEndpoint(Supplier<ProfileFile> profileFile, Supplier<String> profileName) {
84         String env = envVarSetting();
85
86         if (env != null) {
87             return REGIONAL_SETTING.equalsIgnoreCase(env);
88         }
89
90         String profile = profileFileSetting(profileFile, profileName);
91
92         if (profile != null) {
93             return REGIONAL_SETTING.equalsIgnoreCase(profile);
94         }
95
96         return false;
97     }
98
99     private static String envVarSetting() {
100         return SdkSystemSetting.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT.getStringValue().orElse(null);
101     }
102
103     private String profileFileSetting(Supplier<ProfileFile> profileFileSupplier, Supplier<String> profileNameSupplier) {
104         try {
105             ProfileFile profileFile = profileFileSupplier.get();
106             String profileName = profileNameSupplier.get();
107             if (profileFile == null || profileName == null) {
108                 return null;
109             }
110             return profileFile.profile(profileName)
111                               .flatMap(p -> p.property(ProfileProperty.S3_US_EAST_1_REGIONAL_ENDPOINT))
112                               .orElse(null);
113         } catch (Exception t) {
114             log.warn(() -> "Unable to load config file", t);
115             return null;
116         }
117     }
118
119     @Override
120     public ServiceMetadata reconfigure(ServiceMetadataConfiguration configuration) {
121         return new EnhancedS3ServiceMetadata(configuration);
122     }
123 }
124