Skip to content

Commit 28d7cff

Browse files
committed
HTTPClientRequest: allow custom TLS config
1 parent 16f7e62 commit 28d7cff

File tree

5 files changed

+58
-4
lines changed

5 files changed

+58
-4
lines changed

Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ extension HTTPClient {
7878
// this loop is there to follow potential redirects
7979
while true {
8080
let preparedRequest = try HTTPClientRequest.Prepared(currentRequest, dnsOverride: configuration.dnsOverride)
81-
let response = try await executeCancellable(preparedRequest, deadline: deadline, logger: logger)
81+
let response = try await self.executeCancellable(preparedRequest, deadline: deadline, logger: logger)
8282

8383
guard var redirectState = currentRedirectState else {
8484
// a `nil` redirectState means we should not follow redirects

Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import struct Foundation.URL
1616
import NIOCore
1717
import NIOHTTP1
18+
import NIOSSL
1819

1920
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
2021
extension HTTPClientRequest {
@@ -37,6 +38,7 @@ extension HTTPClientRequest {
3738
var requestFramingMetadata: RequestFramingMetadata
3839
var head: HTTPRequestHead
3940
var body: Body?
41+
var tlsConfiguration: TLSConfiguration?
4042
}
4143
}
4244

@@ -58,15 +60,16 @@ extension HTTPClientRequest.Prepared {
5860

5961
self.init(
6062
url: url,
61-
poolKey: .init(url: deconstructedURL, tlsConfiguration: nil, dnsOverride: dnsOverride),
63+
poolKey: .init(url: deconstructedURL, tlsConfiguration: request.tlsConfiguration, dnsOverride: dnsOverride),
6264
requestFramingMetadata: metadata,
6365
head: .init(
6466
version: .http1_1,
6567
method: request.method,
6668
uri: deconstructedURL.uri,
6769
headers: headers
6870
),
69-
body: request.body.map { .init($0) }
71+
body: request.body.map { .init($0) },
72+
tlsConfiguration: request.tlsConfiguration
7073
)
7174
}
7275
}

Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414

1515
import NIOCore
1616
import NIOHTTP1
17+
import NIOSSL
1718

1819
/// A representation of an HTTP request for the Swift Concurrency HTTPClient API.
1920
///
2021
/// This object is similar to ``HTTPClient/Request``, but used for the Swift Concurrency API.
22+
///
23+
/// - note: For many ``HTTPClientRequest/body-swift.property`` configurations, this type is _not_ a value type
24+
/// (https://github.com/swift-server/async-http-client/issues/708).
2125
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
2226
public struct HTTPClientRequest: Sendable {
2327
/// The request URL, including scheme, hostname, and optionally port.
@@ -32,11 +36,15 @@ public struct HTTPClientRequest: Sendable {
3236
/// The request body, if any.
3337
public var body: Body?
3438

39+
/// Request-specific TLS configuration, defaults to no request-specific TLS configuration.
40+
public var tlsConfiguration: TLSConfiguration?
41+
3542
public init(url: String) {
3643
self.url = url
3744
self.method = .GET
3845
self.headers = .init()
3946
self.body = .none
47+
self.tlsConfiguration = nil
4048
}
4149
}
4250

Sources/AsyncHTTPClient/AsyncAwait/Transaction.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ import NIOSSL
146146
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
147147
extension Transaction: HTTPSchedulableRequest {
148148
var poolKey: ConnectionPool.Key { self.request.poolKey }
149-
var tlsConfiguration: TLSConfiguration? { return nil }
149+
var tlsConfiguration: TLSConfiguration? { return self.request.tlsConfiguration }
150150
var requiredEventLoop: EventLoop? { return nil }
151151

152152
func requestWasQueued(_ scheduler: HTTPRequestScheduler) {

Tests/AsyncHTTPClientTests/HTTPClientTests.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3525,4 +3525,47 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass {
35253525
let response = try client.get(url: self.defaultHTTPBinURLPrefix + "get").wait()
35263526
XCTAssertEqual(.ok, response.status)
35273527
}
3528+
3529+
func testAsyncExecuteWithCustomTLS() async throws {
3530+
let httpsBin = HTTPBin(.http1_1(ssl: true))
3531+
defer {
3532+
XCTAssertNoThrow(try httpsBin.shutdown())
3533+
}
3534+
3535+
// A client with default TLS settings, i.e. it won't accept `httpsBin`'s fake self-signed cert
3536+
let client = HTTPClient(eventLoopGroup: MultiThreadedEventLoopGroup.singleton)
3537+
defer {
3538+
XCTAssertNoThrow(try client.shutdown().wait())
3539+
}
3540+
3541+
var request = HTTPClientRequest(url: "https://localhost:\(httpsBin.port)/get")
3542+
3543+
// For now, let's allow bad TLS certs
3544+
request.tlsConfiguration = TLSConfiguration.clientDefault
3545+
request.tlsConfiguration!.certificateVerification = .none
3546+
3547+
let response1 = try await client.execute(request, timeout: .hours(99))
3548+
XCTAssertEqual(.ok, response1.status)
3549+
3550+
// For the second request, we reset the TLS config
3551+
request.tlsConfiguration = nil
3552+
do {
3553+
let response2 = try await client.execute(request, timeout: .hours(99))
3554+
XCTFail("shouldn't succeed, self-signed cert: \(response2)")
3555+
} catch {
3556+
switch error as? NIOSSLError {
3557+
case .some(.handshakeFailed(_)):
3558+
() // ok
3559+
default:
3560+
XCTFail("unexpected error: \(error)")
3561+
}
3562+
}
3563+
3564+
// And finally we allow it again.
3565+
request.tlsConfiguration = TLSConfiguration.clientDefault
3566+
request.tlsConfiguration!.certificateVerification = .none
3567+
3568+
let response3 = try await client.execute(request, timeout: .hours(99))
3569+
XCTAssertEqual(.ok, response3.status)
3570+
}
35283571
}

0 commit comments

Comments
 (0)