diff --git a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift index ae89c9359e..0350c6ca01 100644 --- a/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift +++ b/AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Dependency/UploadPartInput+presignURL.swift @@ -5,12 +5,13 @@ // SPDX-License-Identifier: Apache-2.0 import Foundation -@testable import AWSS3 +import AWSS3 @_spi(SmithyReadWrite) import ClientRuntime -import AWSClientRuntime +@_spi(UnknownAWSHTTPServiceError) @_spi(SmithyReadWrite) import AWSClientRuntime import Smithy import SmithyHTTPAPI import SmithyRetries +@_spi(SmithyReadWrite) import SmithyXML // swiftlint:disable identifier_name // swiftlint:disable line_length @@ -29,7 +30,7 @@ extension UploadPartInput { .withServiceName(value: serviceName) .withOperation(value: "uploadPart") .withIdempotencyTokenGenerator(value: config.idempotencyTokenGenerator) - .withLogger(value: config.logger) + .withLogger(value: config.telemetryProvider.loggerProvider.getLogger(name: S3Client.clientName)) .withPartitionID(value: config.partitionID) .withAuthSchemes(value: config.authSchemes ?? []) .withAuthSchemeResolver(value: config.authSchemeResolver) @@ -52,9 +53,9 @@ extension UploadPartInput { config.httpInterceptorProviders.forEach { provider in builder.interceptors.add(provider.create()) } - builder.interceptors.add(ClientRuntime.URLPathMiddleware(UploadPartInput.urlPathProvider(_:))) + builder.interceptors.add(ClientRuntime.URLPathMiddleware(UploadPartInput.customUrlPathProvider(_:))) builder.interceptors.add(ClientRuntime.URLHostMiddleware()) - builder.deserialize(ClientRuntime.DeserializeMiddleware(UploadPartOutput.httpOutput(from:), PutObjectOutputError.httpError(from:))) + builder.deserialize(ClientRuntime.DeserializeMiddleware(UploadPartOutput.customHttpOutput(from:), CustomUploadPartOutputError.httpError(from:))) builder.interceptors.add(ClientRuntime.LoggerMiddleware(clientLogMode: config.clientLogMode)) builder.retryStrategy(SmithyRetries.DefaultRetryStrategy(options: config.retryStrategyOptions)) builder.retryErrorInfoProvider(AWSClientRuntime.AWSRetryErrorInfoProvider.errorInfo(for:)) @@ -80,9 +81,89 @@ extension UploadPartInput { .build() return try await op.presignRequest(input: input).endpoint.url } - } +} + +private extension UploadPartInput { + static func customUrlPathProvider(_ value: UploadPartInput) -> Swift.String? { + guard let key = value.key else { + return nil + } + return "/\(key.urlPercentEncoding(encodeForwardSlash: false))" + } +} + +private extension UploadPartOutput { + static func customHttpOutput(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> UploadPartOutput { + var value = UploadPartOutput() + if let bucketKeyEnabledHeaderValue = httpResponse.headers.value(for: "x-amz-server-side-encryption-bucket-key-enabled") { + value.bucketKeyEnabled = Swift.Bool(bucketKeyEnabledHeaderValue) ?? false + } + if let checksumCRC32HeaderValue = httpResponse.headers.value(for: "x-amz-checksum-crc32") { + value.checksumCRC32 = checksumCRC32HeaderValue + } + if let checksumCRC32CHeaderValue = httpResponse.headers.value(for: "x-amz-checksum-crc32c") { + value.checksumCRC32C = checksumCRC32CHeaderValue + } + if let checksumSHA1HeaderValue = httpResponse.headers.value(for: "x-amz-checksum-sha1") { + value.checksumSHA1 = checksumSHA1HeaderValue + } + if let checksumSHA256HeaderValue = httpResponse.headers.value(for: "x-amz-checksum-sha256") { + value.checksumSHA256 = checksumSHA256HeaderValue + } + if let eTagHeaderValue = httpResponse.headers.value(for: "ETag") { + value.eTag = eTagHeaderValue + } + if let requestChargedHeaderValue = httpResponse.headers.value(for: "x-amz-request-charged") { + value.requestCharged = S3ClientTypes.RequestCharged(rawValue: requestChargedHeaderValue) + } + if let sseCustomerAlgorithmHeaderValue = httpResponse.headers.value(for: "x-amz-server-side-encryption-customer-algorithm") { + value.sseCustomerAlgorithm = sseCustomerAlgorithmHeaderValue + } + if let sseCustomerKeyMD5HeaderValue = httpResponse.headers.value(for: "x-amz-server-side-encryption-customer-key-MD5") { + value.sseCustomerKeyMD5 = sseCustomerKeyMD5HeaderValue + } + if let ssekmsKeyIdHeaderValue = httpResponse.headers.value(for: "x-amz-server-side-encryption-aws-kms-key-id") { + value.ssekmsKeyId = ssekmsKeyIdHeaderValue + } + if let serverSideEncryptionHeaderValue = httpResponse.headers.value(for: "x-amz-server-side-encryption") { + value.serverSideEncryption = S3ClientTypes.ServerSideEncryption(rawValue: serverSideEncryptionHeaderValue) + } + return value + } +} + +private enum CustomUploadPartOutputError { + static func httpError(from httpResponse: SmithyHTTPAPI.HTTPResponse) async throws -> Swift.Error { + let data = try await httpResponse.data() + let responseReader = try SmithyXML.Reader.from(data: data) + let baseError = try AWSClientRuntime.RestXMLError(httpResponse: httpResponse, responseReader: responseReader, noErrorWrapping: true) + if let error = baseError.customError() { return error } + if baseError.httpResponse.statusCode == .notFound && baseError.httpResponse.body.isEmpty { + return CustomUploadPartOutputError.NotFound( + httpResponse: baseError.httpResponse, + message: baseError.requestID, + requestID: baseError.message, + requestID2: baseError.requestID2 + ) + } + switch baseError.code { + default: return try AWSClientRuntime.UnknownAWSHTTPServiceError.makeError(baseError: baseError) + } + } + + private struct NotFound: ClientRuntime.ModeledError, AWSClientRuntime.AWSS3ServiceError, ClientRuntime.HTTPError, Swift.Error { + static var typeName: Swift.String { "NotFound" } + static var fault: ClientRuntime.ErrorFault { .client } + static var isRetryable: Swift.Bool { false } + static var isThrottling: Swift.Bool { false } + var httpResponse = SmithyHTTPAPI.HTTPResponse() + var message: Swift.String? + var requestID: Swift.String? + var requestID2: Swift.String? + } +} -struct UploadPartPresignedMiddleware: Smithy.RequestMessageSerializer { +private struct UploadPartPresignedMiddleware: Smithy.RequestMessageSerializer { typealias InputType = UploadPartInput typealias RequestType = SmithyHTTPAPI.HTTPRequest