@@ -29,74 +29,83 @@ extension HTTPConnectionPool {
29
29
}
30
30
31
31
final class ConnectionFactory {
32
- struct Timeouts {
33
- /// the `TimeAmount` waited before the TCP connection creation is failed with a timeout error
34
- var connect : TimeAmount = . seconds( 10 )
35
-
36
- /// the `TimeAmount` waited before the SOCKS proxy connection creation is failed with a timeout error
37
- var socksProxyHandshake : TimeAmount = . seconds( 10 )
38
-
39
- /// the `TimeAmount` waited before the HTTP proxy connection creation is failed with a timeout error
40
- var httpProxyHandshake : TimeAmount = . seconds( 10 )
41
-
42
- /// the `TimeAmount` waited before we the TLS handshake is failed with a timeout error
43
- var tlsHandshake : TimeAmount = . seconds( 10 )
44
- }
45
-
46
32
let key : ConnectionPool . Key
47
33
let clientConfiguration : HTTPClient . Configuration
48
34
let tlsConfiguration : TLSConfiguration
49
35
let sslContextCache : SSLContextCache
50
- let timeouts : Timeouts
51
36
52
37
init ( key: ConnectionPool . Key ,
53
38
tlsConfiguration: TLSConfiguration ? ,
54
39
clientConfiguration: HTTPClient . Configuration ,
55
- sslContextCache: SSLContextCache ,
56
- timeouts: Timeouts = . init( ) ) {
40
+ sslContextCache: SSLContextCache ) {
57
41
self . key = key
58
42
self . clientConfiguration = clientConfiguration
59
43
self . sslContextCache = sslContextCache
60
44
self . tlsConfiguration = tlsConfiguration ?? clientConfiguration. tlsConfiguration ?? . forClient( )
61
-
62
- var timeouts = timeouts
63
- if let connect = clientConfiguration. timeout. connect {
64
- timeouts. connect = connect
65
- }
66
- self . timeouts = timeouts
67
45
}
68
46
}
69
47
}
70
48
71
49
extension HTTPConnectionPool . ConnectionFactory {
72
- func makeChannel( connectionID: HTTPConnectionPool . Connection . ID , eventLoop: EventLoop , logger: Logger ) -> EventLoopFuture < ( Channel , HTTPVersion ) > {
50
+ func makeChannel(
51
+ connectionID: HTTPConnectionPool . Connection . ID ,
52
+ deadline: NIODeadline ,
53
+ eventLoop: EventLoop ,
54
+ logger: Logger
55
+ ) -> EventLoopFuture < ( Channel , HTTPVersion ) > {
56
+ let channelFuture : EventLoopFuture < ( Channel , HTTPVersion ) >
57
+
73
58
if self . key. scheme. isProxyable, let proxy = self . clientConfiguration. proxy {
74
59
switch proxy. type {
75
60
case . socks:
76
- return self . makeSOCKSProxyChannel ( proxy, connectionID: connectionID, eventLoop: eventLoop, logger: logger)
61
+ channelFuture = self . makeSOCKSProxyChannel (
62
+ proxy,
63
+ connectionID: connectionID,
64
+ deadline: deadline,
65
+ eventLoop: eventLoop,
66
+ logger: logger)
77
67
case . http:
78
- return self . makeHTTPProxyChannel ( proxy, connectionID: connectionID, eventLoop: eventLoop, logger: logger)
68
+ channelFuture = self . makeHTTPProxyChannel (
69
+ proxy,
70
+ connectionID: connectionID,
71
+ deadline: deadline,
72
+ eventLoop: eventLoop,
73
+ logger: logger)
79
74
}
80
75
} else {
81
- return self . makeNonProxiedChannel ( eventLoop: eventLoop, logger: logger)
76
+ channelFuture = self . makeNonProxiedChannel ( deadline: deadline, eventLoop: eventLoop, logger: logger)
77
+ }
78
+
79
+ // let's map `ChannelError.connectTimeout` into a `HTTPClientError.connectTimeout`
80
+ return channelFuture. flatMapErrorThrowing { error throws -> ( Channel , HTTPVersion ) in
81
+ switch error {
82
+ case ChannelError . connectTimeout:
83
+ throw HTTPClientError . connectTimeout
84
+ default :
85
+ throw error
86
+ }
82
87
}
83
88
}
84
89
85
- private func makeNonProxiedChannel( eventLoop: EventLoop , logger: Logger ) -> EventLoopFuture < ( Channel , HTTPVersion ) > {
90
+ private func makeNonProxiedChannel(
91
+ deadline: NIODeadline ,
92
+ eventLoop: EventLoop ,
93
+ logger: Logger
94
+ ) -> EventLoopFuture < ( Channel , HTTPVersion ) > {
86
95
switch self . key. scheme {
87
96
case . http, . http_unix, . unix:
88
- return self . makePlainChannel ( eventLoop: eventLoop) . map { ( $0, . http1_1) }
97
+ return self . makePlainChannel ( deadline : deadline , eventLoop: eventLoop) . map { ( $0, . http1_1) }
89
98
case . https, . https_unix:
90
- return self . makeTLSChannel ( eventLoop: eventLoop, logger: logger) . flatMapThrowing {
99
+ return self . makeTLSChannel ( deadline : deadline , eventLoop: eventLoop, logger: logger) . flatMapThrowing {
91
100
channel, negotiated in
92
101
93
102
( channel, try self . matchALPNToHTTPVersion ( negotiated) )
94
103
}
95
104
}
96
105
}
97
106
98
- private func makePlainChannel( eventLoop: EventLoop ) -> EventLoopFuture < Channel > {
99
- let bootstrap = self . makePlainBootstrap ( eventLoop: eventLoop)
107
+ private func makePlainChannel( deadline : NIODeadline , eventLoop: EventLoop ) -> EventLoopFuture < Channel > {
108
+ let bootstrap = self . makePlainBootstrap ( deadline : deadline , eventLoop: eventLoop)
100
109
101
110
switch self . key. scheme {
102
111
case . http:
@@ -111,21 +120,22 @@ extension HTTPConnectionPool.ConnectionFactory {
111
120
private func makeHTTPProxyChannel(
112
121
_ proxy: HTTPClient . Configuration . Proxy ,
113
122
connectionID: HTTPConnectionPool . Connection . ID ,
123
+ deadline: NIODeadline ,
114
124
eventLoop: EventLoop ,
115
125
logger: Logger
116
126
) -> EventLoopFuture < ( Channel , HTTPVersion ) > {
117
127
// A proxy connection starts with a plain text connection to the proxy server. After
118
128
// the connection has been established with the proxy server, the connection might be
119
129
// upgraded to TLS before we send our first request.
120
- let bootstrap = self . makePlainBootstrap ( eventLoop: eventLoop)
130
+ let bootstrap = self . makePlainBootstrap ( deadline : deadline , eventLoop: eventLoop)
121
131
return bootstrap. connect ( host: proxy. host, port: proxy. port) . flatMap { channel in
122
132
let encoder = HTTPRequestEncoder ( )
123
133
let decoder = ByteToMessageHandler ( HTTPResponseDecoder ( leftOverBytesStrategy: . dropBytes) )
124
134
let proxyHandler = HTTP1ProxyConnectHandler (
125
135
targetHost: self . key. host,
126
136
targetPort: self . key. port,
127
137
proxyAuthorization: proxy. authorization,
128
- deadline: . now ( ) + self . timeouts . httpProxyHandshake
138
+ deadline: deadline
129
139
)
130
140
131
141
do {
@@ -145,24 +155,25 @@ extension HTTPConnectionPool.ConnectionFactory {
145
155
}
146
156
}
147
157
} . flatMap {
148
- self . setupTLSInProxyConnectionIfNeeded ( channel, logger: logger)
158
+ self . setupTLSInProxyConnectionIfNeeded ( channel, deadline : deadline , logger: logger)
149
159
}
150
160
}
151
161
}
152
162
153
163
private func makeSOCKSProxyChannel(
154
164
_ proxy: HTTPClient . Configuration . Proxy ,
155
165
connectionID: HTTPConnectionPool . Connection . ID ,
166
+ deadline: NIODeadline ,
156
167
eventLoop: EventLoop ,
157
168
logger: Logger
158
169
) -> EventLoopFuture < ( Channel , HTTPVersion ) > {
159
170
// A proxy connection starts with a plain text connection to the proxy server. After
160
171
// the connection has been established with the proxy server, the connection might be
161
172
// upgraded to TLS before we send our first request.
162
- let bootstrap = self . makePlainBootstrap ( eventLoop: eventLoop)
173
+ let bootstrap = self . makePlainBootstrap ( deadline : deadline , eventLoop: eventLoop)
163
174
return bootstrap. connect ( host: proxy. host, port: proxy. port) . flatMap { channel in
164
175
let socksConnectHandler = SOCKSClientHandler ( targetAddress: . domain( self . key. host, port: self . key. port) )
165
- let socksEventHandler = SOCKSEventsHandler ( deadline: . now ( ) + self . timeouts . socksProxyHandshake )
176
+ let socksEventHandler = SOCKSEventsHandler ( deadline: deadline )
166
177
167
178
do {
168
179
try channel. pipeline. syncOperations. addHandler ( socksConnectHandler)
@@ -178,12 +189,16 @@ extension HTTPConnectionPool.ConnectionFactory {
178
189
channel. pipeline. removeHandler ( socksConnectHandler)
179
190
}
180
191
} . flatMap {
181
- self . setupTLSInProxyConnectionIfNeeded ( channel, logger: logger)
192
+ self . setupTLSInProxyConnectionIfNeeded ( channel, deadline : deadline , logger: logger)
182
193
}
183
194
}
184
195
}
185
196
186
- private func setupTLSInProxyConnectionIfNeeded( _ channel: Channel , logger: Logger ) -> EventLoopFuture < ( Channel , HTTPVersion ) > {
197
+ private func setupTLSInProxyConnectionIfNeeded(
198
+ _ channel: Channel ,
199
+ deadline: NIODeadline ,
200
+ logger: Logger
201
+ ) -> EventLoopFuture < ( Channel , HTTPVersion ) > {
187
202
switch self . key. scheme {
188
203
case . unix, . http_unix, . https_unix:
189
204
preconditionFailure ( " Unexpected scheme. Not supported for proxy! " )
@@ -193,7 +208,7 @@ extension HTTPConnectionPool.ConnectionFactory {
193
208
var tlsConfig = self . tlsConfiguration
194
209
// since we can support h2, we need to advertise this in alpn
195
210
tlsConfig. applicationProtocols = [ " http/1.1 " /* , "h2" */ ]
196
- let tlsEventHandler = TLSEventsHandler ( deadline: . now ( ) + self . timeouts . tlsHandshake )
211
+ let tlsEventHandler = TLSEventsHandler ( deadline: deadline )
197
212
198
213
let sslContextFuture = self . sslContextCache. sslContext (
199
214
tlsConfiguration: tlsConfig,
@@ -224,11 +239,11 @@ extension HTTPConnectionPool.ConnectionFactory {
224
239
}
225
240
}
226
241
227
- private func makePlainBootstrap( eventLoop: EventLoop ) -> NIOClientTCPBootstrapProtocol {
242
+ private func makePlainBootstrap( deadline : NIODeadline , eventLoop: EventLoop ) -> NIOClientTCPBootstrapProtocol {
228
243
#if canImport(Network)
229
244
if #available( OSX 10 . 14 , iOS 12 . 0 , tvOS 12 . 0 , watchOS 6 . 0 , * ) , let tsBootstrap = NIOTSConnectionBootstrap ( validatingGroup: eventLoop) {
230
245
return tsBootstrap
231
- . connectTimeout ( self . timeouts . connect )
246
+ . connectTimeout ( deadline - NIODeadline . now ( ) )
232
247
. channelInitializer { channel in
233
248
do {
234
249
try channel. pipeline. syncOperations. addHandler ( HTTPClient . NWErrorHandler ( ) )
@@ -242,14 +257,15 @@ extension HTTPConnectionPool.ConnectionFactory {
242
257
243
258
if let nioBootstrap = ClientBootstrap ( validatingGroup: eventLoop) {
244
259
return nioBootstrap
245
- . connectTimeout ( self . timeouts . connect )
260
+ . connectTimeout ( deadline - NIODeadline . now ( ) )
246
261
}
247
262
248
263
preconditionFailure ( " No matching bootstrap found " )
249
264
}
250
265
251
- private func makeTLSChannel( eventLoop: EventLoop , logger: Logger ) -> EventLoopFuture < ( Channel , String ? ) > {
266
+ private func makeTLSChannel( deadline : NIODeadline , eventLoop: EventLoop , logger: Logger ) -> EventLoopFuture < ( Channel , String ? ) > {
252
267
let bootstrapFuture = self . makeTLSBootstrap (
268
+ deadline: deadline,
253
269
eventLoop: eventLoop,
254
270
logger: logger
255
271
)
@@ -285,7 +301,7 @@ extension HTTPConnectionPool.ConnectionFactory {
285
301
return channelFuture
286
302
}
287
303
288
- private func makeTLSBootstrap( eventLoop: EventLoop , logger: Logger )
304
+ private func makeTLSBootstrap( deadline : NIODeadline , eventLoop: EventLoop , logger: Logger )
289
305
-> EventLoopFuture < NIOClientTCPBootstrapProtocol > {
290
306
// since we can support h2, we need to advertise this in alpn
291
307
var tlsConfig = self . tlsConfiguration
@@ -298,7 +314,7 @@ extension HTTPConnectionPool.ConnectionFactory {
298
314
options -> NIOClientTCPBootstrapProtocol in
299
315
300
316
tsBootstrap
301
- . connectTimeout ( self . timeouts . connect )
317
+ . connectTimeout ( deadline - NIODeadline . now ( ) )
302
318
. tlsOptions ( options)
303
319
. channelInitializer { channel in
304
320
do {
@@ -327,7 +343,7 @@ extension HTTPConnectionPool.ConnectionFactory {
327
343
)
328
344
329
345
let bootstrap = ClientBootstrap ( group: eventLoop)
330
- . connectTimeout ( self . timeouts . connect )
346
+ . connectTimeout ( deadline - NIODeadline . now ( ) )
331
347
. channelInitializer { channel in
332
348
sslContextFuture. flatMap { ( sslContext) -> EventLoopFuture < Void > in
333
349
do {
@@ -336,7 +352,7 @@ extension HTTPConnectionPool.ConnectionFactory {
336
352
context: sslContext,
337
353
serverHostname: hostname
338
354
)
339
- let tlsEventHandler = TLSEventsHandler ( deadline: . now ( ) + self . timeouts . tlsHandshake )
355
+ let tlsEventHandler = TLSEventsHandler ( deadline: deadline )
340
356
341
357
try sync. addHandler ( sslHandler)
342
358
try sync. addHandler ( tlsEventHandler)
0 commit comments