@@ -47,6 +47,7 @@ protocol HTTPConnectionRequester {
47
47
func http1ConnectionCreated( _: HTTP1Connection )
48
48
func http2ConnectionCreated( _: HTTP2Connection , maximumStreams: Int )
49
49
func failedToCreateHTTPConnection( _: HTTPConnectionPool . Connection . ID , error: Error )
50
+ func waitingForConnectivity( _: HTTPConnectionPool . Connection . ID , error: Error )
50
51
}
51
52
52
53
extension HTTPConnectionPool . ConnectionFactory {
@@ -62,7 +63,7 @@ extension HTTPConnectionPool.ConnectionFactory {
62
63
var logger = logger
63
64
logger [ metadataKey: " ahc-connection-id " ] = " \( connectionID) "
64
65
65
- self . makeChannel ( connectionID: connectionID, deadline: deadline, eventLoop: eventLoop, logger: logger) . whenComplete { result in
66
+ self . makeChannel ( requester : requester , connectionID: connectionID, deadline: deadline, eventLoop: eventLoop, logger: logger) . whenComplete { result in
66
67
switch result {
67
68
case . success( . http1_1( let channel) ) :
68
69
do {
@@ -104,13 +105,15 @@ extension HTTPConnectionPool.ConnectionFactory {
104
105
case http2( Channel )
105
106
}
106
107
107
- func makeHTTP1Channel(
108
+ func makeHTTP1Channel< Requester: HTTPConnectionRequester > (
109
+ requester: Requester ,
108
110
connectionID: HTTPConnectionPool . Connection . ID ,
109
111
deadline: NIODeadline ,
110
112
eventLoop: EventLoop ,
111
113
logger: Logger
112
114
) -> EventLoopFuture < Channel > {
113
115
self . makeChannel (
116
+ requester: requester,
114
117
connectionID: connectionID,
115
118
deadline: deadline,
116
119
eventLoop: eventLoop,
@@ -137,7 +140,8 @@ extension HTTPConnectionPool.ConnectionFactory {
137
140
}
138
141
}
139
142
140
- func makeChannel(
143
+ func makeChannel< Requester: HTTPConnectionRequester > (
144
+ requester: Requester ,
141
145
connectionID: HTTPConnectionPool . Connection . ID ,
142
146
deadline: NIODeadline ,
143
147
eventLoop: EventLoop ,
@@ -150,6 +154,7 @@ extension HTTPConnectionPool.ConnectionFactory {
150
154
case . socks:
151
155
channelFuture = self . makeSOCKSProxyChannel (
152
156
proxy,
157
+ requester: requester,
153
158
connectionID: connectionID,
154
159
deadline: deadline,
155
160
eventLoop: eventLoop,
@@ -158,14 +163,15 @@ extension HTTPConnectionPool.ConnectionFactory {
158
163
case . http:
159
164
channelFuture = self . makeHTTPProxyChannel (
160
165
proxy,
166
+ requester: requester,
161
167
connectionID: connectionID,
162
168
deadline: deadline,
163
169
eventLoop: eventLoop,
164
170
logger: logger
165
171
)
166
172
}
167
173
} else {
168
- channelFuture = self . makeNonProxiedChannel ( deadline: deadline, eventLoop: eventLoop, logger: logger)
174
+ channelFuture = self . makeNonProxiedChannel ( requester : requester , connectionID : connectionID , deadline: deadline, eventLoop: eventLoop, logger: logger)
169
175
}
170
176
171
177
// let's map `ChannelError.connectTimeout` into a `HTTPClientError.connectTimeout`
@@ -179,30 +185,38 @@ extension HTTPConnectionPool.ConnectionFactory {
179
185
}
180
186
}
181
187
182
- private func makeNonProxiedChannel(
188
+ private func makeNonProxiedChannel< Requester: HTTPConnectionRequester > (
189
+ requester: Requester ,
190
+ connectionID: HTTPConnectionPool . Connection . ID ,
183
191
deadline: NIODeadline ,
184
192
eventLoop: EventLoop ,
185
193
logger: Logger
186
194
) -> EventLoopFuture < NegotiatedProtocol > {
187
195
switch self . key. scheme {
188
196
case . http, . httpUnix, . unix:
189
- return self . makePlainChannel ( deadline: deadline, eventLoop: eventLoop) . map { . http1_1( $0) }
197
+ return self . makePlainChannel ( requester : requester , connectionID : connectionID , deadline: deadline, eventLoop: eventLoop) . map { . http1_1( $0) }
190
198
case . https, . httpsUnix:
191
- return self . makeTLSChannel ( deadline: deadline, eventLoop: eventLoop, logger: logger) . flatMapThrowing {
199
+ return self . makeTLSChannel ( requester : requester , connectionID : connectionID , deadline: deadline, eventLoop: eventLoop, logger: logger) . flatMapThrowing {
192
200
channel, negotiated in
193
201
194
202
try self . matchALPNToHTTPVersion ( negotiated, channel: channel)
195
203
}
196
204
}
197
205
}
198
206
199
- private func makePlainChannel( deadline: NIODeadline , eventLoop: EventLoop ) -> EventLoopFuture < Channel > {
207
+ private func makePlainChannel< Requester: HTTPConnectionRequester > (
208
+ requester: Requester ,
209
+ connectionID: HTTPConnectionPool . Connection . ID ,
210
+ deadline: NIODeadline ,
211
+ eventLoop: EventLoop
212
+ ) -> EventLoopFuture < Channel > {
200
213
precondition ( !self . key. scheme. usesTLS, " Unexpected scheme " )
201
- return self . makePlainBootstrap ( deadline: deadline, eventLoop: eventLoop) . connect ( target: self . key. connectionTarget)
214
+ return self . makePlainBootstrap ( requester : requester , connectionID : connectionID , deadline: deadline, eventLoop: eventLoop) . connect ( target: self . key. connectionTarget)
202
215
}
203
216
204
- private func makeHTTPProxyChannel(
217
+ private func makeHTTPProxyChannel< Requester : HTTPConnectionRequester > (
205
218
_ proxy: HTTPClient . Configuration . Proxy ,
219
+ requester: Requester ,
206
220
connectionID: HTTPConnectionPool . Connection . ID ,
207
221
deadline: NIODeadline ,
208
222
eventLoop: EventLoop ,
@@ -211,7 +225,7 @@ extension HTTPConnectionPool.ConnectionFactory {
211
225
// A proxy connection starts with a plain text connection to the proxy server. After
212
226
// the connection has been established with the proxy server, the connection might be
213
227
// upgraded to TLS before we send our first request.
214
- let bootstrap = self . makePlainBootstrap ( deadline: deadline, eventLoop: eventLoop)
228
+ let bootstrap = self . makePlainBootstrap ( requester : requester , connectionID : connectionID , deadline: deadline, eventLoop: eventLoop)
215
229
return bootstrap. connect ( host: proxy. host, port: proxy. port) . flatMap { channel in
216
230
let encoder = HTTPRequestEncoder ( )
217
231
let decoder = ByteToMessageHandler ( HTTPResponseDecoder ( leftOverBytesStrategy: . dropBytes) )
@@ -243,8 +257,9 @@ extension HTTPConnectionPool.ConnectionFactory {
243
257
}
244
258
}
245
259
246
- private func makeSOCKSProxyChannel(
260
+ private func makeSOCKSProxyChannel< Requester : HTTPConnectionRequester > (
247
261
_ proxy: HTTPClient . Configuration . Proxy ,
262
+ requester: Requester ,
248
263
connectionID: HTTPConnectionPool . Connection . ID ,
249
264
deadline: NIODeadline ,
250
265
eventLoop: EventLoop ,
@@ -253,7 +268,7 @@ extension HTTPConnectionPool.ConnectionFactory {
253
268
// A proxy connection starts with a plain text connection to the proxy server. After
254
269
// the connection has been established with the proxy server, the connection might be
255
270
// upgraded to TLS before we send our first request.
256
- let bootstrap = self . makePlainBootstrap ( deadline: deadline, eventLoop: eventLoop)
271
+ let bootstrap = self . makePlainBootstrap ( requester : requester , connectionID : connectionID , deadline: deadline, eventLoop: eventLoop)
257
272
return bootstrap. connect ( host: proxy. host, port: proxy. port) . flatMap { channel in
258
273
let socksConnectHandler = SOCKSClientHandler ( targetAddress: SOCKSAddress ( self . key. connectionTarget) )
259
274
let socksEventHandler = SOCKSEventsHandler ( deadline: deadline)
@@ -331,14 +346,21 @@ extension HTTPConnectionPool.ConnectionFactory {
331
346
}
332
347
}
333
348
334
- private func makePlainBootstrap( deadline: NIODeadline , eventLoop: EventLoop ) -> NIOClientTCPBootstrapProtocol {
349
+ private func makePlainBootstrap< Requester: HTTPConnectionRequester > (
350
+ requester: Requester ,
351
+ connectionID: HTTPConnectionPool . Connection . ID ,
352
+ deadline: NIODeadline ,
353
+ eventLoop: EventLoop
354
+ ) -> NIOClientTCPBootstrapProtocol {
335
355
#if canImport(Network)
336
356
if #available( OSX 10 . 14 , iOS 12 . 0 , tvOS 12 . 0 , watchOS 6 . 0 , * ) , let tsBootstrap = NIOTSConnectionBootstrap ( validatingGroup: eventLoop) {
337
357
return tsBootstrap
358
+ . channelOption ( NIOTSChannelOptions . waitForActivity, value: self . clientConfiguration. networkFrameworkWaitForConnectivity)
338
359
. connectTimeout ( deadline - NIODeadline. now ( ) )
339
360
. channelInitializer { channel in
340
361
do {
341
362
try channel. pipeline. syncOperations. addHandler ( HTTPClient . NWErrorHandler ( ) )
363
+ try channel. pipeline. syncOperations. addHandler ( NWWaitingHandler ( requester: requester, connectionID: connectionID) )
342
364
return channel. eventLoop. makeSucceededVoidFuture ( )
343
365
} catch {
344
366
return channel. eventLoop. makeFailedFuture ( error)
@@ -355,9 +377,17 @@ extension HTTPConnectionPool.ConnectionFactory {
355
377
preconditionFailure ( " No matching bootstrap found " )
356
378
}
357
379
358
- private func makeTLSChannel( deadline: NIODeadline , eventLoop: EventLoop , logger: Logger ) -> EventLoopFuture < ( Channel , String ? ) > {
380
+ private func makeTLSChannel< Requester: HTTPConnectionRequester > (
381
+ requester: Requester ,
382
+ connectionID: HTTPConnectionPool . Connection . ID ,
383
+ deadline: NIODeadline ,
384
+ eventLoop: EventLoop ,
385
+ logger: Logger
386
+ ) -> EventLoopFuture < ( Channel , String ? ) > {
359
387
precondition ( self . key. scheme. usesTLS, " Unexpected scheme " )
360
388
let bootstrapFuture = self . makeTLSBootstrap (
389
+ requester: requester,
390
+ connectionID: connectionID,
361
391
deadline: deadline,
362
392
eventLoop: eventLoop,
363
393
logger: logger
@@ -387,8 +417,13 @@ extension HTTPConnectionPool.ConnectionFactory {
387
417
return channelFuture
388
418
}
389
419
390
- private func makeTLSBootstrap( deadline: NIODeadline , eventLoop: EventLoop , logger: Logger )
391
- -> EventLoopFuture < NIOClientTCPBootstrapProtocol > {
420
+ private func makeTLSBootstrap< Requester: HTTPConnectionRequester > (
421
+ requester: Requester ,
422
+ connectionID: HTTPConnectionPool . Connection . ID ,
423
+ deadline: NIODeadline ,
424
+ eventLoop: EventLoop ,
425
+ logger: Logger
426
+ ) -> EventLoopFuture < NIOClientTCPBootstrapProtocol > {
392
427
var tlsConfig = self . tlsConfiguration
393
428
switch self . clientConfiguration. httpVersion. configuration {
394
429
case . automatic:
@@ -408,11 +443,13 @@ extension HTTPConnectionPool.ConnectionFactory {
408
443
options -> NIOClientTCPBootstrapProtocol in
409
444
410
445
tsBootstrap
446
+ . channelOption ( NIOTSChannelOptions . waitForActivity, value: self . clientConfiguration. networkFrameworkWaitForConnectivity)
411
447
. connectTimeout ( deadline - NIODeadline. now ( ) )
412
448
. tlsOptions ( options)
413
449
. channelInitializer { channel in
414
450
do {
415
451
try channel. pipeline. syncOperations. addHandler ( HTTPClient . NWErrorHandler ( ) )
452
+ try channel. pipeline. syncOperations. addHandler ( NWWaitingHandler ( requester: requester, connectionID: connectionID) )
416
453
// we don't need to set a TLS deadline for NIOTS connections, since the
417
454
// TLS handshake is part of the TS connection bootstrap. If the TLS
418
455
// handshake times out the complete connection creation will be failed.
0 commit comments