Skip to content

Commit 6101e3a

Browse files
committed
Fixed testProxyTLS()
The ssl handler has to be added after the proxy handler has run. Resurrected handshakePromise.
1 parent 632a8a1 commit 6101e3a

File tree

3 files changed

+74
-16
lines changed

3 files changed

+74
-16
lines changed

Sources/AsyncHTTPClient/ConnectionPool.swift

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -370,15 +370,42 @@ final class ConnectionPool {
370370
}
371371
}
372372

373+
// if the configuration includes a proxy and requires TLS, TLS will not have been enabled yet. This
374+
// returns whether we need to call addSSLHandlerIfNeeded().
375+
private func requiresSSLHandler(on eventLoop: EventLoop) -> Bool {
376+
// if a proxy is not set return false, otherwise for non-TS situation return true, if TS is available and
377+
// either we don't have an NIOTSEventLoop or the scheme is HTTPS then return true
378+
if self.configuration.proxy != nil {
379+
#if canImport(Network)
380+
if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
381+
{
382+
if !(eventLoop is NIOTSEventLoop) {
383+
return true
384+
}
385+
if self.key.scheme == .https {
386+
return true
387+
}
388+
} else {
389+
return true
390+
}
391+
#else
392+
return true
393+
#endif
394+
}
395+
return false
396+
}
397+
373398
private func makeConnection(on eventLoop: EventLoop) -> EventLoopFuture<Connection> {
374399
self.activityPrecondition(expected: [.opened])
375400
let address = HTTPClient.resolveAddress(host: self.key.host, port: self.key.port, proxy: self.configuration.proxy)
401+
let requiresTLS = self.key.scheme == .https
376402
let bootstrap : NIOClientTCPBootstrap
377403
do {
378-
bootstrap = try NIOClientTCPBootstrap.makeHTTPClientBootstrapBase(on: eventLoop, host: key.host, port: key.port, requiresTLS: self.key.scheme == .https, configuration: self.configuration)
404+
bootstrap = try NIOClientTCPBootstrap.makeHTTPClientBootstrapBase(on: eventLoop, host: key.host, port: key.port, requiresTLS: requiresTLS, configuration: self.configuration)
379405
} catch {
380406
return eventLoop.makeFailedFuture(error)
381407
}
408+
let handshakePromise = eventLoop.makePromise(of: Void.self)
382409

383410
let channel: EventLoopFuture<Channel>
384411
switch self.key.scheme {
@@ -389,16 +416,25 @@ final class ConnectionPool {
389416
}
390417

391418
return channel.flatMap { channel -> EventLoopFuture<ConnectionPool.Connection> in
392-
393-
channel.pipeline.addHTTPClientHandlers(leftOverBytesStrategy: .forwardBytes).map {
394-
let connection = Connection(key: self.key, channel: channel, parentPool: self.parentPool)
395-
connection.isLeased = true
396-
return connection
419+
if self.requiresSSLHandler(on: eventLoop) {
420+
channel.pipeline.addSSLHandlerIfNeeded(for: self.key, tlsConfiguration: self.configuration.tlsConfiguration, handshakePromise: handshakePromise)
421+
} else {
422+
handshakePromise.succeed(())
423+
}
424+
return handshakePromise.futureResult.flatMap {
425+
channel.pipeline.addHTTPClientHandlers(leftOverBytesStrategy: .forwardBytes)
426+
}.map {
427+
let connection = Connection(key: self.key, channel: channel, parentPool: self.parentPool)
428+
connection.isLeased = true
429+
return connection
397430
}
398431
}.map { connection in
399432
self.configureCloseCallback(of: connection)
400433
return connection
401434
}.flatMapError { error in
435+
// This promise may not have been completed if we reach this
436+
// so we fail it to avoid any leak
437+
handshakePromise.fail(error)
402438
let action = self.parentPool.connectionProvidersLock.withLock {
403439
self.stateLock.withLock {
404440
self.state.failedConnectionAction()

Sources/AsyncHTTPClient/Utils.swift

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,36 +49,61 @@ public final class HTTPClientCopyingDelegate: HTTPClientResponseDelegate {
4949
}
5050

5151
extension ClientBootstrap {
52-
fileprivate static func makeBootstrap(on eventLoop: EventLoop, host: String, requiresTLS: Bool, configuration: HTTPClient.Configuration) throws -> NIOClientTCPBootstrap {
52+
fileprivate static func makeBootstrap(
53+
on eventLoop: EventLoop,
54+
host: String,
55+
requiresTLS: Bool,
56+
configuration: HTTPClient.Configuration
57+
) throws -> NIOClientTCPBootstrap {
5358
let tlsConfiguration = configuration.tlsConfiguration ?? TLSConfiguration.forClient()
5459
let sslContext = try NIOSSLContext(configuration: tlsConfiguration)
55-
let tlsProvider = try NIOSSLClientTLSProvider<ClientBootstrap>(context: sslContext, serverHostname: (!requiresTLS || host.isIPAddress) ? nil : host)
60+
let hostname = (!requiresTLS || host.isIPAddress) ? nil : host
61+
let tlsProvider = try NIOSSLClientTLSProvider<ClientBootstrap>(context: sslContext, serverHostname: hostname)
5662
return NIOClientTCPBootstrap(ClientBootstrap(group: eventLoop), tls: tlsProvider)
5763
}
5864
}
5965

6066
extension NIOClientTCPBootstrap {
6167
/// create a TCP Bootstrap based off what type of `EventLoop` has been passed to the function.
62-
fileprivate static func makeBootstrap(on eventLoop: EventLoop, host: String, requiresTLS: Bool, configuration: HTTPClient.Configuration) throws -> NIOClientTCPBootstrap {
68+
fileprivate static func makeBootstrap(
69+
on eventLoop: EventLoop,
70+
host: String,
71+
requiresTLS: Bool,
72+
configuration: HTTPClient.Configuration
73+
) throws -> NIOClientTCPBootstrap {
6374
let bootstrap: NIOClientTCPBootstrap
6475
#if canImport(Network)
6576
if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *), eventLoop is NIOTSEventLoop {
66-
let tlsProvider = NIOTSClientTLSProvider(tlsOptions: .init())
67-
bootstrap = NIOClientTCPBootstrap(NIOTSConnectionBootstrap(group: eventLoop), tls: tlsProvider)
77+
if configuration.proxy != nil, requiresTLS {
78+
let tlsConfiguration = configuration.tlsConfiguration ?? TLSConfiguration.forClient()
79+
let sslContext = try NIOSSLContext(configuration: tlsConfiguration)
80+
let hostname = (!requiresTLS || host.isIPAddress) ? nil : host
81+
bootstrap = try NIOClientTCPBootstrap(NIOTSConnectionBootstrap(group: eventLoop), tls: NIOSSLClientTLSProvider(context: sslContext, serverHostname: hostname))
82+
} else {
83+
let tlsProvider = NIOTSClientTLSProvider(tlsOptions: .init())
84+
bootstrap = NIOClientTCPBootstrap(NIOTSConnectionBootstrap(group: eventLoop), tls: tlsProvider)
85+
}
6886
} else {
6987
bootstrap = try ClientBootstrap.makeBootstrap(on: eventLoop, host: host, requiresTLS: requiresTLS, configuration: configuration)
7088
}
7189
#else
7290
bootstrap = try ClientBootstrap.makeBootstrap(on: eventLoop, host: host, requiresTLS: requiresTLS, configuration: configuration)
7391
#endif
7492

75-
if requiresTLS {
93+
// don't enable TLS if we have a proxy, this will be enabled later on
94+
if requiresTLS, configuration.proxy == nil {
7695
return bootstrap.enableTLS()
7796
}
7897
return bootstrap
7998
}
8099

81-
static func makeHTTPClientBootstrapBase(on eventLoop: EventLoop, host: String, port: Int, requiresTLS: Bool, configuration: HTTPClient.Configuration) throws -> NIOClientTCPBootstrap {
100+
static func makeHTTPClientBootstrapBase(
101+
on eventLoop: EventLoop,
102+
host: String,
103+
port: Int,
104+
requiresTLS: Bool,
105+
configuration: HTTPClient.Configuration
106+
) throws -> NIOClientTCPBootstrap {
82107
return try makeBootstrap(on: eventLoop, host: host, requiresTLS: requiresTLS, configuration: configuration)
83108
.channelOption(ChannelOptions.socket(SocketOptionLevel(IPPROTO_TCP), TCP_NODELAY), value: 1)
84109
.channelInitializer { channel in

Tests/AsyncHTTPClientTests/HTTPClientTests.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,9 +384,6 @@ class HTTPClientTests: XCTestCase {
384384
}
385385

386386
func testProxyTLS() throws {
387-
XCTFail("Disabled test as it crashes");
388-
return
389-
390387
let httpBin = HTTPBin(simulateProxy: .tls)
391388
let httpClient = HTTPClient(
392389
eventLoopGroupProvider: .shared(self.clientGroup),

0 commit comments

Comments
 (0)