public static void ProcessRequestHandlers(IExecutionContext executionContext) { var request = executionContext.RequestContext.Request; var config = executionContext.RequestContext.ClientConfig; // If the marshalled request has the SSE header and it is set to KMS, // force SigV4 for this request. // Current operations that may set this header: // CopyObject, CopyPart, InitiateMultipart, PutObject string sseHeaderValue; if (request.Headers.TryGetValue(HeaderKeys.XAmzServerSideEncryptionHeader, out sseHeaderValue) && string.Equals(sseHeaderValue, ServerSideEncryptionMethod.AWSKMS.Value, StringComparison.Ordinal)) { request.UseSigV4 = true; } var bucketResourcePathToken = GetBucketName(request.ResourcePath); if (string.IsNullOrEmpty(bucketResourcePathToken)) { return; } var s3Config = config as AmazonS3Config; if (s3Config == null) { throw new AmazonClientException("Current config object is not of type AmazonS3Config"); } //If a ServiceURL is set the config ClientRegion should be null. Under this case //the region needs to be determined from the ServiceURL. var regionEndpoint = config.RegionEndpoint; if (regionEndpoint == null && !string.IsNullOrEmpty(config.ServiceURL)) { var regionName = AWSSDKUtils.DetermineRegion(config.ServiceURL); regionEndpoint = RegionEndpoint.GetBySystemName(regionName); } bool isHttp; bool removeBucketFromResourcePath = false; if (Arn.IsArn(bucketResourcePathToken)) { string accessPoint; Arn s3Arn = Arn.Parse(bucketResourcePathToken); if (s3Arn.IsService("s3") && s3Arn.TryParseAccessPoint(out accessPoint)) { ValidateS3AccessPoint(s3Arn, s3Config, regionEndpoint); UriBuilder ub; if (!string.IsNullOrEmpty(config.ServiceURL)) { ub = new UriBuilder(EndpointResolver.DetermineEndpoint(s3Config, request)); isHttp = string.Equals(ub.Scheme, "http", StringComparison.OrdinalIgnoreCase); ub.Host = string.Concat($"{accessPoint}-{s3Arn.AccountId}.", ub.Host); } else { isHttp = config.UseHttp; var scheme = isHttp ? "http" : "https"; ub = new UriBuilder($"{scheme}://{accessPoint}-{s3Arn.AccountId}.s3-accesspoint{(config.UseDualstackEndpoint ? ".dualstack" : "")}.{s3Arn.Region}.{config.RegionEndpoint.PartitionDnsSuffix}"); } request.Endpoint = ub.Uri; } else if (s3Arn.IsService(s3ObjectLambdaServiceName) && s3Arn.TryParseAccessPoint(out accessPoint)) { ValidateS3ObjectLambdaAccessPoint(s3Arn, s3Config, regionEndpoint); UriBuilder ub; if (!string.IsNullOrEmpty(config.ServiceURL)) { ub = new UriBuilder(EndpointResolver.DetermineEndpoint(s3Config, request)); isHttp = string.Equals(ub.Scheme, "http", StringComparison.OrdinalIgnoreCase); ub.Host = string.Concat($"{accessPoint}-{s3Arn.AccountId}.", ub.Host); } else { isHttp = s3Config.UseHttp; var scheme = isHttp ? "http" : "https"; ub = new UriBuilder($"{scheme}://{accessPoint}-{s3Arn.AccountId}.{s3ObjectLambdaServiceName}.{s3Arn.Region}.{config.RegionEndpoint.PartitionDnsSuffix}"); } request.Endpoint = ub.Uri; } else if (s3Arn.IsOutpostArn()) { var outpost = s3Arn.ParseOutpost(); ValidateOutpostAccessPoint(s3Arn, s3Config, regionEndpoint); var region = s3Config.UseArnRegion ? s3Arn.Region : regionEndpoint.SystemName; bucketResourcePathToken = outpost.FullAccessPointName; UriBuilder ub; if (!string.IsNullOrEmpty(config.ServiceURL)) { ub = new UriBuilder(EndpointResolver.DetermineEndpoint(s3Config, request)); isHttp = string.Equals(ub.Scheme, "http", StringComparison.OrdinalIgnoreCase); ub.Host = string.Concat($"{outpost.AccessPointName}-{s3Arn.AccountId}.{outpost.OutpostId}.", ub.Host); } else { isHttp = config.UseHttp; var scheme = isHttp ? "http" : "https"; ub = new UriBuilder($"{scheme}://{outpost.AccessPointName}-{s3Arn.AccountId}.{outpost.OutpostId}.s3-outposts.{region}.{config.RegionEndpoint.PartitionDnsSuffix}"); } request.Endpoint = ub.Uri; } else { throw new AmazonClientException("Invalid ARN specified for bucket name. Only access point ARNs are allowed for the value of bucket name."); } request.OverrideSigningServiceName = s3Arn.Service; // The access point arn can be using a region different from the configured region for the service client. // If so be sure to set the authentication region so the signer will use the correct region. request.AuthenticationRegion = s3Arn.Region; request.UseSigV4 = true; removeBucketFromResourcePath = true; } else { // If path style is not forced and the bucket name is DNS // compatible modify the endpoint to use virtual host style // addressing var bucketIsDnsCompatible = IsDnsCompatibleBucketName(bucketResourcePathToken); var ub = new UriBuilder(EndpointResolver.DetermineEndpoint(s3Config, request)); isHttp = string.Equals(ub.Scheme, "http", StringComparison.OrdinalIgnoreCase); if (!s3Config.ForcePathStyle && bucketIsDnsCompatible) { // If using HTTPS, bucketName cannot contain a period if (isHttp || bucketResourcePathToken.IndexOf('.') < 0) { // Add bucket to host ub.Host = string.Concat(bucketResourcePathToken, ".", ub.Host); request.Endpoint = ub.Uri; removeBucketFromResourcePath = true; } } if (request.OriginalRequest.GetType() == typeof(WriteGetObjectResponseRequest)) { if (!string.IsNullOrEmpty(config.ServiceURL)) { ub = new UriBuilder(EndpointResolver.DetermineEndpoint(s3Config, request)); isHttp = string.Equals(ub.Scheme, "http", StringComparison.OrdinalIgnoreCase); } else { isHttp = s3Config.UseHttp; var scheme = isHttp ? "http" : "https"; var region = regionEndpoint.SystemName == "us-east-1-regional" ? "us-east-1" : regionEndpoint.SystemName; ub = new UriBuilder($"{scheme}://{request.Headers["x-amz-request-route"]}.{s3ObjectLambdaServiceName}.{region}.{config.RegionEndpoint.PartitionDnsSuffix}"); } request.Endpoint = ub.Uri; request.OverrideSigningServiceName = s3ObjectLambdaServiceName; request.UseSigV4 = true; } if (s3Config.UseAccelerateEndpoint) { // Validate if bucket name is accelerate compatible and enable acceleration by using // Accelerate endpoint for this request if (!bucketIsDnsCompatible || BucketNameContainsPeriod(bucketResourcePathToken)) { throw new AmazonClientException( @"S3 accelerate is enabled for this request but the bucket name is not accelerate compatible." + " The bucket name must be DNS compatible (http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html)" + " and must not contain any period (.) characters to be accelerate compatible."); } var originalRequest = request.OriginalRequest; bool accelerateSupportedApi = !UnsupportedAccelerateRequestTypes.Contains(originalRequest.GetType()); // Skip requests which are not supported if (accelerateSupportedApi) { request.Endpoint = GetAccelerateEndpoint(bucketResourcePathToken, s3Config); if (request.UseSigV4 && s3Config.RegionEndpoint != null) { request.AlternateEndpoint = s3Config.RegionEndpoint; } } } } if (removeBucketFromResourcePath) { // Remove bucket from resource path but retain in canonical resource // prefix, so it gets included when we sign the request later var resourcePath = request.ResourcePath; var canonicalBucketName = string.Concat("/", bucketResourcePathToken); if (resourcePath.IndexOf(canonicalBucketName, StringComparison.Ordinal) == 0) { resourcePath = resourcePath.Substring(canonicalBucketName.Length); } request.ResourcePath = resourcePath; request.CanonicalResourcePrefix = canonicalBucketName; } // Some parameters should not be sent over HTTP, just HTTPS if (isHttp) { ValidateHttpsOnlyHeaders(request); } }