/******************************************************************************* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"). You may not use * this file except in compliance with the License. A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. * ***************************************************************************** * __ _ _ ___ * ( )( \/\/ )/ __) * /__\ \ / \__ \ * (_)(_) \/\/ (___/ * * AWS SDK for .NET * */ using Amazon.Internal; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Threading; using ThirdParty.Json.LitJson; namespace Amazon { /// /// This class contains region information used to lazily compute the service endpoints. The static constants representing the /// regions can be used while constructing the AWS client instead of looking up the exact endpoint URL. /// public partial class RegionEndpoint { #region Statics private static Dictionary _hashBySystemName = new Dictionary(StringComparer.OrdinalIgnoreCase); private static ReaderWriterLockSlim _regionEndpointOverrideLock = new ReaderWriterLockSlim(); // controls access to _hashRegionEndpointOverride /// /// Represents the endpoint overridding rules in the endpoints.json /// Is used to map private region (ie us-east-1-regional) to public regions (us-east-1) /// For signing purposes. Map is keyed by region SystemName. /// private static Dictionary _hashRegionEndpointOverride = new Dictionary(); /// /// Enumerate through all the regions. /// public static IEnumerable EnumerableAllRegions { get { List list = new List(); foreach (IRegionEndpoint endpoint in RegionEndpointProvider.AllRegionEndpoints) { list.Add(GetEndpoint(endpoint.RegionName, endpoint.DisplayName)); } return list; } } /// /// Gets the region based on its system name like "us-west-1" /// /// The system name of the service like "us-west-1" /// public static RegionEndpoint GetBySystemName(string systemName) { return GetEndpoint(systemName, null); } /// /// Gets the region endpoint override if exists /// /// The region endpoint to find the possible override for /// [Obsolete("This operation is obsoleted because as of version 3.7.100 endpoint is resolved using a newer system that uses request level parameters to resolve the endpoint.")] public static RegionEndpoint GetRegionEndpointOverride(RegionEndpoint regionEndpoint) { try { _regionEndpointOverrideLock.EnterReadLock(); if (!_hashRegionEndpointOverride.TryGetValue(regionEndpoint.SystemName, out var regionEndpointOverride)) { return null; } return regionEndpointOverride; } finally { _regionEndpointOverrideLock.ExitReadLock(); } } /// /// Force the SDK to load and apply the given endpoints details /// (eg: an updated endpoints.json file). /// Service clients created after this call will be set up with /// endpoints based on this information. /// /// This function should only be used at application startup, before /// creating service clients. /// /// Known Caveats: /// * static readonly fields (eg: ) are not updated. /// If you use this function, you should use with /// explicit region system names to ensure you work with RegionEndpoint objects containing /// the reloaded data. RegionEndpoint objects returned from GetEndpoint will generally /// fail Equality comparisons against the static fields. /// * Service clients created before calling Reload have no guarantee around /// which endpoint data will be used. /// /// Stream containing an Endpoints manifest to reload in the SDK. /// Pass null in to reset the SDK, so that it uses its built-in manifest instead. [Obsolete("This operation is obsoleted because as of version 3.7.100 endpoint is resolved using a newer system that uses request level parameters to resolve the endpoint.")] public static void Reload(Stream stream) { if (stream == null) { _regionEndpointProvider = null; } else { JsonData rootData = null; using (StreamReader reader = new StreamReader(stream)) { rootData = JsonMapper.ToObject(reader); } var manifestVersion = rootData?["version"]?.ToString(); if (manifestVersion == "3") { _regionEndpointProvider = new RegionEndpointProviderV3(rootData); } else { throw new NotSupportedException("Endpoints data format is not supported by reload"); } } // Reset static lookup maps that may contain objects relating to old endpoint data lock (_hashBySystemName) { _hashBySystemName.Clear(); // Add the .NET/S3-specific legacy region back to the lookup map GetEndpoint("us-east-1-regional", "US East (Virginia) regional"); } ResetRegionEndpointOverride(); } /// /// Rebuilds the endpoint override map, referencing the SDK's current Region Endpoint data. /// private static void ResetRegionEndpointOverride() { try { _regionEndpointOverrideLock.EnterWriteLock(); _hashRegionEndpointOverride.Clear(); _hashRegionEndpointOverride.Add(USEast1Regional.SystemName, GetEndpoint(USEast1.SystemName, null)); } finally { _regionEndpointOverrideLock.ExitWriteLock(); } } /// /// Returns the DNS suffix for the given partition, or /// an empty string if a matching partition was not found in endpoints.json /// /// partition /// DNS suffix for the given partition, empty string if a matching partition was not found [Obsolete("This operation is obsoleted because as of version 3.7.100 endpoint is resolved using a newer system that uses request level parameters to resolve the endpoint.")] public static string GetDnsSuffixForPartition(string partition) { return RegionEndpointProvider.GetDnsSuffixForPartition(partition); } private static RegionEndpoint GetEndpoint(string systemName, string displayName) { RegionEndpoint regionEndpoint = null; if (displayName == null) { lock (_hashBySystemName) { if (_hashBySystemName.TryGetValue(systemName, out regionEndpoint)) return regionEndpoint; } // GetRegionEndpoint will always return a non-null value. If the the region(systemName) is unknown, // the providers will create a fallback instance that will generate an endpoint to the best // of its knowledge. displayName = RegionEndpointProvider.GetRegionEndpoint(systemName).DisplayName; } lock (_hashBySystemName) { if (_hashBySystemName.TryGetValue(systemName, out regionEndpoint)) return regionEndpoint; regionEndpoint = new RegionEndpoint(systemName, displayName); _hashBySystemName.Add(regionEndpoint.SystemName, regionEndpoint); } return regionEndpoint; } private static IRegionEndpointProvider _regionEndpointProvider; private static IRegionEndpointProvider RegionEndpointProvider { get { if (_regionEndpointProvider == null) { // If the existing customer provided the endpoints.json file via // , it's in v2 format. We we will create // a v2 provider which does a fall through during LoadEndpointDefinitions() // and loads from the override file provided by the user. // // Else, we are loading from the assembly resource. In which case we use the // latest provider. // // It's actually a bug that _regionEndpointProvider is a static member variable // since the IEndpointProvider should respect the AWSConfigs.EndpointDefinition // _at_ the time of the service client instantiation. However, since this is // the existing behavior with v2 endpoint file format, we will preserve this behavior as is. if (!string.IsNullOrEmpty(AWSConfigs.EndpointDefinition)) { _regionEndpointProvider = new RegionEndpointProviderV2(); } else { _regionEndpointProvider = new RegionEndpointProviderV3(); } } return _regionEndpointProvider; } } #endregion private RegionEndpoint(string systemName, string displayName) { this.SystemName = systemName; this.OriginalSystemName = systemName; this.DisplayName = displayName; } /// /// Gets the system name of a region. /// public string SystemName { get; private set; } [Obsolete("It should not be necessary to use this property. To support upgrading to Endpoint Variants, " + "ClientConfig will manipulate the assigned RegionEndpoint. To support the Polly PreSigner, it's still necessary" + "to check the OriginalSystemName to determine if a PseudoRegion was assigned.",error: false)] public string OriginalSystemName { get; internal set; } /// /// Gets the display name of a region. /// public string DisplayName { get; private set; } /// /// Gets the partition name the region is in. For example for us-east-1 the partition name is aws. For cn-northwest-1 the partition name is aws-cn. /// public string PartitionName { get { var regionEndpointV3 = this.InternedRegionEndpoint as RegionEndpointV3; return regionEndpointV3?.PartitionName; } } /// /// Gets the dns suffix for the region endpoints in a partition. For example the aws partition's suffix is amazonaws.com. The aws-cn partition's suffix is amazonaws.com.cn. /// public string PartitionDnsSuffix { get { var regionEndpointV3 = this.InternedRegionEndpoint as RegionEndpointV3; return regionEndpointV3?.PartitionDnsSuffix; } } private IRegionEndpoint InternedRegionEndpoint { get { return RegionEndpointProvider.GetRegionEndpoint(SystemName); } } /// /// Gets the endpoint for a service in a region. /// /// /// The services system name. Service system names can be obtained from the /// RegionEndpointServiceName member of the ClientConfig-derived class for the service. /// /// /// For forwards compatibility, if the service being requested for isn't known in the region, this method /// will generate an endpoint using the AWS endpoint heuristics. In this case, it is not guaranteed the /// endpoint will point to a valid service endpoint. /// /// [Obsolete("This operation is obsoleted because as of version 3.7.100 endpoint is resolved using a newer system that uses request level parameters to resolve the endpoint.")] public Endpoint GetEndpointForService(string serviceName) { return GetEndpointForService(serviceName, new GetEndpointForServiceOptions()); } /// /// Gets the endpoint for a service in a region, optionally selecting a dualstack compatible endpoint. /// /// /// The services system name. Service system names can be obtained from the /// RegionEndpointServiceName member of the ClientConfig-derived class for the service. /// /// /// If true a dualstack endpoint is returned. It is the user's responsibility to verify that the given service /// supports a dualstack endpoint for the region. /// /// /// For forwards compatibility, if the service being requested for isn't known in the region, this method /// will generate an endpoint using the AWS endpoint heuristics. In this case, it is not guaranteed the /// endpoint will point to a valid service endpoint. /// [Obsolete("Use GetEndpointForService(string serviceName, GetEndpointForServiceOptions options) instead", error: false)] public Endpoint GetEndpointForService(string serviceName, bool dualStack) { return GetEndpointForService(serviceName, new GetEndpointForServiceOptions {DualStack = dualStack}); } /// /// Gets the endpoint for a service in a region. /// /// For forwards compatibility, if the service being requested for isn't known in the region, this method /// will generate an endpoint using the AWS endpoint heuristics. In this case, it is not guaranteed the /// endpoint will point to a valid service endpoint. /// /// /// The services system name. Service system names can be obtained from the /// RegionEndpointServiceName member of the ClientConfig-derived class for the service. /// /// /// Specify additional requirements on the to be returned. /// [Obsolete("This operation is obsoleted because as of version 3.7.100 endpoint is resolved using a newer system that uses request level parameters to resolve the endpoint.")] public Endpoint GetEndpointForService(string serviceName, GetEndpointForServiceOptions options) { return InternedRegionEndpoint.GetEndpointForService(serviceName, options); } public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", this.DisplayName, this.SystemName); } /// /// This class defines an endpoints hostname and which protocols it supports. /// [Obsolete("This class is obsoleted because as of version 3.7.100 endpoint is resolved using a newer system that uses request level parameters to resolve the endpoint.")] public class Endpoint { internal Endpoint(string hostname, string authregion, string signatureVersionOverride, string dnsSuffix, bool deprecated) { this.Hostname = hostname; this.AuthRegion = authregion; this.SignatureVersionOverride = signatureVersionOverride; this.Deprecated = deprecated; this.DnsSuffix = dnsSuffix; } /// /// Gets the hostname for the service. /// public string Hostname { get; private set; } /// /// Gets the DNS suffix for the service. /// public string DnsSuffix { get; private set; } /// /// The authentication region to be used in request signing. /// public string AuthRegion { get; private set; } public override string ToString() { return this.Hostname; } /// /// This property is only set for S3 endpoints. For all other services this property returns null. /// For S3 endpoints, if the endpoint supports signature version 2 this property will be "2", otherwise it will be "4". /// public string SignatureVersionOverride { get; private set; } /// /// Gets the hostname for the service. /// public bool Deprecated { get; private set; } } } }