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.endpoint; 17 18 import java.net.URI; 19 import java.net.URISyntaxException; 20 import java.util.ArrayList; 21 import java.util.HashMap; 22 import java.util.List; 23 import java.util.Map; 24 import java.util.function.Supplier; 25 import software.amazon.awssdk.annotations.NotThreadSafe; 26 import software.amazon.awssdk.annotations.SdkProtectedApi; 27 import software.amazon.awssdk.core.exception.SdkClientException; 28 import software.amazon.awssdk.profiles.ProfileFile; 29 import software.amazon.awssdk.profiles.ProfileFileSystemSetting; 30 import software.amazon.awssdk.regions.EndpointTag; 31 import software.amazon.awssdk.regions.Region; 32 import software.amazon.awssdk.regions.ServiceEndpointKey; 33 import software.amazon.awssdk.regions.ServiceMetadata; 34 import software.amazon.awssdk.regions.ServiceMetadataAdvancedOption; 35 import software.amazon.awssdk.utils.Lazy; 36 import software.amazon.awssdk.utils.Validate; 37 38 /** 39 * Uses service metadata and the request region to construct an endpoint for a specific service 40 */ 41 @NotThreadSafe 42 @SdkProtectedApi 43 // TODO We may not need this anymore, we should default to AWS partition when resolving 44 // a region we don't know about yet. 45 public final class DefaultServiceEndpointBuilder { 46 private final String serviceName; 47 private final String protocol; 48 49 private Region region; 50 private Supplier<ProfileFile> profileFile; 51 private String profileName; 52 private final Map<ServiceMetadataAdvancedOption<?>, Object> advancedOptions = new HashMap<>(); 53 private Boolean dualstackEnabled; 54 private Boolean fipsEnabled; 55 DefaultServiceEndpointBuilder(String serviceName, String protocol)56 public DefaultServiceEndpointBuilder(String serviceName, String protocol) { 57 this.serviceName = Validate.paramNotNull(serviceName, "serviceName"); 58 this.protocol = Validate.paramNotNull(protocol, "protocol"); 59 } 60 withRegion(Region region)61 public DefaultServiceEndpointBuilder withRegion(Region region) { 62 if (region == null) { 63 throw new IllegalArgumentException("Region cannot be null"); 64 } 65 this.region = region; 66 return this; 67 } 68 withProfileFile(Supplier<ProfileFile> profileFile)69 public DefaultServiceEndpointBuilder withProfileFile(Supplier<ProfileFile> profileFile) { 70 this.profileFile = profileFile; 71 return this; 72 } 73 withProfileFile(ProfileFile profileFile)74 public DefaultServiceEndpointBuilder withProfileFile(ProfileFile profileFile) { 75 this.profileFile = () -> profileFile; 76 return this; 77 } 78 withProfileName(String profileName)79 public DefaultServiceEndpointBuilder withProfileName(String profileName) { 80 this.profileName = profileName; 81 return this; 82 } 83 putAdvancedOption(ServiceMetadataAdvancedOption<T> option, T value)84 public <T> DefaultServiceEndpointBuilder putAdvancedOption(ServiceMetadataAdvancedOption<T> option, T value) { 85 advancedOptions.put(option, value); 86 return this; 87 } 88 withDualstackEnabled(Boolean dualstackEnabled)89 public DefaultServiceEndpointBuilder withDualstackEnabled(Boolean dualstackEnabled) { 90 this.dualstackEnabled = dualstackEnabled; 91 return this; 92 } 93 withFipsEnabled(Boolean fipsEnabled)94 public DefaultServiceEndpointBuilder withFipsEnabled(Boolean fipsEnabled) { 95 this.fipsEnabled = fipsEnabled; 96 return this; 97 } 98 getServiceEndpoint()99 public URI getServiceEndpoint() { 100 if (profileFile == null) { 101 profileFile = new Lazy<>(ProfileFile::defaultProfileFile)::getValue; 102 } 103 104 if (profileName == null) { 105 profileName = ProfileFileSystemSetting.AWS_PROFILE.getStringValueOrThrow(); 106 } 107 108 if (dualstackEnabled == null) { 109 dualstackEnabled = DualstackEnabledProvider.builder() 110 .profileFile(profileFile) 111 .profileName(profileName) 112 .build() 113 .isDualstackEnabled() 114 .orElse(false); 115 } 116 117 if (fipsEnabled == null) { 118 fipsEnabled = FipsEnabledProvider.builder() 119 .profileFile(profileFile) 120 .profileName(profileName) 121 .build() 122 .isFipsEnabled() 123 .orElse(false); 124 } 125 126 127 128 List<EndpointTag> endpointTags = new ArrayList<>(); 129 if (dualstackEnabled) { 130 endpointTags.add(EndpointTag.DUALSTACK); 131 } 132 if (fipsEnabled) { 133 endpointTags.add(EndpointTag.FIPS); 134 } 135 136 ServiceMetadata serviceMetadata = ServiceMetadata.of(serviceName) 137 .reconfigure(c -> c.profileFile(profileFile) 138 .profileName(profileName) 139 .advancedOptions(advancedOptions)); 140 URI endpoint = addProtocolToServiceEndpoint(serviceMetadata.endpointFor(ServiceEndpointKey.builder() 141 .region(region) 142 .tags(endpointTags) 143 .build())); 144 145 if (endpoint.getHost() == null) { 146 String error = "Configured region (" + region + ") and tags (" + endpointTags + ") resulted in an invalid URI: " 147 + endpoint + ". This is usually caused by an invalid region configuration."; 148 149 List<Region> exampleRegions = serviceMetadata.regions(); 150 if (!exampleRegions.isEmpty()) { 151 error += " Valid regions: " + exampleRegions; 152 } 153 154 throw SdkClientException.create(error); 155 } 156 157 return endpoint; 158 } 159 addProtocolToServiceEndpoint(URI endpointWithoutProtocol)160 private URI addProtocolToServiceEndpoint(URI endpointWithoutProtocol) throws IllegalArgumentException { 161 try { 162 return new URI(protocol + "://" + endpointWithoutProtocol); 163 } catch (URISyntaxException e) { 164 throw new IllegalArgumentException(e); 165 } 166 } 167 getRegion()168 public Region getRegion() { 169 return region; 170 } 171 } 172