@@ -35,9 +35,10 @@ final class HTTP2IdleHandler<Delegate: HTTP2IdleHandlerDelegate>: ChannelDuplexH
3535 let logger : Logger
3636 let delegate : Delegate
3737
38- private var state : StateMachine = . init ( )
38+ private var state : StateMachine
3939
40- init ( delegate: Delegate , logger: Logger ) {
40+ init ( delegate: Delegate , logger: Logger , maximumConnectionUses: Int ? = nil ) {
41+ self . state = StateMachine ( maximumUses: maximumConnectionUses)
4142 self . delegate = delegate
4243 self . logger = logger
4344 }
@@ -140,19 +141,23 @@ extension HTTP2IdleHandler {
140141 }
141142
142143 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 ? )
146147 case closing( openStreams: Int , maxStreams: Int )
147148 case closed
148149 }
149150
150- var state : State = . initialized
151+ var state : State
152+
153+ init ( maximumUses: Int ? ) {
154+ self . state = . initialized( maximumUses: maximumUses)
155+ }
151156
152157 mutating func channelActive( ) {
153158 switch self . state {
154- case . initialized:
155- self . state = . connected
159+ case . initialized( let maximumUses ) :
160+ self . state = . connected( remainingUses : maximumUses )
156161
157162 case . connected, . active, . closing, . closed:
158163 break
@@ -171,17 +176,17 @@ extension HTTP2IdleHandler {
171176 case . initialized:
172177 preconditionFailure ( " Invalid state: \( self . state) " )
173178
174- case . connected:
179+ case . connected( let remainingUses ) :
175180 // a settings frame might have multiple entries for `maxConcurrentStreams`. We are
176181 // only interested in the last value! If no `maxConcurrentStreams` is set, we assume
177182 // the http/2 default of 100.
178183 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 )
180185 return . notifyConnectionNewMaxStreamsSettings( maxStreams)
181186
182- case . active( openStreams: let openStreams, maxStreams: let maxStreams) :
187+ case . active( openStreams: let openStreams, maxStreams: let maxStreams, remainingUses : let remainingUses ) :
183188 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 )
185190 return . notifyConnectionNewMaxStreamsSettings( newMaxStreams)
186191 }
187192 return . nothing
@@ -205,7 +210,7 @@ extension HTTP2IdleHandler {
205210 self . state = . closing( openStreams: 0 , maxStreams: 0 )
206211 return . notifyConnectionGoAwayReceived( close: true )
207212
208- case . active( let openStreams, let maxStreams) :
213+ case . active( let openStreams, let maxStreams, _ ) :
209214 self . state = . closing( openStreams: openStreams, maxStreams: maxStreams)
210215 return . notifyConnectionGoAwayReceived( close: openStreams == 0 )
211216
@@ -228,7 +233,7 @@ extension HTTP2IdleHandler {
228233 self . state = . closing( openStreams: 0 , maxStreams: 0 )
229234 return . close
230235
231- case . active( let openStreams, let maxStreams) :
236+ case . active( let openStreams, let maxStreams, _ ) :
232237 if openStreams == 0 {
233238 self . state = . closed
234239 return . close
@@ -247,10 +252,19 @@ extension HTTP2IdleHandler {
247252 case . initialized, . connected:
248253 preconditionFailure ( " Invalid state: \( self . state) " )
249254
250- case . active( var openStreams, let maxStreams) :
255+ case . active( var openStreams, let maxStreams, let remainingUses ) :
251256 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+ }
254268
255269 case . closing( var openStreams, let maxStreams) :
256270 // A stream might be opened, while we are closing because of race conditions. For
@@ -271,10 +285,10 @@ extension HTTP2IdleHandler {
271285 case . initialized, . connected:
272286 preconditionFailure ( " Invalid state: \( self . state) " )
273287
274- case . active( var openStreams, let maxStreams) :
288+ case . active( var openStreams, let maxStreams, let remainingUses ) :
275289 openStreams -= 1
276290 assert ( openStreams >= 0 )
277- self . state = . active( openStreams: openStreams, maxStreams: maxStreams)
291+ self . state = . active( openStreams: openStreams, maxStreams: maxStreams, remainingUses : remainingUses )
278292 return . notifyConnectionStreamClosed( currentlyAvailable: maxStreams - openStreams)
279293
280294 case . closing( var openStreams, let maxStreams) :
0 commit comments