From 9d6d175416494bfddcc118de6b870a21720e8604 Mon Sep 17 00:00:00 2001 From: David Yaffe Date: Tue, 25 Jun 2024 12:05:55 -0700 Subject: [PATCH 1/2] feat: change SignedBodyValue enum to allow precomputed sha256 hash (#271) --- .../auth/signing/SigningConfig.swift | 53 +++++++++++++++---- .../auth/SigningConfigTests.swift | 2 +- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/Source/AwsCommonRuntimeKit/auth/signing/SigningConfig.swift b/Source/AwsCommonRuntimeKit/auth/signing/SigningConfig.swift index b6da817de..8c7a1cfbe 100644 --- a/Source/AwsCommonRuntimeKit/auth/signing/SigningConfig.swift +++ b/Source/AwsCommonRuntimeKit/auth/signing/SigningConfig.swift @@ -119,7 +119,7 @@ public struct SigningConfig: CStructWithUserData { return withByteCursorFromStrings( region, service, - signedBodyValue.rawValue) { regionCursor, serviceCursor, signedBodyValueCursor in + signedBodyValue.description) { regionCursor, serviceCursor, signedBodyValueCursor in cConfig.region = regionCursor cConfig.service = serviceCursor @@ -174,25 +174,56 @@ public enum SignedBodyHeaderType { /// Optional string to use as the canonical request's body value. /// Typically, this is the SHA-256 of the (request/chunk/event) payload, written as lowercase hex. /// If this has been precalculated, it can be set here. Special values used by certain services can also be set. -public enum SignedBodyValue: String { +public enum SignedBodyValue: CustomStringConvertible, Equatable { /// if empty, a public value will be calculated from the payload during signing - case empty = "" + case empty /// For empty sha256 - case emptySha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + case emptySha256 + /// Use this to provide a precalculated sha256 value + case precomputedSha256(String) /// Use this in the case of needing to not use the payload for signing - case unsignedPayload = "UNSIGNED-PAYLOAD" + case unsignedPayload /// For streaming sha256 payload - case streamingSha256Payload = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" + case streamingSha256Payload /// For streaming sha256 payload trailer - case streamingSha256PayloadTrailer = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER" + case streamingSha256PayloadTrailer /// For streaming sigv4a sha256 payload - case streamingECDSA_P256Sha256Payload = "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD" + case streamingECDSA_P256Sha256Payload /// For streaming sigv4a sha256 payload trailer - case streamingECDSA_P256Sha256PayloadTrailer = "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER" + case streamingECDSA_P256Sha256PayloadTrailer /// For streaming sigv4a sha256 events - case streamingSha256Events = "STREAMING-AWS4-HMAC-SHA256-EVENTS" + case streamingSha256Events /// For streaming unsigned payload trailer - case streamingUnSignedPayloadTrailer = "STREAMING-UNSIGNED-PAYLOAD-TRAILER" + case streamingUnSignedPayloadTrailer + + public var description: String { + switch self { + case .empty: + return "" + case .emptySha256: + return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + case .precomputedSha256(let value): + return value + case .unsignedPayload: + return "UNSIGNED-PAYLOAD" + case .streamingSha256Payload: + return "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" + case .streamingSha256PayloadTrailer: + return "STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER" + case .streamingECDSA_P256Sha256Payload: + return "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD" + case .streamingECDSA_P256Sha256PayloadTrailer: + return "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER" + case .streamingSha256Events: + return "STREAMING-AWS4-HMAC-SHA256-EVENTS" + case .streamingUnSignedPayloadTrailer: + return "STREAMING-UNSIGNED-PAYLOAD-TRAILER" + } + } + + public static func == (lhs: SignedBodyValue, rhs: SignedBodyValue) -> Bool { + return lhs.description == rhs.description + } } public enum SigningAlgorithmType { diff --git a/Test/AwsCommonRuntimeKitTests/auth/SigningConfigTests.swift b/Test/AwsCommonRuntimeKitTests/auth/SigningConfigTests.swift index 3857f0845..3ce8eb696 100644 --- a/Test/AwsCommonRuntimeKitTests/auth/SigningConfigTests.swift +++ b/Test/AwsCommonRuntimeKitTests/auth/SigningConfigTests.swift @@ -29,7 +29,7 @@ class SigningConfigTests: XCBaseTestCase { XCTAssertNotNil(cSigningConfig.credentials) XCTAssertEqual(UInt64(signingConfig.expiration!), cSigningConfig.expiration_in_seconds) XCTAssertEqual(signingConfig.signedBodyHeader.rawValue, cSigningConfig.signed_body_header) - XCTAssertEqual(signingConfig.signedBodyValue.rawValue, cSigningConfig.signed_body_value.toString()) + XCTAssertEqual(signingConfig.signedBodyValue.description, cSigningConfig.signed_body_value.toString()) } } } From 0829d20f252018e776319a0691e715bedb8d9b22 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 3 Jul 2024 09:53:28 -0700 Subject: [PATCH 2/2] feat: Add `fetch_metrics()` for HTTP connection managers (#273) Co-authored-by: Steven Yuan Co-authored-by: Dengke Tang <815825145@qq.com> --- Package.swift | 6 ++++- .../http/HTTP2StreamManager.swift | 11 ++++++++ .../http/HTTPClientConnectionManager.swift | 11 ++++++++ .../HTTPClientConnectionManagerMetrics.swift | 26 +++++++++++++++++++ .../http/HTTP2StreamManagerTests.swift | 3 +++ .../http/HTTPTests.swift | 9 ++++--- aws-common-runtime/aws-c-cal | 2 +- aws-common-runtime/aws-c-common | 2 +- aws-common-runtime/s2n | 2 +- 9 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 Source/AwsCommonRuntimeKit/http/HTTPClientConnectionManagerMetrics.swift diff --git a/Package.swift b/Package.swift index 97cf85c96..d67f7cafb 100644 --- a/Package.swift +++ b/Package.swift @@ -38,6 +38,10 @@ awsCCommonPlatformExcludes.append("source/arch/arm") #if !os(Windows) awsCCommonPlatformExcludes.append("source/windows") #endif +let cSettingsCommon: [CSetting] = [ + .headerSearchPath("source/external/libcbor"), + .define("DEBUG_BUILD", .when(configuration: .debug)) +] ////////////////////////////////////////////////////////////////////// /// aws-c-cal @@ -198,7 +202,7 @@ packageTargets.append(contentsOf: [ dependencies: ["AwsCPlatformConfig"], path: "aws-common-runtime/aws-c-common", exclude: awsCCommonPlatformExcludes, - cSettings: cSettings + cSettings: cSettingsCommon ), .target( name: "AwsCSdkUtils", diff --git a/Source/AwsCommonRuntimeKit/http/HTTP2StreamManager.swift b/Source/AwsCommonRuntimeKit/http/HTTP2StreamManager.swift index e8af6e168..34d4e3e8b 100644 --- a/Source/AwsCommonRuntimeKit/http/HTTP2StreamManager.swift +++ b/Source/AwsCommonRuntimeKit/http/HTTP2StreamManager.swift @@ -42,6 +42,17 @@ public class HTTP2StreamManager { }) } + /// Fetch the current manager metrics from connection manager. + public func fetchMetrics() -> HTTPClientConnectionManagerMetrics { + var cManagerMetrics = aws_http_manager_metrics() + aws_http2_stream_manager_fetch_metrics(rawValue, &cManagerMetrics) + return HTTPClientConnectionManagerMetrics( + availableConcurrency: cManagerMetrics.available_concurrency, + pendingConcurrencyAcquires: cManagerMetrics.pending_concurrency_acquires, + leasedConcurrency: cManagerMetrics.leased_concurrency + ) + } + deinit { aws_http2_stream_manager_release(rawValue) } diff --git a/Source/AwsCommonRuntimeKit/http/HTTPClientConnectionManager.swift b/Source/AwsCommonRuntimeKit/http/HTTPClientConnectionManager.swift index 3eb8e243d..0d2f5e543 100644 --- a/Source/AwsCommonRuntimeKit/http/HTTPClientConnectionManager.swift +++ b/Source/AwsCommonRuntimeKit/http/HTTPClientConnectionManager.swift @@ -39,6 +39,17 @@ public class HTTPClientConnectionManager { } } + /// Fetch the current manager metrics from connection manager. + public func fetchMetrics() -> HTTPClientConnectionManagerMetrics { + var cManagerMetrics = aws_http_manager_metrics() + aws_http_connection_manager_fetch_metrics(rawValue, &cManagerMetrics) + return HTTPClientConnectionManagerMetrics( + availableConcurrency: cManagerMetrics.available_concurrency, + pendingConcurrencyAcquires: cManagerMetrics.pending_concurrency_acquires, + leasedConcurrency: cManagerMetrics.leased_concurrency + ) + } + deinit { aws_http_connection_manager_release(rawValue) } diff --git a/Source/AwsCommonRuntimeKit/http/HTTPClientConnectionManagerMetrics.swift b/Source/AwsCommonRuntimeKit/http/HTTPClientConnectionManagerMetrics.swift new file mode 100644 index 000000000..edf3e9610 --- /dev/null +++ b/Source/AwsCommonRuntimeKit/http/HTTPClientConnectionManagerMetrics.swift @@ -0,0 +1,26 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0. + +public struct HTTPClientConnectionManagerMetrics { + /// The number of additional concurrent requests that can be supported by the HTTP manager without needing to + /// establish additional connections to the target server. + /// + /// For connection manager, it equals to connections that's idle. + /// For stream manager, it equals to the number of streams that are possible to be made without creating new + /// connection, although the implementation can create new connection without fully filling it. + public var availableConcurrency: Int + /// The number of requests that are awaiting concurrency to be made available from the HTTP manager. + public var pendingConcurrencyAcquires: Int + /// The number of connections (HTTP/1.1) or streams (for HTTP/2 via. stream manager) currently vended to user. + public var leasedConcurrency: Int + + public init( + availableConcurrency: Int, + pendingConcurrencyAcquires: Int, + leasedConcurrency: Int + ) { + self.availableConcurrency = availableConcurrency + self.pendingConcurrencyAcquires = pendingConcurrencyAcquires + self.leasedConcurrency = leasedConcurrency + } +} diff --git a/Test/AwsCommonRuntimeKitTests/http/HTTP2StreamManagerTests.swift b/Test/AwsCommonRuntimeKitTests/http/HTTP2StreamManagerTests.swift index 5397a7372..110a60761 100644 --- a/Test/AwsCommonRuntimeKitTests/http/HTTP2StreamManagerTests.swift +++ b/Test/AwsCommonRuntimeKitTests/http/HTTP2StreamManagerTests.swift @@ -125,6 +125,9 @@ class HTT2StreamManagerTests: HTTPClientTestFixture { let stream = try await streamManager.acquireStream(requestOptions: http2RequestOptions) XCTAssertFalse(onCompleteCalled) + let metrics = streamManager.fetchMetrics() + XCTAssertTrue(metrics.availableConcurrency > 0) + XCTAssertTrue(metrics.leasedConcurrency > 0) let data = TEST_DOC_LINE.data(using: .utf8)! for chunk in data.chunked(into: 5) { try await stream.writeChunk(chunk: chunk, endOfStream: false) diff --git a/Test/AwsCommonRuntimeKitTests/http/HTTPTests.swift b/Test/AwsCommonRuntimeKitTests/http/HTTPTests.swift index 2f9d02723..c7c6ef796 100644 --- a/Test/AwsCommonRuntimeKitTests/http/HTTPTests.swift +++ b/Test/AwsCommonRuntimeKitTests/http/HTTPTests.swift @@ -15,7 +15,7 @@ class HTTPTests: HTTPClientTestFixture { _ = try await sendHTTPRequest(method: "GET", endpoint: host, path: getPath, connectionManager: connectionManager) _ = try await sendHTTPRequest(method: "GET", endpoint: host, path: "/delete", expectedStatus: 404, connectionManager: connectionManager) } - + func testGetHTTPSRequestWithUtf8Header() async throws { let connectionManager = try await getHttpConnectionManager(endpoint: host, ssh: true, port: 443) let utf8Header = HTTPHeader(name: "TestHeader", value: "TestValueWithEmoji🤯") @@ -64,6 +64,9 @@ class HTTPTests: HTTPClientTestFixture { let streamBase = try connection.makeRequest(requestOptions: httpRequestOptions) try streamBase.activate() XCTAssertFalse(onCompleteCalled) + let metrics = connectionManager.fetchMetrics() + XCTAssertTrue(metrics.leasedConcurrency > 0) + let data = TEST_DOC_LINE.data(using: .utf8)! for chunk in data.chunked(into: 5) { try await streamBase.writeChunk(chunk: chunk, endOfStream: false) @@ -118,7 +121,7 @@ class HTTPTests: HTTPClientTestFixture { // Sleep for 5 seconds to make sure onComplete is not triggerred try await Task.sleep(nanoseconds: 5_000_000_000) XCTAssertFalse(onCompleteCalled) - + let lastChunkData = Data("last chunk data".utf8) try await streamBase.writeChunk(chunk: lastChunkData, endOfStream: true) semaphore.wait() @@ -135,7 +138,7 @@ class HTTPTests: HTTPClientTestFixture { XCTAssertEqual(body.data, TEST_DOC_LINE + String(decoding: lastChunkData, as: UTF8.self)) } - + func testHTTPStreamIsReleasedIfNotActivated() async throws { do { let httpRequestOptions = try getHTTPRequestOptions(method: "GET", endpoint: host, path: getPath) diff --git a/aws-common-runtime/aws-c-cal b/aws-common-runtime/aws-c-cal index 96c47e339..11fc68445 160000 --- a/aws-common-runtime/aws-c-cal +++ b/aws-common-runtime/aws-c-cal @@ -1 +1 @@ -Subproject commit 96c47e339d030d1fa4eaca201be948bc4442510d +Subproject commit 11fc68445b2b4993656ed720fc2788f3c4c7c20f diff --git a/aws-common-runtime/aws-c-common b/aws-common-runtime/aws-c-common index 4f874cea5..6d974f92c 160000 --- a/aws-common-runtime/aws-c-common +++ b/aws-common-runtime/aws-c-common @@ -1 +1 @@ -Subproject commit 4f874cea50a70bc6ebcd85c6ce1c6c0016b5aff4 +Subproject commit 6d974f92c1d86391c1dcb1173239adf757c52b2d diff --git a/aws-common-runtime/s2n b/aws-common-runtime/s2n index 114ccab0f..073c7b415 160000 --- a/aws-common-runtime/s2n +++ b/aws-common-runtime/s2n @@ -1 +1 @@ -Subproject commit 114ccab0ff2cde491203ac841837d0d39b767412 +Subproject commit 073c7b415a17d271a7b2c8c385d0e641fc94871f