public Stream?MarshalRequest(PutBucketLockConfigurationRequest request, IConfig config) { request.SetQueryParameter(AmzParameters.ObjectLock, string.Empty); FastXmlWriter writer = new FastXmlWriter(128); writer.WriteStartElement("ObjectLockConfiguration", "http://s3.amazonaws.com/doc/2006-03-01/"); writer.WriteElement("ObjectLockEnabled", "Enabled"); if (request.LockMode != LockMode.Unknown) { writer.WriteStartElement("Rule"); writer.WriteStartElement("DefaultRetention"); writer.WriteElement("Mode", ValueHelper.EnumToString(request.LockMode)); if (request.LockRetainUntil.HasValue) { writer.WriteElement("Days", (request.LockRetainUntil.Value - DateTimeOffset.UtcNow).Days); } writer.WriteEndElement("DefaultRetention"); writer.WriteEndElement("Rule"); } writer.WriteEndElement("ObjectLockConfiguration"); return(new MemoryStream(writer.GetBytes())); }
public static void AppendHost <TReq>(StringBuilder sb, S3Config config, TReq request) where TReq : IRequest { string?bucketName = null; if (request is IHasBucketName bn) { bucketName = bn.BucketName; } Uri?endpoint = config.Endpoint; if (endpoint != null) { sb.Append(endpoint.Host); if (!endpoint.IsDefaultPort) { sb.Append(':').Append(endpoint.Port); } } else if (bucketName != null && config.NamingMode == NamingMode.VirtualHost) { sb.Append(bucketName).Append(".s3.").Append(ValueHelper.EnumToString(config.Region)).Append(".amazonaws.com"); } else { sb.Append("s3.").Append(ValueHelper.EnumToString(config.Region)).Append(".amazonaws.com"); } }
public static void SetHeader <T>(this IRequest request, string key, T value) where T : struct, Enum { if (value.Equals(default(T))) { return; } request.SetHeader(key, ValueHelper.EnumToString(value)); }
public Stream MarshalRequest(CreateBucketRequest request, IConfig config) { //We only enable object locking on creation. We can't disable it, so there is no "false" option if (request.EnableObjectLocking.HasValue && request.EnableObjectLocking.Value) { request.SetHeader(AmzHeaders.XAmzBucketObjectLockEnabled, request.EnableObjectLocking.Value ? "TRUE" : string.Empty); } //Hard-code the LocationConstraint to the region from the config FastXmlWriter writer = new FastXmlWriter(128); writer.WriteStartElement("CreateBucketConfiguration"); writer.WriteElement("LocationConstraint", ValueHelper.EnumToString(config.Region)); writer.WriteEndElement("CreateBucketConfiguration"); return(new MemoryStream(writer.GetBytes())); }
public Stream MarshalRequest(PutBucketRequest request) { request.AddHeader(AmzHeaders.XAmzBucketObjectLockEnabled, request.EnableObjectLocking); request.AddHeader(AmzHeaders.XAmzAcl, request.Acl); request.AddHeader(AmzHeaders.XAmzGrantRead, request.AclGrantRead); request.AddHeader(AmzHeaders.XAmzGrantReadAcp, request.AclGrantReadAcp); request.AddHeader(AmzHeaders.XAmzGrantWrite, request.AclGrantWrite); request.AddHeader(AmzHeaders.XAmzGrantWriteAcp, request.AclGrantWriteAcp); request.AddHeader(AmzHeaders.XAmzGrantFullControl, request.AclGrantFullControl); if (request.Region != AwsRegion.Unknown) { StringBuilder sb = new StringBuilder(); sb.Append("<CreateBucketConfiguration>"); sb.Append("<LocationConstraint>").Append(ValueHelper.EnumToString(request.Region)).Append("</LocationConstraint>"); sb.Append("</CreateBucketConfiguration>"); return(new MemoryStream(Encoding.UTF8.GetBytes(sb.ToString()))); } return(null); }
public byte[] CreateSigningKey(DateTimeOffset dateTime, string service) { _logger.LogTrace("Creating key for {Service}", service); //https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html //Documentation says the key is valid for 7 days, but tests shows that is not true. string date = dateTime.ToString(DateTimeFormats.Iso8601Date, DateTimeFormatInfo.InvariantInfo); byte[] accessKey = KeyHelper.UnprotectKey(_options.Value.Credentials.AccessKey, _protector); byte[] key = Encoding.UTF8.GetBytes(SigningConstants.Scheme).Concat(accessKey).ToArray(); byte[] hashDate = CryptoHelper.HmacSign(Encoding.UTF8.GetBytes(date), key); byte[] hashRegion = CryptoHelper.HmacSign(Encoding.UTF8.GetBytes(ValueHelper.EnumToString(_options.Value.Region)), hashDate); byte[] hashService = CryptoHelper.HmacSign(Encoding.UTF8.GetBytes(service), hashRegion); byte[] signingKey = CryptoHelper.HmacSign(Encoding.UTF8.GetBytes("aws4_request"), hashService); //Security: clear key material Array.Clear(key, 0, key.Length); _logger.LogDebug("Signing key created: {SigningKey}", signingKey); return(signingKey); }
public Stream MarshalRequest(PutBucketLifecycleConfigurationRequest request, IConfig config) { request.SetQueryParameter(AmzParameters.Lifecycle, string.Empty); FastXmlWriter writer = new FastXmlWriter(600); writer.WriteStartElement("LifecycleConfiguration", "http://s3.amazonaws.com/doc/2006-03-01/"); foreach (S3Rule rule in request.Rules) { writer.WriteStartElement("Rule"); if (rule.AbortIncompleteMultipartUploadDays.HasValue) { writer.WriteStartElement("AbortIncompleteMultipartUpload"); writer.WriteElement("DaysAfterInitiation", rule.AbortIncompleteMultipartUploadDays.Value); writer.WriteEndElement("AbortIncompleteMultipartUpload"); } if (rule.Expiration != null) { writer.WriteStartElement("Expiration"); if (rule.Expiration.ExpireOnDate.HasValue) { writer.WriteElement("Date", ValueHelper.DateToString(rule.Expiration.ExpireOnDate.Value, DateTimeFormat.Iso8601Date)); } if (rule.Expiration.ExpireAfterDays.HasValue) { writer.WriteElement("Days", rule.Expiration.ExpireAfterDays.Value); } if (rule.Expiration.ExpireObjectDeleteMarker.HasValue) { writer.WriteElement("ExpiredObjectDeleteMarker", rule.Expiration.ExpireObjectDeleteMarker.Value); } writer.WriteEndElement("Expiration"); } if (rule.Filter != null) { if (rule.Filter.Prefix != null) { writer.WriteElement("Prefix", rule.Filter.Prefix); } if (rule.Filter.Tag != null) { writer.WriteStartElement("Tag"); writer.WriteElement("Key", rule.Filter.Tag.Value.Key); writer.WriteElement("Value", rule.Filter.Tag.Value.Value); writer.WriteEndElement("Tag"); } foreach (S3AndCondition condition in rule.Filter.Conditions) { writer.WriteStartElement("And"); if (condition.Prefix != null) { writer.WriteElement("Prefix", condition.Prefix); } if (condition.Tag != null) { writer.WriteStartElement("Tag"); writer.WriteElement("Key", condition.Tag.Value.Key); writer.WriteElement("Value", condition.Tag.Value.Value); writer.WriteEndElement("Tag"); } writer.WriteEndElement("And"); } } if (rule.Id != null) { writer.WriteElement("ID", rule.Id); } if (rule.NonCurrentVersionExpirationDays != null) { writer.WriteStartElement("NoncurrentVersionExpiration"); writer.WriteElement("NoncurrentDays", rule.NonCurrentVersionExpirationDays.Value); writer.WriteEndElement("NoncurrentVersionExpiration"); } foreach (S3NonCurrentVersionTransition transition in rule.NonCurrentVersionTransitions) { writer.WriteStartElement("NoncurrentVersionTransition"); writer.WriteElement("NoncurrentDays", transition.NonCurrentDays.Value); writer.WriteElement("StorageClass", ValueHelper.EnumToString(transition.StorageClass)); writer.WriteEndElement("NoncurrentVersionTransition"); } writer.WriteElement("Status", rule.Enabled ? "Enabled" : "Disabled"); foreach (S3Transition transition in rule.Transitions) { writer.WriteStartElement("Transition"); if (transition.TransitionOnDate.HasValue) { writer.WriteElement("Date", ValueHelper.DateToString(transition.TransitionOnDate.Value, DateTimeFormat.Iso8601Date)); } if (transition.TransitionAfterDays.HasValue) { writer.WriteElement("Days", transition.TransitionAfterDays.Value); } if (transition.StorageClass != StorageClass.Unknown) { writer.WriteElement("StorageClass", ValueHelper.EnumToString(transition.StorageClass)); } writer.WriteEndElement("Transition"); } writer.WriteEndElement("Rule"); } writer.WriteEndElement("LifecycleConfiguration"); return(new MemoryStream(writer.GetBytes())); }
public Stream?MarshalRequest(RestoreObjectRequest request, SimpleS3Config config) { request.SetQueryParameter(AmzParameters.Restore, string.Empty); FastXmlWriter xml = new FastXmlWriter(512); xml.WriteStartElement("RestoreRequest", "http://s3.amazonaws.com/doc/2006-03-01/"); if (request.Days > 0) { xml.WriteElement("Days", request.Days); } if (request.RequestType != RestoreRequestType.Unknown) { xml.WriteElement("Type", ValueHelper.EnumToString(request.RequestType)); } if (request.RequestTier != RetrievalTier.Unknown) { xml.WriteElement("Tier", ValueHelper.EnumToString(request.RequestTier)); } if (request.GlacierTier != RetrievalTier.Unknown) { xml.WriteStartElement("GlacierJobParameters"); xml.WriteElement("Tier", ValueHelper.EnumToString(request.GlacierTier)); xml.WriteEndElement("GlacierJobParameters"); } if (request.Description != null) { xml.WriteElement("Description", request.Description); } if (request.SelectParameters != null) { xml.WriteStartElement("SelectParameters"); if (request.SelectParameters.InputFormat != null) { xml.WriteStartElement("InputSerialization"); if (request.SelectParameters.InputFormat.CompressionType != CompressionType.Unknown) { xml.WriteElement("CompressionType", ValueHelper.EnumToString(request.SelectParameters.InputFormat.CompressionType)); } switch (request.SelectParameters.InputFormat) { case S3CsvInputFormat csvInput: { xml.WriteStartElement("CSV"); if (csvInput.HeaderUsage != HeaderUsage.Unknown) { xml.WriteElement("FileHeaderInfo", ValueHelper.EnumToString(csvInput.HeaderUsage)); } if (csvInput.CommentCharacter != null) { xml.WriteElement("Comments", ConvertChar(csvInput.CommentCharacter)); } if (csvInput.QuoteEscapeCharacter != null) { xml.WriteElement("QuoteEscapeCharacter", ConvertChar(csvInput.QuoteEscapeCharacter)); } if (csvInput.RecordDelimiter != null) { xml.WriteElement("RecordDelimiter", ConvertChar(csvInput.RecordDelimiter)); } if (csvInput.FieldDelimiter != null) { xml.WriteElement("FieldDelimiter", ConvertChar(csvInput.FieldDelimiter)); } if (csvInput.QuoteCharacter != null) { xml.WriteElement("QuoteCharacter", ConvertChar(csvInput.QuoteCharacter)); } if (csvInput.AllowQuotedRecordDelimiter != null) { xml.WriteElement("AllowQuotedRecordDelimiter", csvInput.AllowQuotedRecordDelimiter); } xml.WriteEndElement("CSV"); break; } case S3JsonInputFormat jsonInput: { xml.WriteStartElement("JSON"); if (jsonInput.JsonType != JsonType.Unknown) { xml.WriteElement("Type", ValueHelper.EnumToString(jsonInput.JsonType)); } xml.WriteEndElement("JSON"); break; } case S3ParquetInputFormat _: xml.WriteElement("Parquet", string.Empty); break; } xml.WriteEndElement("InputSerialization"); } if (request.SelectParameters.ExpressionType != ExpressionType.Unknown) { xml.WriteElement("ExpressionType", ValueHelper.EnumToString(request.SelectParameters.ExpressionType)); } xml.WriteElement("Expression", request.SelectParameters.Expression); xml.WriteStartElement("OutputSerialization"); switch (request.SelectParameters.OutputFormat) { case S3CsvOutputFormat csvOutput: xml.WriteStartElement("CSV"); if (csvOutput.FieldDelimiter != null) { xml.WriteElement("FieldDelimiter", ConvertChar(csvOutput.FieldDelimiter)); } if (csvOutput.QuoteCharacter != null) { xml.WriteElement("QuoteCharacter", ConvertChar(csvOutput.QuoteCharacter)); } if (csvOutput.QuoteEscapeCharacter != null) { xml.WriteElement("QuoteEscapeCharacter", ConvertChar(csvOutput.QuoteEscapeCharacter)); } if (csvOutput.QuoteFields != QuoteField.Unknown) { xml.WriteElement("QuoteFields", ValueHelper.EnumToString(csvOutput.QuoteFields)); } if (csvOutput.RecordDelimiter != null) { xml.WriteElement("RecordDelimiter", ConvertChar(csvOutput.RecordDelimiter)); } xml.WriteEndElement("CSV"); break; case S3JsonOutputFormat jsonOutput: xml.WriteElement("RecordDelimiter", jsonOutput.RecordDelimiter); break; } xml.WriteEndElement("OutputSerialization"); xml.WriteEndElement("SelectParameters"); } if (request.OutputLocation != null) { xml.WriteStartElement("OutputLocation"); xml.WriteStartElement("S3"); //These two are required, so we don't check for null xml.WriteElement("BucketName", request.OutputLocation.BucketName); if (request.OutputLocation.Prefix != null) { xml.WriteElement("Prefix", request.OutputLocation.Prefix); } if (request.OutputLocation.StorageClass != StorageClass.Unknown) { xml.WriteElement("StorageClass", ValueHelper.EnumToString(request.OutputLocation.StorageClass)); } if (request.OutputLocation.Acl != ObjectCannedAcl.Unknown) { xml.WriteElement("CannedACL", ValueHelper.EnumToString(request.OutputLocation.Acl)); } //TODO: AccessControlList support if (request.OutputLocation.SseAlgorithm != SseAlgorithm.Unknown) { xml.WriteStartElement("Encryption"); xml.WriteElement("EncryptionType", ValueHelper.EnumToString(request.OutputLocation.SseAlgorithm)); string?context = request.OutputLocation.SseContext.Build(); if (context != null) { xml.WriteElement("KMSContext", context); } if (request.OutputLocation.SseKmsKeyId != null) { xml.WriteElement("KMSKeyId", request.OutputLocation.SseKmsKeyId); } xml.WriteEndElement("Encryption"); } List <KeyValuePair <string, string> > tags = request.OutputLocation.Tags.ToList(); if (tags.Count > 0) { xml.WriteStartElement("Tagging"); xml.WriteStartElement("TagSet"); foreach (KeyValuePair <string, string> tag in tags) { xml.WriteStartElement("Tag"); xml.WriteElement("Key", tag.Key); xml.WriteElement("Value", tag.Value); xml.WriteEndElement("Tag"); } xml.WriteEndElement("TagSet"); xml.WriteEndElement("Tagging"); } List <KeyValuePair <string, string> > metadata = request.OutputLocation.Metadata.ToList(); if (metadata.Count > 0) { xml.WriteStartElement("UserMetadata"); foreach (KeyValuePair <string, string> meta in metadata) { xml.WriteStartElement("MetadataEntry"); xml.WriteElement("Name", meta.Key); xml.WriteElement("Value", meta.Value); xml.WriteEndElement("MetadataEntry"); } xml.WriteEndElement("UserMetadata"); } xml.WriteEndElement("S3"); xml.WriteEndElement("OutputLocation"); } xml.WriteEndElement("RestoreRequest"); return(new MemoryStream(xml.GetBytes())); }
public string CreateScope(string service, DateTimeOffset date) { return($"{ValueHelper.DateToString(date, DateTimeFormat.Iso8601Date)}/{ValueHelper.EnumToString(_options.Value.Region)}/{service}/aws4_request"); }
public async Task <TResp> SendRequestAsync <TReq, TResp>(TReq request, CancellationToken cancellationToken = default) where TResp : IResponse, new() where TReq : IRequest { cancellationToken.ThrowIfCancellationRequested(); request.Timestamp = DateTimeOffset.UtcNow; request.RequestId = Guid.NewGuid(); _logger.LogTrace("Sending {RequestType} with request id {RequestId}", typeof(TReq).Name, request.RequestId); S3Config config = _options.Value; Stream requestStream = _marshaller.MarshalRequest(request, config); _validator.ValidateAndThrow(request); string bucketName = null; if (request is IHasBucketName bn) { bucketName = bn.BucketName; } string objectKey = null; if (request is IHasObjectKey ok) { objectKey = ok.ObjectKey; } //Ensure that the object key is encoded string encodedResource = objectKey != null?UrlHelper.UrlPathEncode(objectKey) : null; if (config.Endpoint == null || config.NamingMode == NamingMode.PathStyle) { if (bucketName != null) { objectKey = bucketName + '/' + encodedResource; } else { objectKey = encodedResource; } } else { objectKey = encodedResource; } StringBuilder sb = StringBuilderPool.Shared.Rent(100); Uri endpoint = config.Endpoint; if (endpoint != null) { sb.Append(endpoint.Host); if (!endpoint.IsDefaultPort) { sb.Append(':').Append(endpoint.Port); } } else { if (config.NamingMode == NamingMode.VirtualHost) { if (bucketName != null) { sb.Append(bucketName).Append(".s3.").Append(ValueHelper.EnumToString(config.Region)).Append(".amazonaws.com"); } else { sb.Append("s3.").Append(ValueHelper.EnumToString(config.Region)).Append(".amazonaws.com"); } } else { sb.Append("s3.").Append(ValueHelper.EnumToString(config.Region)).Append(".amazonaws.com"); } } request.SetHeader(HttpHeaders.Host, sb.ToString()); request.SetHeader(AmzHeaders.XAmzDate, request.Timestamp, DateTimeFormat.Iso8601DateTime); if (requestStream != null) { foreach (IRequestStreamWrapper wrapper in _requestStreamWrappers) { if (wrapper.IsSupported(request)) { requestStream = wrapper.Wrap(requestStream, request); } } } if (!request.Headers.TryGetValue(AmzHeaders.XAmzContentSha256, out string contentHash)) { if (config.PayloadSignatureMode == SignatureMode.Unsigned) { contentHash = "UNSIGNED-PAYLOAD"; } else { contentHash = requestStream == null ? "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" : CryptoHelper.Sha256Hash(requestStream, true).HexEncode(); } request.SetHeader(AmzHeaders.XAmzContentSha256, contentHash); } _logger.LogDebug("ContentSha256 is {ContentSha256}", contentHash); //We add the authorization header here because we need ALL other headers to be present when we do request.SetHeader(HttpHeaders.Authorization, _authBuilder.BuildAuthorization(request)); sb.Append('/').Append(objectKey); //Map all the parameters on to the url if (request.QueryParameters.Count > 0) { sb.Append('?').Append(UrlHelper.CreateQueryString(request.QueryParameters)); } string scheme = endpoint == null ? config.UseTLS ? "https" : "http" : endpoint.Scheme; string fullUrl = scheme + "://" + sb; StringBuilderPool.Shared.Return(sb); _logger.LogDebug("Building request for {Url}", fullUrl); (int statusCode, IDictionary <string, string> headers, Stream responseStream) = await _networkDriver.SendRequestAsync(request.Method, fullUrl, request.Headers, requestStream, cancellationToken).ConfigureAwait(false); //Clear sensitive material from the request if (request is IContainSensitiveMaterial sensitive) { sensitive.ClearSensitiveMaterial(); } TResp response = new TResp(); response.StatusCode = statusCode; response.ContentLength = headers.GetHeaderLong(HttpHeaders.ContentLength); response.ConnectionClosed = "closed".Equals(headers.GetHeader(HttpHeaders.Connection), StringComparison.OrdinalIgnoreCase); response.Date = headers.GetHeaderDate(HttpHeaders.Date, DateTimeFormat.Rfc1123); response.Server = headers.GetHeader(HttpHeaders.Server); response.ResponseId = headers.GetHeader(AmzHeaders.XAmzId2); response.RequestId = headers.GetHeader(AmzHeaders.XAmzRequestId); // https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html response.IsSuccess = !(statusCode == 403 || //Forbidden statusCode == 400 || //BadRequest statusCode == 500 || //InternalServerError statusCode == 416 || //RequestedRangeNotSatisfiable statusCode == 405 || //MethodNotAllowed statusCode == 411 || //LengthRequired statusCode == 404 || //NotFound statusCode == 501 || //NotImplemented statusCode == 504 || //GatewayTimeout statusCode == 301 || //MovedPermanently statusCode == 412 || //PreconditionFailed statusCode == 307 || //TemporaryRedirect statusCode == 409 || //Conflict statusCode == 503); //ServiceUnavailable //Only marshal successful responses if (response.IsSuccess) { _marshaller.MarshalResponse(config, request, response, headers, responseStream); } else { MemoryStream ms = new MemoryStream(); responseStream.CopyTo(ms); if (ms.Length > 0) { ms.Seek(0, SeekOrigin.Begin); using (responseStream) response.Error = ErrorHandler.Create(ms); _logger.LogError("Received error: '{Message}'. Details: '{Details}'", response.Error.Message, response.Error.GetExtraData()); } } return(response); }
public async Task <TResp> SendRequestAsync <TReq, TResp>(TReq request, CancellationToken cancellationToken) where TResp : IResponse, new() where TReq : IRequest { _logger.LogTrace($"Sending {typeof(TReq)} to bucket '{request.BucketName}' as resource '{request.Resource}'"); Stream requestStream = _marshaller.MarshalRequest(request); _validator.ValidateAndThrow(request); //Ensure that the resource is encoded string encodedResource = UrlHelper.UrlPathEncode(request.Resource); if (_options.Value.Endpoint == null || _options.Value.NamingType == NamingType.PathStyle) { if (!string.IsNullOrEmpty(request.BucketName)) { request.Resource = request.BucketName + '/' + encodedResource; } else { request.Resource = encodedResource; } } else { request.Resource = encodedResource; } StringBuilder sb = new StringBuilder(512); if (_options.Value.Endpoint != null) { sb.Append(_options.Value.Endpoint.Host); } else { if (_options.Value.NamingType == NamingType.VirtualHost) { if (!string.IsNullOrEmpty(request.BucketName)) { sb.Append(request.BucketName).Append(".s3.").Append(ValueHelper.EnumToString(_options.Value.Region)).Append(".amazonaws.com"); } else { sb.Append("s3.").Append(ValueHelper.EnumToString(_options.Value.Region)).Append(".amazonaws.com"); } } else { sb.Append("s3.").Append(ValueHelper.EnumToString(_options.Value.Region)).Append(".amazonaws.com"); } } request.AddHeader(HttpHeaders.Host, sb.ToString()); request.AddHeader(AmzHeaders.XAmzDate, request.Date, DateTimeFormat.Iso8601DateTime); if (requestStream != null && _requestStreamWrappers != null) { foreach (IRequestStreamWrapper wrapper in _requestStreamWrappers) { if (wrapper.IsSupported(request)) { requestStream = wrapper.Wrap(requestStream, request); } } } //We check if it was already added here because it might have been due to streaming support if (!request.Headers.ContainsKey(AmzHeaders.XAmzContentSha256)) { string contentHash; if (!_options.Value.EnablePayloadSigning) { contentHash = "UNSIGNED-PAYLOAD"; } else { contentHash = requestStream == null ? "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" : CryptoHelper.Sha256Hash(requestStream, true).HexEncode(); } _logger.LogDebug("ContentSha256 is {ContentSha256}", contentHash); request.AddHeader(AmzHeaders.XAmzContentSha256, contentHash); } //We add the authorization header here because we need ALL other headers to be present when we do request.AddHeader(HttpHeaders.Authorization, _authBuilder.BuildAuthorization(request)); sb.Append('/').Append(request.Resource); //Map all the parameters on to the url if (request.QueryParameters.Count > 0) { sb.Append('?').Append(UrlHelper.CreateQueryString(request.QueryParameters)); } string fullUrl = "https://" + sb; _logger.LogDebug("Building request for {Url}", fullUrl); (int statusCode, IDictionary <string, string> headers, Stream responseStream) = await _networkDriver.SendRequestAsync(request.Method, fullUrl, request.Headers, requestStream, cancellationToken).ConfigureAwait(false); //Clear sensitive material from the request if (request is IContainSensitiveMaterial sensitive) { sensitive.ClearSensitiveMaterial(); } TResp response = new TResp(); response.StatusCode = statusCode; response.ContentLength = headers.GetHeaderLong(HttpHeaders.ContentLength); response.ConnectionClosed = "closed".Equals(headers.GetHeader(HttpHeaders.Connection), StringComparison.OrdinalIgnoreCase); response.Date = headers.GetHeaderDate(HttpHeaders.Date, DateTimeFormat.Rfc1123); response.Server = headers.GetHeader(HttpHeaders.Server); response.ResponseId = headers.GetHeader(AmzHeaders.XAmzId2); response.RequestId = headers.GetHeader(AmzHeaders.XAmzRequestId); // https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html response.IsSuccess = !(statusCode == 403 || //Forbidden statusCode == 400 || //BadRequest statusCode == 500 || //InternalServerError statusCode == 416 || //RequestedRangeNotSatisfiable statusCode == 405 || //MethodNotAllowed statusCode == 411 || //LengthRequired statusCode == 404 || //NotFound statusCode == 501 || //NotImplemented statusCode == 504 || //GatewayTimeout statusCode == 301 || //MovedPermanently statusCode == 412 || //PreconditionFailed statusCode == 307 || //TemporaryRedirect statusCode == 409 || //Conflict statusCode == 503); //ServiceUnavailable //Don't know why, but ContentLength seems to be 0 sometimes, even though it is in the headers if (!response.IsSuccess && (response.ContentLength > 0 || responseStream.Length > 0)) { using (responseStream) response.Error = ErrorHandler.Create(responseStream); _logger.LogError("Received error: '{Message}'. Details: '{Details}'", response.Error.Message, response.Error.GetExtraData()); } //Only marshal successful responses if (response.IsSuccess) { _marshaller.MarshalResponse(request, response, headers, responseStream); } return(response); }