@@ -35,9 +35,10 @@ final class HTTP2IdleHandler<Delegate: HTTP2IdleHandlerDelegate>: ChannelDuplexH
35
35
let logger : Logger
36
36
let delegate : Delegate
37
37
38
- private var state : StateMachine = . init ( )
38
+ private var state : StateMachine
39
39
40
- init ( delegate: Delegate , logger: Logger ) {
40
+ init ( delegate: Delegate , logger: Logger , maximumConnectionUses: Int ? = nil ) {
41
+ self . state = StateMachine ( maximumUses: maximumConnectionUses)
41
42
self . delegate = delegate
42
43
self . logger = logger
43
44
}
@@ -140,19 +141,23 @@ extension HTTP2IdleHandler {
140
141
}
141
142
142
143
enum State {
143
- case initialized
144
- case connected
145
- case active( openStreams: Int , maxStreams: Int )
144
+ case initialized( maximumUses : Int ? )
145
+ case connected( remainingUses : Int ? )
146
+ case active( openStreams: Int , maxStreams: Int , remainingUses : Int ? )
146
147
case closing( openStreams: Int , maxStreams: Int )
147
148
case closed
148
149
}
149
150
150
- var state : State = . initialized
151
+ var state : State
152
+
153
+ init ( maximumUses: Int ? ) {
154
+ self . state = . initialized( maximumUses: maximumUses)
155
+ }
151
156
152
157
mutating func channelActive( ) {
153
158
switch self . state {
154
- case . initialized:
155
- self . state = . connected
159
+ case . initialized( let maximumUses ) :
160
+ self . state = . connected( remainingUses : maximumUses )
156
161
157
162
case . connected, . active, . closing, . closed:
158
163
break
@@ -171,17 +176,17 @@ extension HTTP2IdleHandler {
171
176
case . initialized:
172
177
preconditionFailure ( " Invalid state: \( self . state) " )
173
178
174
- case . connected:
179
+ case . connected( let remainingUses ) :
175
180
// a settings frame might have multiple entries for `maxConcurrentStreams`. We are
176
181
// only interested in the last value! If no `maxConcurrentStreams` is set, we assume
177
182
// the http/2 default of 100.
178
183
let maxStreams = settings. last ( where: { $0. parameter == . maxConcurrentStreams } ) ? . value ?? 100
179
- self . state = . active( openStreams: 0 , maxStreams: maxStreams)
184
+ self . state = . active( openStreams: 0 , maxStreams: maxStreams, remainingUses : remainingUses )
180
185
return . notifyConnectionNewMaxStreamsSettings( maxStreams)
181
186
182
- case . active( openStreams: let openStreams, maxStreams: let maxStreams) :
187
+ case . active( openStreams: let openStreams, maxStreams: let maxStreams, remainingUses : let remainingUses ) :
183
188
if let newMaxStreams = settings. last ( where: { $0. parameter == . maxConcurrentStreams } ) ? . value, newMaxStreams != maxStreams {
184
- self . state = . active( openStreams: openStreams, maxStreams: newMaxStreams)
189
+ self . state = . active( openStreams: openStreams, maxStreams: newMaxStreams, remainingUses : remainingUses )
185
190
return . notifyConnectionNewMaxStreamsSettings( newMaxStreams)
186
191
}
187
192
return . nothing
@@ -205,7 +210,7 @@ extension HTTP2IdleHandler {
205
210
self . state = . closing( openStreams: 0 , maxStreams: 0 )
206
211
return . notifyConnectionGoAwayReceived( close: true )
207
212
208
- case . active( let openStreams, let maxStreams) :
213
+ case . active( let openStreams, let maxStreams, _ ) :
209
214
self . state = . closing( openStreams: openStreams, maxStreams: maxStreams)
210
215
return . notifyConnectionGoAwayReceived( close: openStreams == 0 )
211
216
@@ -228,7 +233,7 @@ extension HTTP2IdleHandler {
228
233
self . state = . closing( openStreams: 0 , maxStreams: 0 )
229
234
return . close
230
235
231
- case . active( let openStreams, let maxStreams) :
236
+ case . active( let openStreams, let maxStreams, _ ) :
232
237
if openStreams == 0 {
233
238
self . state = . closed
234
239
return . close
@@ -247,10 +252,19 @@ extension HTTP2IdleHandler {
247
252
case . initialized, . connected:
248
253
preconditionFailure ( " Invalid state: \( self . state) " )
249
254
250
- case . active( var openStreams, let maxStreams) :
255
+ case . active( var openStreams, let maxStreams, let remainingUses ) :
251
256
openStreams += 1
252
- self . state = . active( openStreams: openStreams, maxStreams: maxStreams)
253
- return . nothing
257
+ let remainingUses = remainingUses. map { $0 - 1 }
258
+ self . state = . active( openStreams: openStreams, maxStreams: maxStreams, remainingUses: remainingUses)
259
+
260
+ if remainingUses == 0 {
261
+ // Treat running out of connection uses as if we received a GOAWAY frame. This
262
+ // will notify the delegate (i.e. connection pool) that the connection can no
263
+ // longer be used.
264
+ return self . goAwayReceived ( )
265
+ } else {
266
+ return . nothing
267
+ }
254
268
255
269
case . closing( var openStreams, let maxStreams) :
256
270
// A stream might be opened, while we are closing because of race conditions. For
@@ -271,10 +285,10 @@ extension HTTP2IdleHandler {
271
285
case . initialized, . connected:
272
286
preconditionFailure ( " Invalid state: \( self . state) " )
273
287
274
- case . active( var openStreams, let maxStreams) :
288
+ case . active( var openStreams, let maxStreams, let remainingUses ) :
275
289
openStreams -= 1
276
290
assert ( openStreams >= 0 )
277
- self . state = . active( openStreams: openStreams, maxStreams: maxStreams)
291
+ self . state = . active( openStreams: openStreams, maxStreams: maxStreams, remainingUses : remainingUses )
278
292
return . notifyConnectionStreamClosed( currentlyAvailable: maxStreams - openStreams)
279
293
280
294
case . closing( var openStreams, let maxStreams) :
0 commit comments