Skip to content

Commit d208926

Browse files
committed
Refactor NIO HTTP Client to SwiftNIO prefix in its own target
1 parent 4622ab2 commit d208926

12 files changed

+87
-74
lines changed

Package.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ let package = Package(
5353
.library(name: "SmithyCBOR", targets: ["SmithyCBOR"]),
5454
.library(name: "SmithyWaitersAPI", targets: ["SmithyWaitersAPI"]),
5555
.library(name: "SmithyTestUtil", targets: ["SmithyTestUtil"]),
56+
.library(name: "SmithySwiftNIO", targets: ["SmithySwiftNIO"]),
5657
],
5758
dependencies: {
5859
var dependencies: [Package.Dependency] = [
@@ -98,7 +99,6 @@ let package = Package(
9899
"SmithyChecksums",
99100
"SmithyCBOR",
100101
.product(name: "AwsCommonRuntimeKit", package: "aws-crt-swift"),
101-
.product(name: "AsyncHTTPClient", package: "async-http-client"),
102102
// Only include these on macOS, iOS, tvOS, watchOS, and macCatalyst (visionOS and Linux are excluded)
103103
.product(
104104
name: "InMemoryExporter",
@@ -125,6 +125,18 @@ let package = Package(
125125
.copy("PrivacyInfo.xcprivacy")
126126
]
127127
),
128+
.target(
129+
name: "SmithySwiftNIO",
130+
dependencies: [
131+
"Smithy",
132+
"SmithyHTTPAPI",
133+
"SmithyHTTPClient",
134+
"SmithyStreams",
135+
"ClientRuntime",
136+
.product(name: "AsyncHTTPClient", package: "async-http-client"),
137+
],
138+
path: "Sources/SmithySwiftNIO"
139+
),
128140
.target(
129141
name: "SmithyRetriesAPI"
130142
),
@@ -270,6 +282,13 @@ let package = Package(
270282
],
271283
resources: [ .process("Resources") ]
272284
),
285+
.testTarget(
286+
name: "SmithySwiftNIOTests",
287+
dependencies: [
288+
"SmithySwiftNIO",
289+
"SmithyTestUtil",
290+
]
291+
),
273292
.testTarget(
274293
name: "SmithyCBORTests",
275294
dependencies: ["SmithyCBOR", "ClientRuntime", "SmithyTestUtil"]

Sources/ClientRuntime/Networking/Http/HttpTelemetry.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,21 @@ public class HttpTelemetry: @unchecked Sendable {
3333
return attributes
3434
}
3535

36-
internal let contextManager: any TelemetryContextManager
37-
internal let tracerProvider: any TracerProvider
38-
internal let loggerProvider: any LoggerProvider
36+
public let contextManager: any TelemetryContextManager
37+
public let tracerProvider: any TracerProvider
38+
public let loggerProvider: any LoggerProvider
3939

40-
internal let tracerScope: String
41-
internal let spanName: String
40+
public let tracerScope: String
41+
public let spanName: String
4242

43-
internal let connectionsAcquireDuration: any Histogram
43+
public let connectionsAcquireDuration: any Histogram
4444
private let connectionsLimit: any AsyncMeasurementHandle
4545
private let connectionsUsage: any AsyncMeasurementHandle
46-
internal let connectionsUptime: any Histogram
46+
public let connectionsUptime: any Histogram
4747
private let requestsUsage: any AsyncMeasurementHandle
48-
internal let requestsQueuedDuration: any Histogram
49-
internal let bytesSent: any MonotonicCounter
50-
internal let bytesReceived: any MonotonicCounter
48+
public let requestsQueuedDuration: any Histogram
49+
public let bytesSent: any MonotonicCounter
50+
public let bytesReceived: any MonotonicCounter
5151

5252
// Lock to enforce exclusive access to non-Sendable properties
5353
private let lock = NSRecursiveLock()
@@ -64,7 +64,7 @@ public class HttpTelemetry: @unchecked Sendable {
6464

6565
private let _spanAttributes: Attributes?
6666

67-
var spanAttributes: Attributes? {
67+
public var spanAttributes: Attributes? {
6868
lock.lock()
6969
defer { lock.unlock() }
7070
return _spanAttributes
@@ -78,7 +78,7 @@ public class HttpTelemetry: @unchecked Sendable {
7878
return _httpMetricsUsage
7979
}
8080

81-
func updateHTTPMetricsUsage(_ block: (inout HttpMetricsUsage) -> Void) {
81+
public func updateHTTPMetricsUsage(_ block: (inout HttpMetricsUsage) -> Void) {
8282
lock.lock()
8383
defer { lock.unlock() }
8484
block(&_httpMetricsUsage)
@@ -193,7 +193,7 @@ internal enum HttpMetricsAttributesKeys {
193193
internal static let serverAddress = AttributeKey<String>(name: "server.address")
194194
}
195195

196-
internal struct HttpMetricsUsage {
196+
public struct HttpMetricsUsage {
197197
public var connectionsLimit: Int = 0
198198
public var idleConnections: Int = 0
199199
public var acquiredConnections: Int = 0

Sources/ClientRuntime/Networking/Http/CRT/TLSConfiguration.swift renamed to Sources/ClientRuntime/Networking/Http/TLSConfiguration.swift

File renamed without changes.

Sources/SmithyHTTPClient/SdkHttpRequest+CRT.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ extension HTTPRequest {
1818
httpRequest.path = [endpoint.path, endpoint.uri.queryString].compactMap { $0 }.joined(separator: "?")
1919
httpRequest.addHeaders(headers: headers.toHttpHeaders())
2020
httpRequest.body = isChunked ? nil : StreamableHttpBody(body: body) // body needs to be nil to use writeChunk()
21+
httpRequest.addHeader(name: "If-Range", value: "ETAG")
2122
return httpRequest
2223
}
2324

@@ -29,6 +30,7 @@ extension HTTPRequest {
2930
httpRequest.method = method.rawValue
3031
httpRequest.path = [endpoint.path, endpoint.uri.queryString].compactMap { $0 }.joined(separator: "?")
3132
httpRequest.addHeaders(headers: headers.toHttpHeaders())
33+
httpRequest.addHeader(name: "If-Range", value: "ETAG")
3234

3335
// Remove the "Transfer-Encoding" header if it exists since h2 does not support it
3436
httpRequest.removeHeader(name: "Transfer-Encoding")

Sources/ClientRuntime/Networking/Http/NIO/NIOHTTPClient.swift renamed to Sources/SmithySwiftNIO/SwiftNIOHTTPClient.swift

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,57 +10,47 @@ import NIOCore
1010
import NIOHTTP1
1111
import NIOPosix
1212
import NIOSSL
13-
import struct Smithy.Attributes
14-
import struct Smithy.SwiftLogger
15-
import protocol Smithy.LogAgent
16-
import struct SmithyHTTPAPI.Headers
17-
import struct SmithyHTTPAPI.Header
18-
import protocol SmithyHTTPAPI.HTTPClient
19-
import class SmithyHTTPAPI.HTTPResponse
20-
import class SmithyHTTPAPI.HTTPRequest
21-
import enum SmithyHTTPAPI.HTTPStatusCode
22-
import enum SmithyHTTPAPI.HTTPMethodType
23-
import protocol Smithy.ReadableStream
24-
import enum Smithy.ByteStream
25-
import class SmithyStreams.BufferedStream
13+
import Smithy
14+
import SmithyHTTPAPI
15+
import SmithyStreams
16+
import ClientRuntime
2617
import struct Foundation.Date
2718
import struct Foundation.URLComponents
2819
import struct Foundation.URLQueryItem
29-
import AwsCommonRuntimeKit
3020

3121
/// AsyncHTTPClient-based HTTP client implementation that conforms to SmithyHTTPAPI.HTTPClient
3222
/// This implementation is thread-safe and supports concurrent request execution.
33-
public final class NIOHTTPClient: SmithyHTTPAPI.HTTPClient {
34-
public static let noOpNIOHTTPClientTelemetry = HttpTelemetry(
35-
httpScope: "NIOHTTPClient",
36-
telemetryProvider: DefaultTelemetry.provider
23+
public final class SwiftNIOHTTPClient: SmithyHTTPAPI.HTTPClient {
24+
public static let noOpSwiftNIOHTTPClientTelemetry = ClientRuntime.HttpTelemetry(
25+
httpScope: "SwiftNIOHTTPClient",
26+
telemetryProvider: ClientRuntime.DefaultTelemetry.provider
3727
)
3828

3929
private let client: AsyncHTTPClient.HTTPClient
40-
private let config: HttpClientConfiguration
41-
private let tlsConfiguration: NIOHTTPClientTLSOptions?
30+
private let config: ClientRuntime.HttpClientConfiguration
31+
private let tlsConfiguration: SwiftNIOHTTPClientTLSOptions?
4232
private let allocator: ByteBufferAllocator
4333

4434
/// HTTP Client Telemetry
45-
private let telemetry: HttpTelemetry
35+
private let telemetry: ClientRuntime.HttpTelemetry
4636

4737
/// Logger for HTTP-related events.
4838
private var logger: LogAgent
4939

50-
/// Creates a new `NIOHTTPClient`.
40+
/// Creates a new `SwiftNIOHTTPClient`.
5141
///
5242
/// The client is created with its own internal `AsyncHTTPClient`, which is configured with system defaults.
5343
/// - Parameters:
5444
/// - httpClientConfiguration: The configuration to use for the client's `AsyncHTTPClient` setup.
5545
/// - eventLoopGroup: The `EventLoopGroup` that the ``HTTPClient`` will use.
5646
public init(
57-
httpClientConfiguration: HttpClientConfiguration,
47+
httpClientConfiguration: ClientRuntime.HttpClientConfiguration,
5848
eventLoopGroup: (any NIOCore.EventLoopGroup)? = nil
5949
) {
6050
self.config = httpClientConfiguration
61-
self.telemetry = httpClientConfiguration.telemetry ?? NIOHTTPClient.noOpNIOHTTPClientTelemetry
62-
self.logger = self.telemetry.loggerProvider.getLogger(name: "NIOHTTPClient")
63-
self.tlsConfiguration = httpClientConfiguration.tlsConfiguration as? NIOHTTPClientTLSOptions
51+
self.telemetry = httpClientConfiguration.telemetry ?? SwiftNIOHTTPClient.noOpSwiftNIOHTTPClientTelemetry
52+
self.logger = self.telemetry.loggerProvider.getLogger(name: "SwiftNIOHTTPClient")
53+
self.tlsConfiguration = httpClientConfiguration.tlsConfiguration as? SwiftNIOHTTPClientTLSOptions
6454
self.allocator = ByteBufferAllocator()
6555

6656
var clientConfig = AsyncHTTPClient.HTTPClient.Configuration.from(
@@ -154,7 +144,7 @@ public final class NIOHTTPClient: SmithyHTTPAPI.HTTPClient {
154144

155145
let httpMethod = request.method.rawValue
156146
let url = request.destination.url
157-
logger.debug("NIOHTTPClient(\(httpMethod) \(String(describing: url))) started")
147+
logger.debug("SwiftNIOHTTPClient(\(httpMethod) \(String(describing: url))) started")
158148
logBodyDescription(request.body)
159149

160150
do {
@@ -168,16 +158,16 @@ public final class NIOHTTPClient: SmithyHTTPAPI.HTTPClient {
168158
headers.add(name: name, value: value)
169159
}
170160

171-
let body = await NIOHTTPClientStreamBridge.convertResponseBody(from: nioResponse)
161+
let body = await SwiftNIOHTTPClientStreamBridge.convertResponseBody(from: nioResponse)
172162

173163
let response = HTTPResponse(headers: headers, body: body, statusCode: statusCode)
174-
logger.debug("NIOHTTPClient(\(httpMethod) \(String(describing: url))) succeeded")
164+
logger.debug("SwiftNIOHTTPClient(\(httpMethod) \(String(describing: url))) succeeded")
175165

176166
return response
177167
} catch {
178168
let urlDescription = String(describing: url)
179169
let errorDescription = String(describing: error)
180-
logger.error("NIOHTTPClient(\(httpMethod) \(urlDescription)) failed with error: \(errorDescription)")
170+
logger.error("SwiftNIOHTTPClient(\(httpMethod) \(urlDescription)) failed with error: \(errorDescription)")
181171
throw error
182172
}
183173
}
@@ -196,7 +186,7 @@ public final class NIOHTTPClient: SmithyHTTPAPI.HTTPClient {
196186
URLQueryItem(name: $0.name, value: $0.value)
197187
}
198188
}
199-
guard let url = components.url else { throw NIOHTTPClientError.incompleteHTTPRequest }
189+
guard let url = components.url else { throw SwiftNIOHTTPClientError.incompleteHTTPRequest }
200190

201191
let method = NIOHTTP1.HTTPMethod(rawValue: request.method.rawValue)
202192
var nioRequest = AsyncHTTPClient.HTTPClientRequest(url: url.absoluteString)
@@ -209,7 +199,7 @@ public final class NIOHTTPClient: SmithyHTTPAPI.HTTPClient {
209199
}
210200
}
211201

212-
nioRequest.body = try await NIOHTTPClientStreamBridge.convertRequestBody(
202+
nioRequest.body = try await SwiftNIOHTTPClientStreamBridge.convertRequestBody(
213203
from: request.body,
214204
allocator: allocator
215205
)

Sources/ClientRuntime/Networking/Http/NIO/NIOHTTPClientConfiguration+HTTPClientConfiguration.swift renamed to Sources/SmithySwiftNIO/SwiftNIOHTTPClientConfiguration+HTTPClientConfiguration.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77

88
import AsyncHTTPClient
99
import NIOCore
10+
import ClientRuntime
1011

1112
extension HTTPClient.Configuration {
1213

13-
static func from(httpClientConfiguration: HttpClientConfiguration) -> HTTPClient.Configuration {
14+
static func from(httpClientConfiguration: ClientRuntime.HttpClientConfiguration) -> HTTPClient.Configuration {
1415
let connect: TimeAmount? = httpClientConfiguration.connectTimeout != nil
1516
? .seconds(Int64(httpClientConfiguration.connectTimeout!))
1617
: nil

Sources/ClientRuntime/Networking/Http/NIO/NIOHTTPClientError.swift renamed to Sources/SmithySwiftNIO/SwiftNIOHTTPClientError.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//
77

88
/// Errors that are particular to the NIO-based Smithy HTTP client.
9-
public enum NIOHTTPClientError: Error {
9+
public enum SwiftNIOHTTPClientError: Error {
1010

1111
/// A URL could not be formed from the `HTTPRequest`.
1212
/// Please file a bug with aws-sdk-swift if you experience this error.

Sources/ClientRuntime/Networking/Http/NIO/NIOHTTPClientStreamBridge.swift renamed to Sources/SmithySwiftNIO/SwiftNIOHTTPClientStreamBridge.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Smithy
1212
import SmithyStreams
1313

1414
/// Handles streaming between Smithy streams and AsyncHTTPClient
15-
final class NIOHTTPClientStreamBridge {
15+
final class SwiftNIOHTTPClientStreamBridge {
1616

1717
/// Convert Smithy ByteStream to AsyncHTTPClient request body
1818
static func convertRequestBody(
@@ -130,7 +130,7 @@ internal struct StreamToAsyncSequence: AsyncSequence, Sendable {
130130
} catch {
131131
isFinished = true
132132
stream.close()
133-
throw NIOHTTPClientError.streamingError(error)
133+
throw SwiftNIOHTTPClientError.streamingError(error)
134134
}
135135
}
136136
}

Sources/ClientRuntime/Networking/Http/NIO/NIOHTTPClientTLSOptions.swift renamed to Sources/SmithySwiftNIO/SwiftNIOHTTPClientTLSOptions.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77

88
import Foundation
99
import NIOSSL
10+
import ClientRuntime
1011

11-
public struct NIOHTTPClientTLSOptions: TLSConfiguration, Sendable {
12+
public struct SwiftNIOHTTPClientTLSOptions: ClientRuntime.TLSConfiguration, Sendable {
1213

1314
/// Optional path to a PEM certificate
1415
public var certificate: String?

Sources/ClientRuntime/Networking/Http/NIO/NIOHTTPClientTLSResolverUtils.swift renamed to Sources/SmithySwiftNIO/SwiftNIOHTTPClientTLSResolverUtils.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,30 @@
88
import Foundation
99
import NIOSSL
1010

11-
extension NIOHTTPClientTLSOptions {
11+
extension SwiftNIOHTTPClientTLSOptions {
1212

1313
func makeNIOSSLConfiguration() throws -> NIOSSL.TLSConfiguration {
1414
var tlsConfig = NIOSSL.TLSConfiguration.makeClientConfiguration()
1515

1616
if useSelfSignedCertificate {
1717
if let certificateDir = certificateDir, let certificate = certificate {
1818
let certificatePath = "\(certificateDir)/\(certificate)"
19-
let certificates = try NIOHTTPClientTLSOptions.loadCertificates(from: certificatePath)
19+
let certificates = try SwiftNIOHTTPClientTLSOptions.loadCertificates(from: certificatePath)
2020
tlsConfig.trustRoots = .certificates(certificates)
2121
} else if let certificate = certificate {
22-
let certificates = try NIOHTTPClientTLSOptions.loadCertificates(from: certificate)
22+
let certificates = try SwiftNIOHTTPClientTLSOptions.loadCertificates(from: certificate)
2323
tlsConfig.trustRoots = .certificates(certificates)
2424
}
2525
}
2626

2727
if useProvidedKeystore {
2828
if let pkcs12Path = pkcs12Path, let pkcs12Password = pkcs12Password {
29-
let bundle = try NIOHTTPClientTLSOptions.loadPKCS12Bundle(from: pkcs12Path, password: pkcs12Password)
29+
let bundle = try SwiftNIOHTTPClientTLSOptions.loadPKCS12Bundle(from: pkcs12Path, password: pkcs12Password)
3030
tlsConfig.certificateChain = bundle.certificateChain.map { .certificate($0) }
3131
tlsConfig.privateKey = .privateKey(bundle.privateKey)
3232
} else if let certificate = certificate, let privateKey = privateKey {
33-
let cert = try NIOHTTPClientTLSOptions.loadCertificate(from: certificate)
34-
let key = try NIOHTTPClientTLSOptions.loadPrivateKey(from: privateKey)
33+
let cert = try SwiftNIOHTTPClientTLSOptions.loadCertificate(from: certificate)
34+
let key = try SwiftNIOHTTPClientTLSOptions.loadPrivateKey(from: privateKey)
3535
tlsConfig.certificateChain = [.certificate(cert)]
3636
tlsConfig.privateKey = .privateKey(key)
3737
}
@@ -41,7 +41,7 @@ extension NIOHTTPClientTLSOptions {
4141
}
4242
}
4343

44-
extension NIOHTTPClientTLSOptions {
44+
extension SwiftNIOHTTPClientTLSOptions {
4545

4646
static func loadCertificates(from filePath: String) throws -> [NIOSSLCertificate] {
4747
let fileData = try Data(contentsOf: URL(fileURLWithPath: filePath))
@@ -51,7 +51,7 @@ extension NIOHTTPClientTLSOptions {
5151
static func loadCertificate(from filePath: String) throws -> NIOSSLCertificate {
5252
let certificates = try loadCertificates(from: filePath)
5353
guard let certificate = certificates.first else {
54-
throw NIOHTTPClientTLSError.noCertificateFound(filePath)
54+
throw SwiftNIOHTTPClientTLSError.noCertificateFound(filePath)
5555
}
5656
return certificate
5757
}
@@ -65,12 +65,12 @@ extension NIOHTTPClientTLSOptions {
6565
do {
6666
return try NIOSSLPKCS12Bundle(file: filePath, passphrase: password.utf8)
6767
} catch {
68-
throw NIOHTTPClientTLSError.invalidPKCS12(filePath, underlying: error)
68+
throw SwiftNIOHTTPClientTLSError.invalidPKCS12(filePath, underlying: error)
6969
}
7070
}
7171
}
7272

73-
public enum NIOHTTPClientTLSError: Error, LocalizedError {
73+
public enum SwiftNIOHTTPClientTLSError: Error, LocalizedError {
7474
case noCertificateFound(String)
7575
case invalidPKCS12(String, underlying: Error)
7676

0 commit comments

Comments
 (0)