@@ -18,10 +18,11 @@ extension HTTPConnectionPool {
18
18
/// Represents the state of a single HTTP/1.1 connection
19
19
private struct HTTP1ConnectionState {
20
20
enum State {
21
- /// the connection is creating a connection. Valid transitions are to: .idle , .available and .closed
21
+ /// the connection is creating a connection. Valid transitions are to: .backingOff , .idle, and .closed
22
22
case starting
23
- /// the connection is waiting to retry the establishing a connection. Transition to .closed. From .closed
24
- /// a new connection state must be created for a retry.
23
+ /// the connection is waiting to retry the establishing a connection. Valid transitions are to: .closed.
24
+ /// This means, the connection can be removed from the connections without cancelling external
25
+ /// state. The connection state can then be replaced by a new one.
25
26
case backingOff
26
27
/// the connection is idle for a new request. Valid transitions to: .leased and .closed
27
28
case idle( Connection , since: NIODeadline )
@@ -152,6 +153,11 @@ extension HTTPConnectionPool {
152
153
}
153
154
}
154
155
156
+ enum CleanupAction {
157
+ case removeConnection
158
+ case keepConnection
159
+ }
160
+
155
161
/// Cleanup the current connection for shutdown.
156
162
///
157
163
/// This method is called, when the connections shall shutdown. Depending on the state
@@ -164,21 +170,21 @@ extension HTTPConnectionPool {
164
170
/// or fail until we finalize the shutdown.
165
171
///
166
172
/// - Parameter context: A cleanup context to add the connection to based on its state.
167
- /// - Returns: A bool weather the connection can be removed from the connections
168
- /// struct immediately .
169
- func cleanup( _ context: inout CleanupContext ) -> Bool {
173
+ /// - Returns: A cleanup action indicating if the connection can be removed from the
174
+ /// connection list .
175
+ func cleanup( _ context: inout CleanupContext ) -> CleanupAction {
170
176
switch self . state {
171
177
case . backingOff:
172
178
context. connectBackoff. append ( self . connectionID)
173
- return true
179
+ return . removeConnection
174
180
case . starting:
175
- return false
181
+ return . keepConnection
176
182
case . idle( let connection, since: _) :
177
183
context. close. append ( connection)
178
- return true
184
+ return . removeConnection
179
185
case . leased( let connection) :
180
186
context. cancel. append ( connection)
181
- return false
187
+ return . keepConnection
182
188
case . closed:
183
189
preconditionFailure ( " Unexpected state: Did not expect to have connections with this state in the state machine: \( self . state) " )
184
190
}
@@ -215,15 +221,17 @@ extension HTTPConnectionPool {
215
221
216
222
var stats : Stats {
217
223
var stats = Stats ( )
224
+ // all additions here can be unchecked, since we will have at max self.connections.count
225
+ // which itself is an Int. For this reason we will never overflow.
218
226
for connectionState in self . connections {
219
227
if connectionState. isConnecting {
220
- stats. connecting += 1
228
+ stats. connecting & += 1
221
229
} else if connectionState. isBackingOff {
222
- stats. backingOff += 1
230
+ stats. backingOff & += 1
223
231
} else if connectionState. isLeased {
224
- stats. leased += 1
232
+ stats. leased & += 1
225
233
} else if connectionState. isIdle {
226
- stats. idle += 1
234
+ stats. idle & += 1
227
235
}
228
236
}
229
237
return stats
@@ -234,11 +242,7 @@ extension HTTPConnectionPool {
234
242
}
235
243
236
244
var canGrow : Bool {
237
- if self . overflowIndex < self . connections. endIndex {
238
- return self . overflowIndex < self . maximumConcurrentConnections
239
- } else {
240
- return self . connections. endIndex < self . maximumConcurrentConnections
241
- }
245
+ self . overflowIndex < self . maximumConcurrentConnections
242
246
}
243
247
244
248
var startingGeneralPurposeConnections : Int {
@@ -254,7 +258,7 @@ extension HTTPConnectionPool {
254
258
}
255
259
256
260
func startingEventLoopConnections( on eventLoop: EventLoop ) -> Int {
257
- self . connections [ self . overflowIndex..< self . connections. endIndex] . reduce ( into: 0 ) { count, connection in
261
+ return self . connections [ self . overflowIndex..< self . connections. endIndex] . reduce ( into: 0 ) { count, connection in
258
262
guard connection. eventLoop === eventLoop else { return }
259
263
if connection. isConnecting || connection. isBackingOff {
260
264
count += 1
@@ -292,7 +296,7 @@ extension HTTPConnectionPool {
292
296
// MARK: Connection creation
293
297
294
298
mutating func createNewConnection( on eventLoop: EventLoop ) -> Connection . ID {
295
- assert ( self . canGrow)
299
+ precondition ( self . canGrow)
296
300
let connection = HTTP1ConnectionState ( connectionID: self . generator. next ( ) , eventLoop: eventLoop)
297
301
self . connections. insert ( connection, at: self . overflowIndex)
298
302
self . overflowIndex = self . connections. index ( after: self . overflowIndex)
@@ -307,7 +311,7 @@ extension HTTPConnectionPool {
307
311
308
312
/// A new HTTP/1.1 connection was established.
309
313
///
310
- /// This will put the position into the idle state.
314
+ /// This will put the connection into the idle state.
311
315
///
312
316
/// - Parameter connection: The new established connection.
313
317
/// - Returns: An index and an IdleConnectionContext to determine the next action for the now idle connection.
@@ -317,7 +321,7 @@ extension HTTPConnectionPool {
317
321
guard let index = self . connections. firstIndex ( where: { $0. connectionID == connection. id } ) else {
318
322
preconditionFailure ( " There is a new connection that we didn't request! " )
319
323
}
320
- assert ( connection. eventLoop === self . connections [ index] . eventLoop, " Expected the new connection to be on EL " )
324
+ precondition ( connection. eventLoop === self . connections [ index] . eventLoop, " Expected the new connection to be on EL " )
321
325
self . connections [ index] . connected ( connection)
322
326
let context = self . generateIdleConnectionContextForConnection ( at: index)
323
327
return ( index, context)
@@ -391,14 +395,17 @@ extension HTTPConnectionPool {
391
395
392
396
// MARK: Connection close/removal
393
397
398
+ /// Closes the connection at the given index. This will also remove the connection right away.
394
399
mutating func closeConnection( at index: Int ) -> Connection {
400
+ if index < self . overflowIndex {
401
+ self . overflowIndex = self . connections. index ( before: self . overflowIndex)
402
+ }
395
403
var connectionState = self . connections. remove ( at: index)
396
- self . overflowIndex = self . connections. index ( before: self . overflowIndex)
397
404
return connectionState. close ( )
398
405
}
399
406
400
407
mutating func removeConnection( at index: Int ) {
401
- assert ( self . connections [ index] . isClosed)
408
+ precondition ( self . connections [ index] . isClosed)
402
409
if index < self . overflowIndex {
403
410
self . overflowIndex = self . connections. index ( before: self . overflowIndex)
404
411
}
@@ -421,7 +428,7 @@ extension HTTPConnectionPool {
421
428
}
422
429
423
430
mutating func replaceConnection( at index: Int ) -> ( Connection . ID , EventLoop ) {
424
- assert ( self . connections [ index] . isClosed)
431
+ precondition ( self . connections [ index] . isClosed)
425
432
let newConnection = HTTP1ConnectionState (
426
433
connectionID: self . generator. next ( ) ,
427
434
eventLoop: self . connections [ index] . eventLoop
@@ -444,7 +451,7 @@ extension HTTPConnectionPool {
444
451
/// supplied index after this.
445
452
mutating func failConnection( _ connectionID: Connection . ID ) -> ( Int , FailedConnectionContext ) {
446
453
guard let index = self . connections. firstIndex ( where: { $0. connectionID == connectionID } ) else {
447
- preconditionFailure ( " We tried to create a new connection that we know nothing about? " )
454
+ preconditionFailure ( " We tried to fail a new connection that we know nothing about? " )
448
455
}
449
456
450
457
let use : ConnectionUse
@@ -474,13 +481,16 @@ extension HTTPConnectionPool {
474
481
let initialOverflowIndex = self . overflowIndex
475
482
476
483
self . connections = self . connections. enumerated ( ) . compactMap { index, connectionState in
477
- if connectionState. cleanup ( & cleanupContext) {
484
+ switch connectionState. cleanup ( & cleanupContext) {
485
+ case . removeConnection:
478
486
if index < initialOverflowIndex {
479
- self . overflowIndex -= 1
487
+ self . overflowIndex = self . connections . index ( before : self . overflowIndex )
480
488
}
481
489
return nil
490
+
491
+ case . keepConnection:
492
+ return connectionState
482
493
}
483
- return connectionState
484
494
}
485
495
486
496
return cleanupContext
@@ -489,7 +499,7 @@ extension HTTPConnectionPool {
489
499
// MARK: - Private functions -
490
500
491
501
private func generateIdleConnectionContextForConnection( at index: Int ) -> IdleConnectionContext {
492
- assert ( self . connections [ index] . isIdle)
502
+ precondition ( self . connections [ index] . isIdle)
493
503
let eventLoop = self . connections [ index] . eventLoop
494
504
let use : ConnectionUse
495
505
if index < self . overflowIndex {
0 commit comments