/* * Copyright 2010-2017 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. */ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Net; using System.Text; using Amazon.Runtime.Internal.Auth; using Amazon.Util; using System.Globalization; #if NETSTANDARD using System.Runtime.InteropServices; #endif namespace Amazon.Runtime { /// /// This class is the base class of all the configurations settings to connect /// to a service. /// public abstract partial class ClientConfig : IClientConfig { // Represents infinite timeout. http://msdn.microsoft.com/en-us/library/system.threading.timeout.infinite.aspx internal static readonly TimeSpan InfiniteTimeout = TimeSpan.FromMilliseconds(-1); // Represents max timeout. public static readonly TimeSpan MaxTimeout = TimeSpan.FromMilliseconds(int.MaxValue); private RegionEndpoint regionEndpoint = null; private bool probeForRegionEndpoint = true; private bool throttleRetries = true; private bool useHttp = false; private string serviceURL = null; private string authRegion = null; private string authServiceName = null; private string signatureVersion = "4"; private SigningAlgorithm signatureMethod = SigningAlgorithm.HmacSHA256; private int maxErrorRetry = 4; private bool readEntireResponse = false; private bool logResponse = false; private int bufferSize = AWSSDKUtils.DefaultBufferSize; private long progressUpdateInterval = AWSSDKUtils.DefaultProgressUpdateInterval; private bool resignRetries = false; private ICredentials proxyCredentials; private bool logMetrics = AWSConfigs.LoggingConfig.LogMetrics; private bool disableLogging = false; private TimeSpan? timeout = null; private bool allowAutoRedirect = true; private bool useDualstackEndpoint = false; private TimeSpan? readWriteTimeout = null; private bool disableHostPrefixInjection = false; private bool? endpointDiscoveryEnabled = null; private int endpointDiscoveryCacheLimit = 1000; #if BCL private readonly TcpKeepAlive tcpKeepAlive = new TcpKeepAlive(); #endif /// /// Gets Service Version /// public abstract string ServiceVersion { get; } /// /// Gets and sets of the signatureMethod property. /// public SigningAlgorithm SignatureMethod { get { return this.signatureMethod; } set { this.signatureMethod = value; } } /// /// Gets and sets of the SignatureVersion property. /// /// Note: This property exists for backward compatibility but is no longer /// used by any service other than S3. /// public string SignatureVersion { get { return this.signatureVersion; } set { this.signatureVersion = value; } } /// /// Gets and sets of the UserAgent property. /// public abstract string UserAgent { get; } /// /// Gets and sets the RegionEndpoint property. The region constant that /// determines the endpoint to use. /// /// Setting this property to null will force the SDK to recalculate the /// RegionEndpoint value based on App/WebConfig, environment variables, /// profile, etc. /// public RegionEndpoint RegionEndpoint { get { #if BCL || NETSTANDARD if (probeForRegionEndpoint) { RegionEndpoint = GetDefaultRegionEndpoint(); this.probeForRegionEndpoint = false; } #endif return this.regionEndpoint; } set { this.serviceURL = null; this.regionEndpoint = value; this.probeForRegionEndpoint = this.regionEndpoint == null; } } /// /// The constant used to lookup in the region hash the endpoint. /// public abstract string RegionEndpointServiceName { get; } /// /// Gets and sets of the ServiceURL property. /// This is an optional property; change it /// only if you want to try a different service /// endpoint. /// public string ServiceURL { get { return this.serviceURL; } set { this.regionEndpoint = null; this.probeForRegionEndpoint = false; this.serviceURL = value; } } /// /// Gets and sets the UseHttp. /// If this property is set to true, the client attempts /// to use HTTP protocol, if the target endpoint supports it. /// By default, this property is set to false. /// public bool UseHttp { get { return this.useHttp; } set { this.useHttp = value; } } /// /// Given this client configuration, return a string form ofthe service endpoint url. /// public virtual string DetermineServiceURL() { string url; if (this.ServiceURL != null) { url = this.ServiceURL; } else { url = GetUrl(this.RegionEndpoint, this.RegionEndpointServiceName, this.UseHttp, this.UseDualstackEndpoint); } return url; } internal static string GetUrl(RegionEndpoint regionEndpoint, string regionEndpointServiceName, bool useHttp, bool useDualStack) { var endpoint = regionEndpoint.GetEndpointForService(regionEndpointServiceName, useDualStack); string url = new Uri(string.Format(CultureInfo.InvariantCulture, "{0}{1}", useHttp ? "http://" : "https://", endpoint.Hostname)).AbsoluteUri; return url; } /// /// Gets and sets the AuthenticationRegion property. /// Used in AWS4 request signing, this is an optional property; /// change it only if the region cannot be determined from the /// service endpoint. /// public string AuthenticationRegion { get { return this.authRegion; } set { this.authRegion = value; } } /// /// Gets and sets the AuthenticationServiceName property. /// Used in AWS4 request signing, this is the short-form /// name of the service being called. /// public string AuthenticationServiceName { get { return this.authServiceName; } set { this.authServiceName = value; } } /// /// Gets and sets of the MaxErrorRetry property. /// public int MaxErrorRetry { get { return this.maxErrorRetry; } set { this.maxErrorRetry = value; } } /// /// Gets and sets the LogResponse property. /// If this property is set to true, the service response is logged. /// The size of response being logged is controlled by the AWSConfigs.LoggingConfig.LogResponsesSizeLimit property. /// public bool LogResponse { get { return this.logResponse; } set { this.logResponse = value; } } /// /// Gets and sets the ReadEntireResponse property. /// NOTE: This property does not effect response processing and is deprecated. /// To enable response logging, the ClientConfig.LogResponse and AWSConfigs.LoggingConfig /// properties can be used. /// [Obsolete("This property does not effect response processing and is deprecated." + "To enable response logging, the ClientConfig.LogResponse and AWSConfigs.LoggingConfig.LogResponses properties can be used.")] public bool ReadEntireResponse { get { return this.readEntireResponse; } set { this.readEntireResponse = value; } } /// /// Gets and Sets the BufferSize property. /// The BufferSize controls the buffer used to read in from input streams and write /// out to the request. /// public int BufferSize { get { return this.bufferSize; } set { this.bufferSize = value; } } /// /// /// Gets or sets the interval at which progress update events are raised /// for upload operations. By default, the progress update events are /// raised at every 100KB of data transferred. /// /// /// If the value of this property is set less than ClientConfig.BufferSize, /// progress updates events will be raised at the interval specified by ClientConfig.BufferSize. /// /// public long ProgressUpdateInterval { get { return progressUpdateInterval; } set { progressUpdateInterval = value; } } /// /// Flag on whether to resign requests on retry or not. /// For Amazon S3 and Amazon Glacier this value will always be set to true. /// public bool ResignRetries { get { return this.resignRetries; } set { this.resignRetries = value; } } /// /// This flag controls if .NET HTTP infrastructure should follow redirection /// responses (e.g. HTTP 307 - temporary redirect). /// public bool AllowAutoRedirect { get { return this.allowAutoRedirect; } set { this.allowAutoRedirect = value; } } /// /// Flag on whether to log metrics for service calls. /// /// This can be set in the application's configs, as below: /// /// <?xml version="1.0" encoding="utf-8" ?> /// <configuration> /// <appSettings> /// <add key="AWSLogMetrics" value"true"/> /// </appSettings> /// </configuration> /// /// public bool LogMetrics { get { return this.logMetrics; } set { this.logMetrics = value; } } /// /// Gets and sets the DisableLogging. If true logging for this client will be disabled. /// public bool DisableLogging { get { return this.disableLogging; } set { this.disableLogging = value; } } /// /// Credentials to use with a proxy. /// public ICredentials ProxyCredentials { get { if(this.proxyCredentials == null && (!string.IsNullOrEmpty(AWSConfigs.ProxyConfig.Username) || !string.IsNullOrEmpty(AWSConfigs.ProxyConfig.Password))) { return new NetworkCredential(AWSConfigs.ProxyConfig.Username, AWSConfigs.ProxyConfig.Password ?? string.Empty); } return this.proxyCredentials; } set { this.proxyCredentials = value; } } #if BCL /// /// Specifies the TCP keep-alive values to use for service requests. /// public TcpKeepAlive TcpKeepAlive { get { return this.tcpKeepAlive; } } #endif #region Constructor public ClientConfig() { Initialize(); } #endregion protected virtual void Initialize() { } #if UNITY /// /// Overrides the default request timeout value. /// On Unity platform this value is not used as Unity HTTP client does not support timeouts. /// #endif /// /// /// If the value is set, the value is assigned to the Timeout property of the HttpWebRequest/HttpClient object used /// to send requests. /// /// /// Please specify a timeout value only if the operation will not complete within the default intervals /// specified for an HttpWebRequest/HttpClient. /// /// /// The timeout specified is null. /// The timeout specified is less than or equal to zero and is not Infinite. /// /// public TimeSpan? Timeout { get { return this.timeout; } set { ValidateTimeout(value); this.timeout = value; } } /// /// Configures the endpoint calculation for a service to go to a dual stack (ipv6 enabled) endpoint /// for the configured region. /// /// /// Note: AWS services are enabling dualstack endpoints over time. It is your responsibility to check /// that the service actually supports a dualstack endpoint in the configured region before enabling /// this option for a service. /// public bool UseDualstackEndpoint { get { return useDualstackEndpoint; } set { useDualstackEndpoint = value; } } /// /// Enable or disable the Retry Throttling feature by setting the ThrottleRetries flag to True/False resepctively. /// Retry Throttling is a feature that intelligently throttles retry attempts when a large precentage of requests /// are failing and retries are unsuccessful as well. In such situations the allotted retry capacity for the service URL /// will be drained until requests start to succeed again. Once the requisite capacity is available, retries would /// be permitted again. When retries are throttled, the service enters a fail-fast behaviour as the traditional retry attempt /// for the request would be circumvented. Hence, errors will resurface quickly. This will result in a greated number of exceptions /// but prevents requests being tied up in unsuccessful retry attempts. /// Note: Retry Throttling is enabled by default. Set the ThrottleRetries flag to false to switch off this feature. /// public bool ThrottleRetries { get { return throttleRetries; } set { throttleRetries = value; } } /// /// Enable or disable the Nagle algorithm on the underlying http /// client. /// /// This method is not intended to be called by consumers of the AWS SDK for .NET /// /// public void SetUseNagleIfAvailable(bool useNagle) { #if BCL this.UseNagleAlgorithm = useNagle; #endif } /// /// Performs validation on this config object. /// Throws exception if any of the required values are missing/invalid. /// public virtual void Validate() { if (RegionEndpoint == null && string.IsNullOrEmpty(this.ServiceURL)) throw new AmazonClientException("No RegionEndpoint or ServiceURL configured"); #if BCL if (TcpKeepAlive.Enabled) { ValidateTcpKeepAliveTimeSpan(TcpKeepAlive.Timeout, "TcpKeepAlive.Timeout"); ValidateTcpKeepAliveTimeSpan(TcpKeepAlive.Interval, "TcpKeepAlive.Interval"); } #endif } /// /// Returns the current UTC now after clock correction for this endpoint. /// [Obsolete("Please use CorrectClockSkew.GetCorrectedUtcNowForEndpoint(string endpoint) instead.", false)] public DateTime CorrectedUtcNow { get { return CorrectClockSkew.GetCorrectedUtcNowForEndpoint(DetermineServiceURL()); } } /// /// The calculated clock skew correction for a specific endpoint, if there is one. /// This field will be set if a service call resulted in an exception /// and the SDK has determined that there is a difference between local /// and server times. /// /// If is set to true, this /// value will still be set to the correction, but it will not be used by the /// SDK and clock skew errors will not be retried. /// public TimeSpan ClockOffset { get { if (AWSConfigs.ManualClockCorrection.HasValue) { return AWSConfigs.ManualClockCorrection.Value; } else { string endpoint = DetermineServiceURL(); return CorrectClockSkew.GetClockCorrectionForEndpoint(endpoint); } } } /// /// Gets and sets the DisableHostPrefixInjection flag. If true, host prefix injection will be disabled for this client, the default value of this flag is false. /// Host prefix injection prefixes the service endpoint with request members from APIs which use this feature. /// Example: for a hostPrefix of "foo-name." and a endpoint of "service.region.amazonaws.com" the default behavior is to /// prefix the endpoint with the hostPrefix resulting in a final endpoint of "foo-name.service.region.amazonaws.com". Setting /// DisableHostPrefixInjection to true will disable hostPrefix injection resulting in a final endpoint of /// "service.region.amazonaws.com" regardless of the value of hostPrefix. E.g. You may want to disable host prefix injection for testing against a local mock endpoint. /// public bool DisableHostPrefixInjection { get { return this.disableHostPrefixInjection; } set { this.disableHostPrefixInjection = value; } } /// /// Returns the flag indicating if endpoint discovery should be enabled or disabled for operations that are not required to use endpoint discovery. /// public bool EndpointDiscoveryEnabled { get { if (!this.endpointDiscoveryEnabled.HasValue) { var endpointDiscoveryEnabled = FallbackEndpointDiscoveryEnabledFactory.GetEnabled(); if (endpointDiscoveryEnabled != null) { return endpointDiscoveryEnabled.Value; } else { return false; } } return this.endpointDiscoveryEnabled.Value; } set { this.endpointDiscoveryEnabled = value; } } /// /// Returns the maximum number of discovered endpoints that can be stored within the cache for the client. The default limit is 1000 cache entries. /// public int EndpointDiscoveryCacheLimit { get { return this.endpointDiscoveryCacheLimit; } set { this.endpointDiscoveryCacheLimit = value; } } /// /// Throw an exception if the boxed TimeSpan parameter doesn't have a value or is out of range. /// public static void ValidateTimeout(TimeSpan? timeout) { if (!timeout.HasValue) { throw new ArgumentNullException("timeout"); } if (timeout != InfiniteTimeout && (timeout <= TimeSpan.Zero || timeout > MaxTimeout)) { throw new ArgumentOutOfRangeException("timeout"); } } #if BCL private static void ValidateTcpKeepAliveTimeSpan(TimeSpan? value, string paramName) { if (!value.HasValue) { throw new ArgumentNullException(paramName); } if (value > MaxTimeout || (int)value.Value.TotalMilliseconds <= 0) { throw new ArgumentOutOfRangeException(paramName); } } #endif /// /// Returns the request timeout value if its value is set, /// else returns client timeout value. /// public static TimeSpan? GetTimeoutValue(TimeSpan? clientTimeout, TimeSpan? requestTimeout) { return requestTimeout.HasValue ? requestTimeout : (clientTimeout.HasValue ? clientTimeout : null); } #if NETSTANDARD || PCL #if NETSTANDARD bool cacheHttpClient = true; #else bool cacheHttpClient = false; #endif /// /// /// This is a switch used for performance testing and is not intended for production applications /// to change. This switch may be removed in a future version of the SDK as the .NET Core platform matures. /// /// /// If true, the HttpClient is cached and reused for every request made by the service client /// and shared with other service clients. /// /// /// For the .NET Core platform this is default to true because the HttpClient manages the connection /// pool. /// /// public bool CacheHttpClient { get { return this.cacheHttpClient; } set { this.cacheHttpClient = value; } } int? _httpClientCacheSize; /// /// If CacheHttpClient is set to true then HttpClientCacheSize controls the number of HttpClients cached. /// /// On Windows the default value is 1 since the underlying native implementation does not have throttling constraints /// like the non Windows Curl based implementation. For non Windows based platforms the default is the value return from /// System.Environment.ProcessorCount. /// /// public int HttpClientCacheSize { get { if(this._httpClientCacheSize.HasValue) { return this._httpClientCacheSize.Value; } #if NETSTANDARD if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return 1; } return Environment.ProcessorCount; #else return 1; #endif } set { this._httpClientCacheSize = value; } } #endif /// /// Overrides the default read-write timeout value. /// On Unity platform, this value is not used as Unity HTTP client does not support timeouts, so /// /// /// /// If the value is set, the value is assigned to the ReadWriteTimeout property of the HttpWebRequest object used /// to send requests. /// /// The timeout specified is null. /// The timeout specified is less than or equal to zero and is not Infinite. /// /// public TimeSpan? ReadWriteTimeout { get { return this.readWriteTimeout; } set { ValidateTimeout(value); this.readWriteTimeout = value; } } } }