Skip to content

Commit d766674

Browse files
authored
HTTPClientRequest: allow custom TLS config (#709)
1 parent 4c07d3b commit d766674

File tree

5 files changed

+60
-4
lines changed

5 files changed

+60
-4
lines changed

Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift

+1-1
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

+5-2
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

+8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import Algorithms
1616
import NIOCore
1717
import NIOHTTP1
18+
import NIOSSL
1819

1920
@usableFromInline
2021
let bagOfBytesToByteBufferConversionChunkSize = 1024 * 1024 * 4
@@ -32,6 +33,9 @@ let byteBufferMaxSize = Int(UInt32.max)
3233
/// A representation of an HTTP request for the Swift Concurrency HTTPClient API.
3334
///
3435
/// This object is similar to ``HTTPClient/Request``, but used for the Swift Concurrency API.
36+
///
37+
/// - note: For many ``HTTPClientRequest/body-swift.property`` configurations, this type is _not_ a value type
38+
/// (https://github.com/swift-server/async-http-client/issues/708).
3539
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
3640
public struct HTTPClientRequest: Sendable {
3741
/// The request URL, including scheme, hostname, and optionally port.
@@ -46,11 +50,15 @@ public struct HTTPClientRequest: Sendable {
4650
/// The request body, if any.
4751
public var body: Body?
4852

53+
/// Request-specific TLS configuration, defaults to no request-specific TLS configuration.
54+
public var tlsConfiguration: TLSConfiguration?
55+
4956
public init(url: String) {
5057
self.url = url
5158
self.method = .GET
5259
self.headers = .init()
5360
self.body = .none
61+
self.tlsConfiguration = nil
5462
}
5563
}
5664

Sources/AsyncHTTPClient/AsyncAwait/Transaction.swift

+1-1
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

+45
Original file line numberDiff line numberDiff line change
@@ -3525,4 +3525,49 @@ 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+
// ! is safe, assigned above
3546+
request.tlsConfiguration!.certificateVerification = .none
3547+
3548+
let response1 = try await client.execute(request, timeout: /* infinity */ .hours(99))
3549+
XCTAssertEqual(.ok, response1.status)
3550+
3551+
// For the second request, we reset the TLS config
3552+
request.tlsConfiguration = nil
3553+
do {
3554+
let response2 = try await client.execute(request, timeout: /* infinity */ .hours(99))
3555+
XCTFail("shouldn't succeed, self-signed cert: \(response2)")
3556+
} catch {
3557+
switch error as? NIOSSLError {
3558+
case .some(.handshakeFailed(_)):
3559+
() // ok
3560+
default:
3561+
XCTFail("unexpected error: \(error)")
3562+
}
3563+
}
3564+
3565+
// And finally we allow it again.
3566+
request.tlsConfiguration = TLSConfiguration.clientDefault
3567+
// ! is safe, assigned above
3568+
request.tlsConfiguration!.certificateVerification = .none
3569+
3570+
let response3 = try await client.execute(request, timeout: /* infinity */ .hours(99))
3571+
XCTAssertEqual(.ok, response3.status)
3572+
}
35283573
}

0 commit comments

Comments
 (0)