-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: Merge latest changes from main into SRA I&A (#662)
* chore: Require Swift 5.7, fix deprecation warnings (#600) * feat: support initial-response in RPC based event streams (#597) * chore: Updates version to 0.32.0 * chore: Add newline to README.md (#602) * feat: add limited support in smithy-swift for visionOS (#606) * feat: add support for requiresLength trait and Transfer-Encoding: Chunked (#604) * chore: Update to aws-crt-swift 0.15.0 (#607) * fix: content-length middleware should not error on event streams (#608) * chore: Updates version to 0.33.0 * chore: Improved downstream task (#568) * chore: Convert idempotency token middleware from closure to reusable type (#610) * fix: Update aws-crt-swift dependency to 0.17.0 (#612) * chore: Updates version to 0.34.0 * fix: Endpoint url should be nil if host or scheme is missing (#614) * fix: Pool HTTP connections based on scheme, host, and port (#615) * add default log level to initialize method (#616) * feat: add utility method for converting SdkHttpRequest to URLRequest. (#613) * Add extension constructor to URLRequest to convert SDKHttpRequest * Add preprocessor conditional import functionality to SwiftWriter. --------- Co-authored-by: Sichan Yoo <[email protected]> * chore: Updates version to 0.35.0 * fix: Add a header to operation doc comments (#621) * remove unnecessary TODOs (#622) * fix: Codegen issues re: recursion, Swift keywords in unions (#623) * feat!: Replace the XML encoder with a custom Smithy implementation (#619) * feat!: Use closures for processing HTTP response (#624) * feat: add custom trait PaginationTruncationMember (#625) * allow isTruncated to be optional bool (#626) * chore: Updates version to 0.36.0 * chore: Run tvOS old & new in CI (#628) * fix: Fix Package.swift warning on Mac (#629) * chore: refactor HttpBody and ByteStream to be a single class ByteStream (#627) * chore: remove sync read in unused data extension (#630) * update smithy to 1.42.0 (#631) * chore: Updates version to 0.37.0 * chore: Update to aws-crt-swift 0.20.0 (#633) * fix: add back from method with fileHandle (#635) * fix!: Add no-op behavior for initialize methods of logging system. (#637) * Add no-op behavior for initialize methods if it isn't the first time being called. * Make LockingSystem threadsafe. * Make initialize methods async. --------- Co-authored-by: Sichan Yoo <[email protected]> * feat!: URLSession-based HTTP Client (#636) * bump up CRT version to 0.22.0 (#639) * chore: Update version to 0.38.0 (#641) * chore: Empty commit (#643) * feat: add wrapper for checksums + unit tests (#642) * feat: Use the Foundation HTTP client by default on Mac (#646) * chore: Updates version to 0.39.0 * chore: Change MyURLQueryItem to SDKURLQueryItem (#652) * feat!: Provide HTTP request components by closure instead of protocol (#654) * fix: Don't retry modeled errors by default (#653) * feat!: Eliminate service client protocols (#655) * feat: add support for flexible checksums on Normal payloads (#647) * chore: Update aws-crt-swift to 0.26.0 (#661) --------- Co-authored-by: Josh Elkins <[email protected]> Co-authored-by: David Yaffe <[email protected]> Co-authored-by: AWS SDK Swift Automation <[email protected]> Co-authored-by: Cyprien Ricque <[email protected]> Co-authored-by: Sichan Yoo <[email protected]>
- Loading branch information
1 parent
ec8f4ea
commit bb7045d
Showing
20 changed files
with
538 additions
and
258 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsRequestMiddleware.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0. | ||
|
||
public struct FlexibleChecksumsRequestMiddleware<OperationStackInput, OperationStackOutput>: Middleware { | ||
|
||
public let id: String = "FlexibleChecksumsRequestMiddleware" | ||
|
||
let checksumAlgorithm: String? | ||
|
||
public init(checksumAlgorithm: String?) { | ||
self.checksumAlgorithm = checksumAlgorithm | ||
} | ||
|
||
public func handle<H>(context: Context, | ||
input: SerializeStepInput<OperationStackInput>, | ||
next: H) async throws -> OperationOutput<OperationStackOutput> | ||
where H: Handler, | ||
Self.MInput == H.Input, | ||
Self.MOutput == H.Output, | ||
Self.Context == H.Context { | ||
|
||
// Initialize logger | ||
guard let logger = context.getLogger() else { | ||
throw ClientError.unknownError("No logger found!") | ||
} | ||
|
||
guard let checksumString = checksumAlgorithm else { | ||
logger.info("No checksum provided! Skipping flexible checksums workflow...") | ||
return try await next.handle(context: context, input: input) | ||
} | ||
|
||
guard let checksumHashFunction = HashFunction.from(string: checksumString) else { | ||
logger.info("Found no supported checksums! Skipping flexible checksums workflow...") | ||
return try await next.handle(context: context, input: input) | ||
} | ||
|
||
// Determine the header name | ||
let headerName = "x-amz-checksum-\(checksumHashFunction)" | ||
logger.debug("Resolved checksum header name: \(headerName)") | ||
|
||
// Get the request | ||
let request = input.builder | ||
|
||
func handleNormalPayload(_ data: Data?) throws { | ||
|
||
// Check if any checksum header is already provided by the user | ||
let checksumHeaderPrefix = "x-amz-checksum-" | ||
if request.headers.headers.contains(where: { $0.name.lowercased().starts(with: checksumHeaderPrefix) }) { | ||
logger.debug("Checksum header already provided by the user. Skipping calculation.") | ||
return | ||
} | ||
|
||
guard let data else { | ||
throw ClientError.dataNotFound("Cannot calculate checksum of empty body!") | ||
} | ||
|
||
if input.builder.headers.value(for: headerName) == nil { | ||
logger.debug("Calculating checksum") | ||
} | ||
|
||
let checksum = try checksumHashFunction.computeHash(of: data).toBase64String() | ||
|
||
request.updateHeader(name: headerName, value: [checksum]) | ||
} | ||
|
||
func handleStreamPayload(_ stream: Stream) throws { | ||
logger.error("Stream payloads are not yet supported with flexible checksums!") | ||
return | ||
} | ||
|
||
// Handle body vs handle stream | ||
switch request.body { | ||
case .data(let data): | ||
try handleNormalPayload(data) | ||
case .stream(let stream): | ||
try handleStreamPayload(stream) | ||
case .noStream: | ||
throw ClientError.dataNotFound("Cannot calculate the checksum of an empty body!") | ||
} | ||
|
||
return try await next.handle(context: context, input: input) | ||
} | ||
|
||
public typealias MInput = SerializeStepInput<OperationStackInput> | ||
public typealias MOutput = OperationOutput<OperationStackOutput> | ||
public typealias Context = HttpContext | ||
} |
115 changes: 115 additions & 0 deletions
115
Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsResponseMiddleware.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0. | ||
|
||
public struct FlexibleChecksumsResponseMiddleware<OperationStackOutput>: Middleware { | ||
|
||
public let id: String = "FlexibleChecksumsResponseMiddleware" | ||
|
||
// The priority to validate response checksums, if multiple are present | ||
let CHECKSUM_HEADER_VALIDATION_PRIORITY_LIST: [String] = [ | ||
HashFunction.crc32c, | ||
.crc32, | ||
.sha1, | ||
.sha256 | ||
].map { $0.toString() } | ||
|
||
let validationMode: Bool | ||
|
||
public init(validationMode: Bool) { | ||
self.validationMode = validationMode | ||
} | ||
|
||
public func handle<H>(context: Context, | ||
input: SdkHttpRequest, | ||
next: H) async throws -> OperationOutput<OperationStackOutput> | ||
where H: Handler, | ||
Self.MInput == H.Input, | ||
Self.MOutput == H.Output, | ||
Self.Context == H.Context { | ||
|
||
// The name of the checksum header which was validated. If `null`, validation was not performed. | ||
context.attributes.set(key: AttributeKey<String>(name: "ChecksumHeaderValidated"), value: nil) | ||
|
||
// Initialize logger | ||
guard let logger = context.getLogger() else { | ||
throw ClientError.unknownError("No logger found!") | ||
} | ||
|
||
// Exit if validation should not be performed | ||
if !validationMode { | ||
logger.info("Checksum validation should not be performed! Skipping workflow...") | ||
return try await next.handle(context: context, input: input) | ||
} | ||
|
||
// Get the response | ||
let response = try await next.handle(context: context, input: input) | ||
let httpResponse = response.httpResponse | ||
|
||
// Determine if any checksum headers are present | ||
logger.debug("HEADERS: \(httpResponse.headers)") | ||
let _checksumHeader = CHECKSUM_HEADER_VALIDATION_PRIORITY_LIST.first { | ||
httpResponse.headers.value(for: "x-amz-checksum-\($0)") != nil | ||
} | ||
guard let checksumHeader = _checksumHeader else { | ||
logger.warn( | ||
"User requested checksum validation, but the response headers did not contain any valid checksums" | ||
) | ||
return try await next.handle(context: context, input: input) | ||
} | ||
|
||
let fullChecksumHeader = "x-amz-checksum-" + checksumHeader | ||
|
||
// let the user know which checksum will be validated | ||
logger.debug("Validating checksum from \(fullChecksumHeader)") | ||
context.attributes.set(key: AttributeKey<String>(name: "ChecksumHeaderValidated"), value: fullChecksumHeader) | ||
|
||
let checksumString = checksumHeader.removePrefix("x-amz-checksum-") | ||
guard let responseChecksum = HashFunction.from(string: checksumString) else { | ||
throw ClientError.dataNotFound("Checksum found in header is not supported!") | ||
} | ||
guard let expectedChecksum = httpResponse.headers.value(for: fullChecksumHeader) else { | ||
throw ClientError.dataNotFound("Could not determine the expected checksum!") | ||
} | ||
|
||
func handleNormalPayload(_ data: Data?) throws { | ||
|
||
guard let data else { | ||
throw ClientError.dataNotFound("Cannot calculate checksum of empty body!") | ||
} | ||
|
||
let calculatedChecksum = try responseChecksum.computeHash(of: data) | ||
|
||
let actualChecksum = calculatedChecksum.toBase64String() | ||
|
||
if expectedChecksum != actualChecksum { | ||
throw ChecksumMismatchException.message( | ||
"Checksum mismatch. Expected \(expectedChecksum) but was \(actualChecksum)" | ||
) | ||
} | ||
} | ||
|
||
func handleStreamPayload(_ stream: Stream) throws { | ||
return | ||
} | ||
|
||
// Handle body vs handle stream | ||
switch response.httpResponse.body { | ||
case .data(let data): | ||
try handleNormalPayload(data) | ||
case .stream(let stream): | ||
try handleStreamPayload(stream) | ||
case .noStream: | ||
throw ClientError.dataNotFound("Cannot calculate the checksum of an empty body!") | ||
} | ||
|
||
return try await next.handle(context: context, input: input) | ||
} | ||
|
||
public typealias MInput = SdkHttpRequest | ||
public typealias MOutput = OperationOutput<OperationStackOutput> | ||
public typealias Context = HttpContext | ||
} | ||
|
||
enum ChecksumMismatchException: Error { | ||
case message(String) | ||
} |
Oops, something went wrong.