@@ -16,7 +16,8 @@ import Logging
16
16
import NIO
17
17
import NIOHTTP2
18
18
19
- internal final class HTTP2IdleHandler : ChannelDuplexHandler {
19
+ // This is a `ChannelDuplexHandler` since we need to intercept outgoing user events.
20
+ final class HTTP2IdleHandler : ChannelDuplexHandler {
20
21
typealias InboundIn = HTTP2Frame
21
22
typealias InboundOut = HTTP2Frame
22
23
typealias OutboundIn = HTTP2Frame
@@ -25,7 +26,7 @@ internal final class HTTP2IdleHandler: ChannelDuplexHandler {
25
26
let logger : Logger
26
27
let connection : HTTP2Connection
27
28
28
- var state : StateMachine = . init( )
29
+ private var state : StateMachine = . init( )
29
30
30
31
init ( connection: HTTP2Connection , logger: Logger ) {
31
32
self . connection = connection
@@ -66,7 +67,7 @@ internal final class HTTP2IdleHandler: ChannelDuplexHandler {
66
67
context. fireChannelRead ( data)
67
68
}
68
69
69
- func userInboundEventTriggered ( context: ChannelHandlerContext , event: Any ) {
70
+ func triggerUserOutboundEvent ( context: ChannelHandlerContext , event: Any , promise : EventLoopPromise < Void > ? ) {
70
71
switch event {
71
72
case HTTPConnectionEvent . closeConnection:
72
73
let action = self . state. closeEventReceived ( )
@@ -140,6 +141,9 @@ extension HTTP2IdleHandler {
140
141
preconditionFailure ( " Invalid state: \( self . state) " )
141
142
142
143
case . connected:
144
+ // a settings frame might have multiple entries for `maxConcurrentStreams`. We are
145
+ // only interested in the last value! If no `maxConcurrentStreams` is set, we assume
146
+ // the http/2 default of 100.
143
147
let maxStreams = settings. last ( where: { $0. parameter == . maxConcurrentStreams } ) ? . value ?? 100
144
148
self . state = . active( openStreams: 0 , maxStreams: maxStreams)
145
149
return . notifyConnectionNewSettings( settings)
@@ -197,28 +201,41 @@ extension HTTP2IdleHandler {
197
201
}
198
202
199
203
mutating func streamCreated( ) -> Action {
200
- guard case . active( var openStreams, let maxStreams) = self . state else {
201
- preconditionFailure ( " Invalid state " )
202
- }
204
+ switch self . state {
205
+ case . active( var openStreams, let maxStreams) :
206
+ openStreams += 1
207
+ self . state = . active( openStreams: openStreams, maxStreams: maxStreams)
208
+ return . nothing
203
209
204
- openStreams += 1
205
- assert ( openStreams <= maxStreams)
210
+ case . closing( var openStreams, let maxStreams) :
211
+ openStreams += 1
212
+ self . state = . active( openStreams: openStreams, maxStreams: maxStreams)
213
+ return . nothing
206
214
207
- self . state = . active( openStreams: openStreams, maxStreams: maxStreams)
208
- return . nothing
215
+ case . initialized, . connected, . closed:
216
+ preconditionFailure ( " Invalid state: \( self . state) " )
217
+ }
209
218
}
210
219
211
220
mutating func streamClosed( ) -> Action {
212
- guard case . active( var openStreams, let maxStreams) = self . state else {
213
- preconditionFailure ( " Invalid state " )
214
- // TODO: What happens, if we received a go away?!??!
215
- }
221
+ switch self . state {
222
+ case . active( var openStreams, let maxStreams) :
223
+ openStreams -= 1
224
+ self . state = . active( openStreams: openStreams, maxStreams: maxStreams)
225
+ return . notifyConnectionStreamClosed( currentlyAvailable: maxStreams - openStreams)
216
226
217
- openStreams -= 1
218
- assert ( openStreams >= 0 )
227
+ case . closing( var openStreams, let maxStreams) :
228
+ openStreams -= 1
229
+ if openStreams == 0 {
230
+ self . state = . closed
231
+ return . close
232
+ }
233
+ self . state = . closing( openStreams: openStreams, maxStreams: maxStreams)
234
+ return . nothing
219
235
220
- self . state = . active( openStreams: openStreams, maxStreams: maxStreams)
221
- return . notifyConnectionStreamClosed( currentlyAvailable: maxStreams - openStreams)
236
+ case . initialized, . connected, . closed:
237
+ preconditionFailure ( " Invalid state: \( self . state) " )
238
+ }
222
239
}
223
240
}
224
241
}
0 commit comments