Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Merge latest changes from main into SRA I&A #662

Merged
merged 49 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
81a5043
chore: Require Swift 5.7, fix deprecation warnings (#600)
jbelkins Oct 11, 2023
b759e19
feat: support initial-response in RPC based event streams (#597)
dayaffe Oct 12, 2023
e7f1d28
chore: Updates version to 0.32.0
aws-sdk-swift-automation Oct 12, 2023
f6c8bc8
chore: Add newline to README.md (#602)
jbelkins Oct 13, 2023
62ad3a2
feat: add limited support in smithy-swift for visionOS (#606)
dayaffe Oct 20, 2023
818a5c7
feat: add support for requiresLength trait and Transfer-Encoding: Chu…
dayaffe Oct 23, 2023
e0b4f25
chore: Update to aws-crt-swift 0.15.0 (#607)
jbelkins Oct 24, 2023
31d7652
fix: content-length middleware should not error on event streams (#608)
dayaffe Oct 25, 2023
4d965ac
chore: Updates version to 0.33.0
aws-sdk-swift-automation Oct 25, 2023
53fbde2
chore: Improved downstream task (#568)
jbelkins Oct 25, 2023
ef962dd
chore: Convert idempotency token middleware from closure to reusable …
jbelkins Oct 30, 2023
a35c983
fix: Update aws-crt-swift dependency to 0.17.0 (#612)
sichanyoo Oct 31, 2023
6037a81
chore: Updates version to 0.34.0
aws-sdk-swift-automation Oct 31, 2023
96b95c1
fix: Endpoint url should be nil if host or scheme is missing (#614)
dayaffe Oct 31, 2023
a03eedc
fix: Pool HTTP connections based on scheme, host, and port (#615)
jbelkins Nov 2, 2023
c19f585
add default log level to initialize method (#616)
CyprienRicque Nov 6, 2023
c2ab0a5
feat: add utility method for converting SdkHttpRequest to URLRequest.…
sichanyoo Nov 6, 2023
ab999a9
chore: Updates version to 0.35.0
aws-sdk-swift-automation Nov 7, 2023
8d05731
fix: Add a header to operation doc comments (#621)
jbelkins Nov 22, 2023
2658d06
remove unnecessary TODOs (#622)
dayaffe Nov 27, 2023
b1a0cf4
fix: Codegen issues re: recursion, Swift keywords in unions (#623)
jbelkins Nov 28, 2023
34aecef
feat!: Replace the XML encoder with a custom Smithy implementation (#…
jbelkins Nov 29, 2023
af13f00
feat!: Use closures for processing HTTP response (#624)
jbelkins Nov 30, 2023
794b36b
feat: add custom trait PaginationTruncationMember (#625)
dayaffe Dec 1, 2023
2550e03
allow isTruncated to be optional bool (#626)
dayaffe Dec 4, 2023
ca28bf2
chore: Updates version to 0.36.0
aws-sdk-swift-automation Dec 5, 2023
39638f3
chore: Run tvOS old & new in CI (#628)
jbelkins Dec 6, 2023
562a8cc
fix: Fix Package.swift warning on Mac (#629)
jbelkins Dec 7, 2023
460e51c
chore: refactor HttpBody and ByteStream to be a single class ByteStre…
dayaffe Dec 7, 2023
d51e9a6
chore: remove sync read in unused data extension (#630)
dayaffe Dec 12, 2023
3254b1f
update smithy to 1.42.0 (#631)
dayaffe Dec 13, 2023
77b100f
chore: Updates version to 0.37.0
aws-sdk-swift-automation Dec 20, 2023
d222378
chore: Update to aws-crt-swift 0.20.0 (#633)
dayaffe Dec 22, 2023
f11e2e8
fix: add back from method with fileHandle (#635)
dayaffe Dec 22, 2023
f9046ac
fix!: Add no-op behavior for initialize methods of logging system. (#…
sichanyoo Jan 9, 2024
922d066
feat!: URLSession-based HTTP Client (#636)
jbelkins Jan 9, 2024
29f734a
bump up CRT version to 0.22.0 (#639)
dayaffe Jan 11, 2024
4ccc50e
chore: Update version to 0.38.0 (#641)
jbelkins Jan 11, 2024
98018ec
chore: Empty commit (#643)
jbelkins Jan 11, 2024
4332319
feat: add wrapper for checksums + unit tests (#642)
dayaffe Jan 12, 2024
9a25887
feat: Use the Foundation HTTP client by default on Mac (#646)
jbelkins Jan 18, 2024
a89f313
chore: Updates version to 0.39.0
aws-sdk-swift-automation Jan 18, 2024
6aabf23
chore: Change MyURLQueryItem to SDKURLQueryItem (#652)
sichanyoo Jan 26, 2024
ea1b674
feat!: Provide HTTP request components by closure instead of protocol…
jbelkins Jan 26, 2024
a084a20
fix: Don't retry modeled errors by default (#653)
jbelkins Jan 26, 2024
15a2518
feat!: Eliminate service client protocols (#655)
jbelkins Feb 5, 2024
7e832e2
feat: add support for flexible checksums on Normal payloads (#647)
dayaffe Feb 7, 2024
7560c21
chore: Update aws-crt-swift to 0.26.0 (#661)
jbelkins Feb 8, 2024
e4dfa6d
Merge main into chore/merge-changes-from-main.
Feb 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 71 additions & 4 deletions Sources/ClientRuntime/Networking/HashFunction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@

import AwsCommonRuntimeKit

enum HashResult {
public enum HashResult {
case data(Data)
case integer(UInt32)
}

enum HashError: Error {
public enum HashError: Error {
case invalidInput
case hashingFailed(reason: String)
}

enum HashFunction {
public enum HashFunction {
case crc32, crc32c, sha1, sha256, md5

static func from(string: String) -> HashFunction? {
Expand All @@ -29,7 +29,29 @@
}
}

var isSupported: Bool {
static func fromList(_ stringArray: [String]) -> [HashFunction] {
var hashFunctions = [HashFunction]()

for string in stringArray {
if let hashFunction = HashFunction.from(string: string) {
hashFunctions.append(hashFunction)
}
}

return hashFunctions
}

Check warning on line 43 in Sources/ClientRuntime/Networking/HashFunction.swift

View workflow job for this annotation

GitHub Actions / swiftlint

Lines should not have trailing whitespace (trailing_whitespace)
func toString() -> String {
switch self {
case .crc32: return "crc32"
case .crc32c: return "crc32c"
case .sha1: return "sha1"
case .sha256: return "sha256"
case .md5: return "md5"
}
}

var isFlexibleChecksum: Bool {
switch self {
case .crc32, .crc32c, .sha256, .sha1:
return true
Expand Down Expand Up @@ -69,6 +91,41 @@
}
}

extension HashFunction: Comparable {
/*
* Priority-order for validating checksum = [ CRC32C, CRC32, SHA1, SHA256 ]
* Order is determined by speed of the algorithm's implementation
* MD5 is not supported by list ordering
*/
public static func < (lhs: HashFunction, rhs: HashFunction) -> Bool {
let order: [HashFunction] = [.crc32c, .crc32, .sha1, .sha256]

let lhsIndex = order.firstIndex(of: lhs) ?? Int.max
let rhsIndex = order.firstIndex(of: rhs) ?? Int.max

return lhsIndex < rhsIndex
}
}

extension [HashFunction] {
func getPriorityOrderValidationList() -> [HashFunction] {
// Filter out .md5 if present and then sort the remaining hash functions
return self.filter { $0 != .md5 }.sorted()
}
}

extension UInt32 {
func toBase64EncodedString() -> String {
// Create a Data instance from the UInt32 value
var value = self
var bigEndianValue = value.bigEndian
var data = Data(bytes: &bigEndianValue, count: MemoryLayout<UInt32>.size)

// Base64 encode the data
return data.base64EncodedString()
}
}

extension HashResult {

// Convert a HashResult to a hexadecimal String
Expand All @@ -80,4 +137,14 @@
return String(format: "%08x", integer)
}
}

// Convert a HashResult to a base64-encoded String
func toBase64String() -> String {
switch self {
case .data(let data):
return data.base64EncodedString()
case .integer(let integer):
return integer.toBase64EncodedString()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
Self.MOutput == H.Output,
Self.Context == H.Context {

// Skip MD5 hash if using checksums
if (input.headers.exists(name: "x-amz-sdk-checksum-algorithm")) {

Check warning on line 22 in Sources/ClientRuntime/Networking/Http/Middlewares/ContentMD5Middleware.swift

View workflow job for this annotation

GitHub Actions / swiftlint

`if`, `for`, `guard`, `switch`, `while`, and `catch` statements shouldn't unnecessarily wrap their conditionals or arguments in parentheses (control_statement)
return try await next.handle(context: context, input: input)
}

switch input.body {
case .data(let data):
guard let data else {
Expand Down
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
}
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 {

Check warning on line 75 in Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsResponseMiddleware.swift

View workflow job for this annotation

GitHub Actions / swiftlint

Lines should not have trailing whitespace (trailing_whitespace)
guard let data else {
throw ClientError.dataNotFound("Cannot calculate checksum of empty body!")
}

Check warning on line 79 in Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsResponseMiddleware.swift

View workflow job for this annotation

GitHub Actions / swiftlint

Lines should not have trailing whitespace (trailing_whitespace)
let calculatedChecksum = try responseChecksum.computeHash(of: data)

Check warning on line 81 in Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsResponseMiddleware.swift

View workflow job for this annotation

GitHub Actions / swiftlint

Lines should not have trailing whitespace (trailing_whitespace)
let actualChecksum = calculatedChecksum.toBase64String()

Check warning on line 83 in Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsResponseMiddleware.swift

View workflow job for this annotation

GitHub Actions / swiftlint

Lines should not have trailing whitespace (trailing_whitespace)
if expectedChecksum != actualChecksum {
throw ChecksumMismatchException.message(
"Checksum mismatch. Expected \(expectedChecksum) but was \(actualChecksum)"
)
}
}

func handleStreamPayload(_ stream: Stream) throws {
return
}

Check warning on line 94 in Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsResponseMiddleware.swift

View workflow job for this annotation

GitHub Actions / swiftlint

Lines should not have trailing whitespace (trailing_whitespace)
// 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!")
}

Check warning on line 104 in Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsResponseMiddleware.swift

View workflow job for this annotation

GitHub Actions / swiftlint

Lines should not have trailing whitespace (trailing_whitespace)
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)
}
Loading
Loading