@@ -21,7 +21,7 @@ protocol HTTPConnectionPoolDelegate {
21
21
func connectionPoolDidShutdown( _ pool: HTTPConnectionPool , unclean: Bool )
22
22
}
23
23
24
- class HTTPConnectionPool {
24
+ final class HTTPConnectionPool {
25
25
struct Connection : Hashable {
26
26
typealias ID = Int
27
27
@@ -92,14 +92,14 @@ class HTTPConnectionPool {
92
92
93
93
/// Closes the connection without cancelling running requests. Use this when you are sure, that the
94
94
/// connection is currently idle.
95
- fileprivate func close( ) -> EventLoopFuture < Void > {
95
+ fileprivate func close( promise : EventLoopPromise < Void > ? ) {
96
96
switch self . _ref {
97
97
case . http1_1( let connection) :
98
- return connection. close ( )
98
+ return connection. close ( promise : promise )
99
99
case . http2( let connection) :
100
- return connection. close ( )
101
- case . __testOnly_connection( _ , let eventLoop ) :
102
- return eventLoop . makeSucceededFuture ( ( ) )
100
+ return connection. close ( promise : promise )
101
+ case . __testOnly_connection:
102
+ promise ? . succeed ( ( ) )
103
103
}
104
104
}
105
105
@@ -139,18 +139,20 @@ class HTTPConnectionPool {
139
139
}
140
140
}
141
141
142
- let timerLock = Lock ( )
142
+ static let fallbackConnectTimeout : TimeAmount = . seconds( 30 )
143
+
144
+ private let timerLock = Lock ( )
143
145
private var _requestTimer = [ Request . ID : Scheduled < Void > ] ( )
144
146
private var _idleTimer = [ Connection . ID : Scheduled < Void > ] ( )
145
147
private var _backoffTimer = [ Connection . ID : Scheduled < Void > ] ( )
146
148
147
- let key : ConnectionPool . Key
148
- var logger : Logger
149
+ private let key : ConnectionPool . Key
150
+ private var logger : Logger
149
151
150
- let eventLoopGroup : EventLoopGroup
151
- let connectionFactory : ConnectionFactory
152
- let clientConfiguration : HTTPClient . Configuration
153
- let idleConnectionTimeout : TimeAmount
152
+ private let eventLoopGroup : EventLoopGroup
153
+ private let connectionFactory : ConnectionFactory
154
+ private let clientConfiguration : HTTPClient . Configuration
155
+ private let idleConnectionTimeout : TimeAmount
154
156
155
157
let delegate : HTTPConnectionPoolDelegate
156
158
@@ -197,12 +199,14 @@ class HTTPConnectionPool {
197
199
self . run ( action: action)
198
200
}
199
201
200
- func run( action: StateMachine . Action ) {
202
+ // MARK: Run actions
203
+
204
+ private func run( action: StateMachine . Action ) {
201
205
self . runConnectionAction ( action. connection)
202
206
self . runRequestAction ( action. request)
203
207
}
204
208
205
- func runConnectionAction( _ action: StateMachine . ConnectionAction ) {
209
+ private func runConnectionAction( _ action: StateMachine . ConnectionAction ) {
206
210
switch action {
207
211
case . createConnection( let connectionID, let eventLoop) :
208
212
self . createConnection ( connectionID, on: eventLoop)
@@ -218,19 +222,19 @@ class HTTPConnectionPool {
218
222
219
223
case . closeConnection( let connection, isShutdown: let isShutdown) :
220
224
// we are not interested in the close future...
221
- _ = connection. close ( )
225
+ connection. close ( promise : nil )
222
226
223
227
if case . yes( let unclean) = isShutdown {
224
228
self . delegate. connectionPoolDidShutdown ( self , unclean: unclean)
225
229
}
226
230
227
231
case . cleanupConnections( let cleanupContext, isShutdown: let isShutdown) :
228
232
for connection in cleanupContext. close {
229
- _ = connection. close ( )
233
+ connection. close ( promise : nil )
230
234
}
231
235
232
236
for connection in cleanupContext. cancel {
233
- _ = connection. close ( )
237
+ connection. close ( promise : nil )
234
238
}
235
239
236
240
for connectionID in cleanupContext. connectBackoff {
@@ -246,7 +250,11 @@ class HTTPConnectionPool {
246
250
}
247
251
}
248
252
249
- func runRequestAction( _ action: StateMachine . RequestAction ) {
253
+ private func runRequestAction( _ action: StateMachine . RequestAction ) {
254
+ // The order of execution fail/execute request vs cancelling the request timeout timer does
255
+ // not matter in the actions here. The actions don't cause any side effects that will be
256
+ // reported back to the state machine and are not dependent on each other.
257
+
250
258
switch action {
251
259
case . executeRequest( let request, let connection, cancelTimeout: let cancelTimeout) :
252
260
connection. executeRequest ( request. req)
@@ -255,10 +263,8 @@ class HTTPConnectionPool {
255
263
}
256
264
257
265
case . executeRequestsAndCancelTimeouts( let requests, let connection) :
258
- for request in requests {
259
- connection. executeRequest ( request. req)
260
- self . cancelRequestTimeout ( request. id)
261
- }
266
+ requests. forEach { connection. executeRequest ( $0. req) }
267
+ self . cancelRequestTimeouts ( requests)
262
268
263
269
case . failRequest( let request, let error, cancelTimeout: let cancelTimeout) :
264
270
if cancelTimeout {
@@ -267,10 +273,8 @@ class HTTPConnectionPool {
267
273
request. req. fail ( error)
268
274
269
275
case . failRequestsAndCancelTimeouts( let requests, let error) :
270
- for request in requests {
271
- self . cancelRequestTimeout ( request. id)
272
- request. req. fail ( error)
273
- }
276
+ requests. forEach { $0. req. fail ( error) }
277
+ self . cancelRequestTimeouts ( requests)
274
278
275
279
case . scheduleRequestTimeout( let request, on: let eventLoop) :
276
280
self . scheduleRequestTimeout ( request, on: eventLoop)
@@ -283,15 +287,13 @@ class HTTPConnectionPool {
283
287
}
284
288
}
285
289
286
- // MARK: Run actions
287
-
288
290
private func createConnection( _ connectionID: Connection . ID , on eventLoop: EventLoop ) {
289
291
self . connectionFactory. makeConnection (
290
292
for: self ,
291
293
connectionID: connectionID,
292
294
http1ConnectionDelegate: self ,
293
295
http2ConnectionDelegate: self ,
294
- deadline: . now( ) + ( self . clientConfiguration. timeout. connect ?? . seconds ( 30 ) ) ,
296
+ deadline: . now( ) + ( self . clientConfiguration. timeout. connect ?? Self . fallbackConnectTimeout ) ,
295
297
eventLoop: eventLoop,
296
298
logger: self . logger
297
299
)
@@ -303,17 +305,17 @@ class HTTPConnectionPool {
303
305
// The timer has fired. Now we need to do a couple of things:
304
306
//
305
307
// 1. Remove ourselves from the timer dictionary to not leak any data. If our
306
- // waiter entry still exist , we need to tell the state machine, that we want
308
+ // waiter entry still exists , we need to tell the state machine, that we want
307
309
// to fail the request.
308
310
309
- let timeout = self . timerLock. withLock {
311
+ let timeoutFired = self . timerLock. withLock {
310
312
self . _requestTimer. removeValue ( forKey: requestID) != nil
311
313
}
312
314
313
315
// 2. If the entry did not exists anymore, we can assume that the request was
314
316
// scheduled on another connection. The timer still fired anyhow because of a
315
317
// race. In such a situation we don't need to do anything.
316
- guard timeout else { return }
318
+ guard timeoutFired else { return }
317
319
318
320
// 3. Tell the state machine about the timeout
319
321
let action = self . stateLock. withLock {
@@ -339,6 +341,15 @@ class HTTPConnectionPool {
339
341
scheduled? . cancel ( )
340
342
}
341
343
344
+ private func cancelRequestTimeouts( _ requests: [ Request ] ) {
345
+ let scheduled = self . timerLock. withLock {
346
+ requests. compactMap {
347
+ self . _requestTimer. removeValue ( forKey: $0. id)
348
+ }
349
+ }
350
+ scheduled. forEach { $0. cancel ( ) }
351
+ }
352
+
342
353
private func scheduleIdleTimerForConnection( _ connectionID: Connection . ID , on eventLoop: EventLoop ) {
343
354
let scheduled = eventLoop. scheduleTask ( in: self . idleConnectionTimeout) {
344
355
// there might be a race between a cancelTimer call and the triggering
0 commit comments