Ejemplo n.º 1
0
        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()));
        }
Ejemplo n.º 2
0
        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");
            }
        }
Ejemplo n.º 3
0
        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()));
        }
Ejemplo n.º 5
0
        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);
        }
Ejemplo n.º 6
0
        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);
        }
Ejemplo n.º 7
0
        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()));
        }
Ejemplo n.º 8
0
    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()));
    }
Ejemplo n.º 9
0
 public string CreateScope(string service, DateTimeOffset date)
 {
     return($"{ValueHelper.DateToString(date, DateTimeFormat.Iso8601Date)}/{ValueHelper.EnumToString(_options.Value.Region)}/{service}/aws4_request");
 }
Ejemplo n.º 10
0
        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);
        }
Ejemplo n.º 11
0
        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);
        }