/*
* Copyright 2010-2013 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 Amazon.Runtime;
using System;
using System.Globalization;
#if BCL || NETSTANDARD
using Amazon.Runtime.CredentialManagement;
#endif
namespace Amazon.S3
{
///
/// Configuration for accessing AmazonS3 service
///
public partial class AmazonS3Config : ClientConfig
{
private const string UseArnRegionEnvName = "AWS_S3_USE_ARN_REGION";
private const string AccelerateEndpointSuffix = "s3-accelerate.amazonaws.com";
private const string AccelerateDualstackEndpointSuffix = "s3-accelerate.dualstack.amazonaws.com";
private const string AwsProfileEnvironmentVariable = "AWS_PROFILE";
private const string DefaultProfileName = "default";
private const string AwsS3UsEast1RegionalEndpointsEnvironmentVariable = "AWS_S3_US_EAST_1_REGIONAL_ENDPOINT";
private bool forcePathStyle = false;
private bool useAccelerateEndpoint = false;
private S3UsEast1RegionalEndpointValue? s3UsEast1RegionalEndpointValue;
private readonly RegionEndpoint legacyUSEast1GlobalRegion = RegionEndpoint.USEast1;
#if BCL || NETSTANDARD
private static CredentialProfileStoreChain credentialProfileChain = new CredentialProfileStoreChain();
#endif
///
/// When true, requests will always use path style addressing.
///
public bool ForcePathStyle
{
get { return forcePathStyle; }
set { forcePathStyle = value; }
}
///
/// Enables S3 accelerate by sending requests to the accelerate endpoint instead of the regular region endpoint.
/// To use this feature, the bucket name should be DNS compliant names and should not contain periods (.).
/// The following APIs are not supported and are sent to the regular region endpoint, even if this option is enabled:
///
/// - PutBucket
/// - ListBuckets
/// - DeleteBucket
///
///
///
/// This option cannot be used at the same time as UseDualstackEndpoint.
///
public bool UseAccelerateEndpoint
{
get { return useAccelerateEndpoint; }
set { useAccelerateEndpoint = value; }
}
bool? _useArnRegion;
///
/// If set to true and the service package supports it the region identified in the arn for a resource
/// will be used when making the service request.
///
public bool UseArnRegion
{
get
{
if (!this._useArnRegion.HasValue)
{
#if BCL || NETSTANDARD
var profileName = Environment.GetEnvironmentVariable(AwsProfileEnvironmentVariable) ?? DefaultProfileName;
if (credentialProfileChain.TryGetProfile(profileName, out var profile))
{
this._useArnRegion = profile.S3UseArnRegion;
}
if (!this._useArnRegion.HasValue && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(UseArnRegionEnvName)))
{
if (bool.TryParse(Environment.GetEnvironmentVariable(UseArnRegionEnvName), out var value))
{
this._useArnRegion = value;
}
}
#endif
if (!this._useArnRegion.HasValue)
{
// To maintain consistency with buckets default UseArnRegion to true when client configured for us-east-1.
this._useArnRegion = this.RegionEndpoint == RegionEndpoint.USEast1;
}
}
return this._useArnRegion.GetValueOrDefault();
}
set { this._useArnRegion = value; }
}
///
/// USEast1RegionalEndpointValue determines wheter or not
/// to send the us-east-1 s3 requests to the regional endpoint or to
/// the legacy global endpoint.
/// This flags takes precedence over the AWS_S3_US_EAST_1_REGIONAL_ENDPOINT
/// environment variable and the credential file.
///
public S3UsEast1RegionalEndpointValue? USEast1RegionalEndpointValue
{
get
{
if (s3UsEast1RegionalEndpointValue == null)
{
s3UsEast1RegionalEndpointValue = CheckS3EnvironmentVariable() ?? CheckCredentialsFile() ?? S3UsEast1RegionalEndpointValue.Legacy;
}
return s3UsEast1RegionalEndpointValue;
}
set { s3UsEast1RegionalEndpointValue = value; }
}
///
/// This method contains custom initializations for the config object.
///
protected override void Initialize()
{
this.AllowAutoRedirect = false;
#if BCL45 || NETSTANDARD
// Set Timeout and ReadWriteTimeout for S3 to max timeout as per-request
// timeouts are not supported.
this.Timeout = ClientConfig.MaxTimeout;
this.ReadWriteTimeout = ClientConfig.MaxTimeout;
#elif PCL
// Only Timeout property is supported for WinRT and Windows Phone.
// Set Timeout for S3 to max timeout as per-request
// timeouts are not supported.
this.Timeout = ClientConfig.MaxTimeout;
#endif
}
///
/// Given this client configuration, returns the service url
///
/// The service url in the form of a string
public override string DetermineServiceURL()
{
if (this.ServiceURL != null)
{
return this.ServiceURL;
}
var actual = this.RegionEndpoint;
if (actual == legacyUSEast1GlobalRegion && !UseAccelerateEndpoint && !UseDualstackEndpoint
&& USEast1RegionalEndpointValue == S3UsEast1RegionalEndpointValue.Regional)
{
actual = RegionEndpoint.GetBySystemName("us-east-1-regional");
}
return GetUrl(actual, this.RegionEndpointServiceName, this.UseHttp, this.UseDualstackEndpoint);
}
///
/// If the client is configured to hit us-east-1 with the S3UsEast1RegionalEndpointValue flag not set,
/// this method checks whether the environment variable is present or the credential file contains a valid value
///
/// A Nullable of S3UsEast1RegionalEndpointValue representing the client configuration for the regional us-east-1 endpoint
private S3UsEast1RegionalEndpointValue? GetEndpointFlagValueForUsEast1Regional()
{
if (this.USEast1RegionalEndpointValue.HasValue)
{
return this.USEast1RegionalEndpointValue;
}
else
{
// Environment variable takes precedence over the credential file
return CheckS3EnvironmentVariable() ?? CheckCredentialsFile();
}
}
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;
}
///
/// Validate that the config object is properly configured.
///
public override void Validate()
{
base.Validate();
if (this.ForcePathStyle && this.UseAccelerateEndpoint)
{
throw new AmazonClientException(
@"S3 accelerate is not compatible with Path style requests. Disable Path style requests" +
" using AmazonS3Config.ForcePathStyle property to use S3 accelerate.");
}
var isExplicitAccelerateEndpoint = !string.IsNullOrEmpty(this.ServiceURL) &&
(this.ServiceURL.IndexOf(AccelerateEndpointSuffix, StringComparison.OrdinalIgnoreCase) >= 0 ||
this.ServiceURL.IndexOf(AccelerateDualstackEndpointSuffix, StringComparison.OrdinalIgnoreCase) >= 0);
if (isExplicitAccelerateEndpoint)
{
if (this.RegionEndpoint == null && string.IsNullOrEmpty(this.AuthenticationRegion))
{
throw new AmazonClientException(
@"Specify a region using AmazonS3Config.RegionEndpoint or AmazonS3Config.AuthenticationRegion" +
" to use S3 accelerate.");
}
else
{
if (this.RegionEndpoint == null && !string.IsNullOrEmpty(this.AuthenticationRegion))
{
this.RegionEndpoint = RegionEndpoint.GetBySystemName(this.AuthenticationRegion);
}
this.UseAccelerateEndpoint = true;
}
}
}
///
/// Checks the AWS_S3_US_EAST_1_REGIONAL_ENDPOINT environment variable for the presence of the s3 regional flag
///
/// A nullable of S3UsEast1RegionalEndpointValue
private static S3UsEast1RegionalEndpointValue? CheckS3EnvironmentVariable()
{
#if BCL || NETSTANDARD
string s3RegionalFlag = Environment.GetEnvironmentVariable(AwsS3UsEast1RegionalEndpointsEnvironmentVariable);
if (!string.IsNullOrEmpty(s3RegionalFlag))
{
#if BCL35
S3UsEast1RegionalEndpointValue? s3RegionalFlagValue = null;
try
{
s3RegionalFlagValue = (S3UsEast1RegionalEndpointValue)Enum.Parse(typeof(S3UsEast1RegionalEndpointValue), s3RegionalFlag, true);
}
catch (Exception)
{
throw new InvalidOperationException("Invalid value for AWS_S3_US_EAST_1_REGIONAL_ENDPOINT environment variable. A string regional/legacy is expected.");
}
#else
if (!Enum.TryParse(s3RegionalFlag, true, out var s3RegionalFlagValue))
{
throw new InvalidOperationException("Invalid value for AWS_S3_US_EAST_1_REGIONAL_ENDPOINT variable. A string regional/legacy is expected.");
}
#endif
return s3RegionalFlagValue;
}
#endif
return null;
}
///
/// Checks the credential file for the presence of the s3 regional flag
///
/// A nullable of S3UsEast1RegionalEndpointValue
private static S3UsEast1RegionalEndpointValue? CheckCredentialsFile()
{
#if BCL || NETSTANDARD
CredentialProfile profile;
var profileName = Environment.GetEnvironmentVariable(AwsProfileEnvironmentVariable) ?? DefaultProfileName;
credentialProfileChain.TryGetProfile(profileName, out profile);
return profile?.S3RegionalEndpoint;
#else
return null;
#endif
}
internal string AccelerateEndpoint
{
get
{
return this.UseDualstackEndpoint ? AccelerateDualstackEndpointSuffix : AccelerateEndpointSuffix;
}
}
}
}