@@ -22,37 +22,46 @@ import NIOHTTP1
2222struct MockConnectionPool {
2323 typealias Connection = HTTPConnectionPool . Connection
2424
25- enum Errors : Error {
25+ enum Errors : Error , Hashable {
2626 case connectionIDAlreadyUsed
2727 case connectionNotFound
2828 case connectionExists
2929 case connectionNotIdle
30- case connectionAlreadyParked
3130 case connectionNotParked
3231 case connectionIsParked
3332 case connectionIsClosed
3433 case connectionIsNotStarting
3534 case connectionIsNotExecuting
3635 case connectionDoesNotFulfillEventLoopRequirement
36+ case connectionDoesNotHaveHTTP2StreamAvailable
3737 case connectionBackoffTimerExists
3838 case connectionBackoffTimerNotFound
3939 }
4040
41- private struct MockConnectionState {
41+ fileprivate struct MockConnectionState {
4242 typealias ID = HTTPConnectionPool . Connection . ID
4343
44- enum State {
44+ private enum State {
45+ enum HTTP1State {
46+ case inUse
47+ case idle( parked: Bool , idleSince: NIODeadline )
48+ }
49+
50+ enum HTTP2State {
51+ case inUse( maxConcurrentStreams: Int , used: Int )
52+ case idle( maxConcurrentStreams: Int , parked: Bool , lastIdle: NIODeadline )
53+ }
54+
4555 case starting
46- case http1( leased : Bool , lastIdle : NIODeadline )
47- case http2( streams : Int , used : Int )
56+ case http1( HTTP1State )
57+ case http2( HTTP2State )
4858 case closed
4959 }
5060
5161 let id : ID
5262 let eventLoop : EventLoop
5363
54- private( set) var state : State = . starting
55- private( set) var isParked : Bool = false
64+ private var state : State = . starting
5665
5766 init ( id: ID , eventLoop: EventLoop ) {
5867 self . id = id
@@ -68,36 +77,46 @@ struct MockConnectionPool {
6877 }
6978 }
7079
80+ /// Is the connection idle (meaning there are no requests executing on it)
7181 var isIdle : Bool {
7282 switch self . state {
73- case . starting, . closed:
83+ case . starting, . closed, . http1 ( . inUse ) , . http2 ( . inUse ) :
7484 return false
75- case . http1( let leased, _) :
76- return !leased
77- case . http2( _, let used) :
78- return used == 0
85+
86+ case . http1( . idle) , . http2( . idle) :
87+ return true
7988 }
8089 }
8190
82- var isLeased : Bool {
91+ /// Is the connection idle and did we create a timeout timer for it?
92+ var isParked : Bool {
8393 switch self . state {
84- case . starting, . closed:
94+ case . starting, . closed, . http1 ( . inUse ) , . http2 ( . inUse ) :
8595 return false
86- case . http1( let leased, _) :
87- return leased
88- case . http2( _, let used) :
89- return used > 0
96+
97+ case . http1( . idle( let parked, _) ) , . http2( . idle( _, let parked, _) ) :
98+ return parked
9099 }
91100 }
92101
93- var lastIdle : NIODeadline ? {
102+ /// Is the connection in use (are there requests executing on it)
103+ var isUsed : Bool {
94104 switch self . state {
95- case . starting, . closed:
96- return nil
97- case . http1( _, let lastReturn) :
98- return lastReturn
99- case . http2:
105+ case . starting, . closed, . http1( . idle) , . http2( . idle) :
106+ return false
107+
108+ case . http1( . inUse) , . http2( . inUse) :
109+ return true
110+ }
111+ }
112+
113+ var idleSince : NIODeadline ? {
114+ switch self . state {
115+ case . starting, . closed, . http1( . inUse) , . http2( . inUse) :
100116 return nil
117+
118+ case . http1( . idle( _, let lastIdle) ) , . http2( . idle( _, _, let lastIdle) ) :
119+ return lastIdle
101120 }
102121 }
103122
@@ -106,76 +125,93 @@ struct MockConnectionPool {
106125 throw Errors . connectionIsNotStarting
107126 }
108127
109- self . state = . http1( leased : false , lastIdle : . now( ) )
128+ self . state = . http1( . idle ( parked : false , idleSince : . now( ) ) )
110129 }
111130
112131 mutating func park( ) throws {
113- guard self . isIdle else {
132+ switch self . state {
133+ case . starting, . closed, . http1( . inUse) , . http2( . inUse) :
114134 throw Errors . connectionNotIdle
115- }
116135
117- guard !self . isParked else {
118- throw Errors . connectionAlreadyParked
119- }
136+ case . http1( . idle( true , _) ) , . http2( . idle( _, true , _) ) :
137+ throw Errors . connectionIsParked
138+
139+ case . http1( . idle( false , let lastIdle) ) :
140+ self . state = . http1( . idle( parked: true , idleSince: lastIdle) )
120141
121- self . isParked = true
142+ case . http2( . idle( let maxStreams, false , let lastIdle) ) :
143+ self . state = . http2( . idle( maxConcurrentStreams: maxStreams, parked: true , lastIdle: lastIdle) )
144+ }
122145 }
123146
124147 mutating func activate( ) throws {
125- guard self . isIdle else {
148+ switch self . state {
149+ case . starting, . closed, . http1( . inUse) , . http2( . inUse) :
126150 throw Errors . connectionNotIdle
127- }
128151
129- guard self . isParked else {
152+ case . http1 ( . idle ( false , _ ) ) , . http2 ( . idle ( _ , false , _ ) ) :
130153 throw Errors . connectionNotParked
131- }
132154
133- self . isParked = false
155+ case . http1( . idle( true , let lastIdle) ) :
156+ self . state = . http1( . idle( parked: false , idleSince: lastIdle) )
157+
158+ case . http2( . idle( let maxStreams, true , let lastIdle) ) :
159+ self . state = . http2( . idle( maxConcurrentStreams: maxStreams, parked: false , lastIdle: lastIdle) )
160+ }
134161 }
135162
136163 mutating func execute( _ request: HTTPSchedulableRequest ) throws {
137- guard !self . isParked else {
164+ switch self . state {
165+ case . starting, . http1( . inUse) :
166+ throw Errors . connectionNotIdle
167+
168+ case . http1( . idle( true , _) ) , . http2( . idle( _, true , _) ) :
138169 throw Errors . connectionIsParked
139- }
140170
141- if let required = request. requiredEventLoop, required !== self . eventLoop {
142- throw Errors . connectionDoesNotFulfillEventLoopRequirement
143- }
171+ case . http1( . idle( false , _) ) :
172+ if let required = request. requiredEventLoop, required !== self . eventLoop {
173+ throw Errors . connectionDoesNotFulfillEventLoopRequirement
174+ }
175+ self . state = . http1( . inUse)
176+
177+ case . http2( . idle( let maxStreams, false , _) ) :
178+ if let required = request. requiredEventLoop, required !== self . eventLoop {
179+ throw Errors . connectionDoesNotFulfillEventLoopRequirement
180+ }
181+ if maxStreams < 1 {
182+ throw Errors . connectionDoesNotHaveHTTP2StreamAvailable
183+ }
184+ self . state = . http2( . inUse( maxConcurrentStreams: maxStreams, used: 1 ) )
185+
186+ case . http2( . inUse( let maxStreams, let used) ) :
187+ if let required = request. requiredEventLoop, required !== self . eventLoop {
188+ throw Errors . connectionDoesNotFulfillEventLoopRequirement
189+ }
190+ if used + 1 > maxStreams {
191+ throw Errors . connectionDoesNotHaveHTTP2StreamAvailable
192+ }
193+ self . state = . http2( . inUse( maxConcurrentStreams: maxStreams, used: used + 1 ) )
144194
145- switch self . state {
146- case . starting:
147- preconditionFailure ( " Should be unreachable " )
148- case . http1( leased: true , _) :
149- throw Errors . connectionNotIdle
150- case . http1( leased: false , let lastIdle) :
151- self . state = . http1( leased: true , lastIdle: lastIdle)
152- case . http2( let streams, let used) where used >= streams:
153- throw Errors . connectionNotIdle
154- case . http2( let streams, var used) :
155- used += 1
156- self . state = . http2( streams: streams, used: used)
157195 case . closed:
158196 throw Errors . connectionIsClosed
159197 }
160198 }
161199
162200 mutating func finishRequest( ) throws {
163- guard !self . isParked else {
164- throw Errors . connectionIsParked
165- }
166-
167201 switch self . state {
168- case . starting:
169- throw Errors . connectionIsNotExecuting
170- case . http1( leased: true , _) :
171- self . state = . http1( leased: false , lastIdle: . now( ) )
172- case . http1( leased: false , _) :
173- throw Errors . connectionIsNotExecuting
174- case . http2( _, let used) where used <= 0 :
202+ case . starting, . http1( . idle) , . http2( . idle) :
175203 throw Errors . connectionIsNotExecuting
176- case . http2( let streams, var used) :
177- used -= 1
178- self . state = . http2( streams: streams, used: used)
204+
205+ case . http1( . inUse) :
206+ self . state = . http1( . idle( parked: false , idleSince: . now( ) ) )
207+
208+ case . http2( . inUse( let maxStreams, let used) ) :
209+ if used == 1 {
210+ self . state = . http2( . idle( maxConcurrentStreams: maxStreams, parked: false , lastIdle: . now( ) ) )
211+ } else {
212+ self . state = . http2( . inUse( maxConcurrentStreams: maxStreams, used: used) )
213+ }
214+
179215 case . closed:
180216 throw Errors . connectionIsClosed
181217 }
@@ -185,14 +221,12 @@ struct MockConnectionPool {
185221 switch self . state {
186222 case . starting:
187223 throw Errors . connectionNotIdle
188- case . http1( let leased, _) :
189- if leased {
190- throw Errors . connectionNotIdle
191- }
192- case . http2( _, let used) :
193- if used > 0 {
194- throw Errors . connectionNotIdle
195- }
224+ case . http1( . idle) , . http2( . idle) :
225+ self . state = . closed
226+
227+ case . http1( . inUse) , . http2( . inUse) :
228+ throw Errors . connectionNotIdle
229+
196230 case . closed:
197231 throw Errors . connectionIsClosed
198232 }
@@ -209,7 +243,7 @@ struct MockConnectionPool {
209243 }
210244
211245 var leased : Int {
212- self . connections. values. filter { $0. isLeased } . count
246+ self . connections. values. filter { $0. isUsed } . count
213247 }
214248
215249 var starting : Int {
@@ -227,22 +261,21 @@ struct MockConnectionPool {
227261 var newestParkedConnection : Connection ? {
228262 self . connections. values
229263 . filter { $0. isParked }
230- . max ( by: { $0. lastIdle ! < $1. lastIdle ! } )
264+ . max ( by: { $0. idleSince ! < $1. idleSince ! } )
231265 . flatMap { . __testOnly_connection( id: $0. id, eventLoop: $0. eventLoop) }
232266 }
233267
234268 var oldestParkedConnection : Connection ? {
235269 self . connections. values
236270 . filter { $0. isParked }
237- . min ( by: { $0. lastIdle ! < $1. lastIdle ! } )
271+ . min ( by: { $0. idleSince ! < $1. idleSince ! } )
238272 . flatMap { . __testOnly_connection( id: $0. id, eventLoop: $0. eventLoop) }
239273 }
240274
241275 func newestParkedConnection( for eventLoop: EventLoop ) -> Connection ? {
242276 self . connections. values
243277 . filter { $0. eventLoop === eventLoop && $0. isParked }
244- . sorted ( by: { $0. lastIdle! > $1. lastIdle! } )
245- . max ( by: { $0. lastIdle! < $1. lastIdle! } )
278+ . max ( by: { $0. idleSince! < $1. idleSince! } )
246279 . flatMap { . __testOnly_connection( id: $0. id, eventLoop: $0. eventLoop) }
247280 }
248281
@@ -254,7 +287,7 @@ struct MockConnectionPool {
254287
255288 mutating func createConnection( _ connectionID: Connection . ID , on eventLoop: EventLoop ) throws {
256289 guard self . connections [ connectionID] == nil else {
257- throw Errors . connectionIDAlreadyUsed
290+ throw Errors . connectionExists
258291 }
259292 self . connections [ connectionID] = . init( id: connectionID, eventLoop: eventLoop)
260293 }
@@ -307,7 +340,7 @@ struct MockConnectionPool {
307340
308341 // MARK: Connection destruction
309342
310- /// Closing a connection signals intend . For this reason, it is verified, that the connection is not running any
343+ /// Closing a connection signals intent . For this reason, it is verified, that the connection is not running any
311344 /// requests when closing.
312345 mutating func closeConnection( _ connection: Connection ) throws {
313346 guard var mockConnection = self . connections. removeValue ( forKey: connection. id) else {
@@ -361,7 +394,9 @@ struct MockConnectionPool {
361394 try connection. finishRequest ( )
362395 self . connections [ connectionID] = connection
363396 }
397+ }
364398
399+ extension MockConnectionPool {
365400 mutating func randomStartingConnection( ) -> Connection . ID ? {
366401 self . connections. values
367402 . filter { $0. isStarting }
@@ -371,7 +406,7 @@ struct MockConnectionPool {
371406
372407 mutating func randomActiveConnection( ) -> Connection . ID ? {
373408 self . connections. values
374- . filter { $0. isLeased || $0. isParked }
409+ . filter { $0. isUsed || $0. isParked }
375410 . randomElement ( )
376411 . map ( \. id)
377412 }
@@ -385,7 +420,7 @@ struct MockConnectionPool {
385420
386421 mutating func randomLeasedConnection( ) -> Connection ? {
387422 self . connections. values
388- . filter { $0. isLeased }
423+ . filter { $0. isUsed }
389424 . randomElement ( )
390425 . flatMap { . __testOnly_connection( id: $0. id, eventLoop: $0. eventLoop) }
391426 }
0 commit comments