@@ -16,20 +16,29 @@ import Logging
16
16
import NIO
17
17
import NIOHTTP2
18
18
19
- // This is a `ChannelDuplexHandler` since we need to intercept outgoing user events.
20
- final class HTTP2IdleHandler : ChannelDuplexHandler {
19
+ protocol HTTP2IdleHandlerDelegate {
20
+ func http2SettingsReceived( maxStreams: Int )
21
+
22
+ func http2GoAwayReceived( )
23
+
24
+ func http2StreamClosed( availableStreams: Int )
25
+ }
26
+
27
+ // This is a `ChannelDuplexHandler` since we need to intercept outgoing user events. It is generic
28
+ // over its delegate to allow for specialization.
29
+ final class HTTP2IdleHandler < Delegate: HTTP2IdleHandlerDelegate > : ChannelDuplexHandler {
21
30
typealias InboundIn = HTTP2Frame
22
31
typealias InboundOut = HTTP2Frame
23
32
typealias OutboundIn = HTTP2Frame
24
33
typealias OutboundOut = HTTP2Frame
25
34
26
35
let logger : Logger
27
- let connection : HTTP2Connection
36
+ let delegate : Delegate
28
37
29
38
private var state : StateMachine = . init( )
30
39
31
- init ( connection : HTTP2Connection , logger: Logger ) {
32
- self . connection = connection
40
+ init ( delegate : Delegate , logger: Logger ) {
41
+ self . delegate = delegate
33
42
self . logger = logger
34
43
}
35
44
@@ -56,17 +65,39 @@ final class HTTP2IdleHandler: ChannelDuplexHandler {
56
65
case . goAway:
57
66
let action = self . state. goAwayReceived ( )
58
67
self . run ( action, context: context)
68
+
59
69
case . settings( . settings( let settings) ) :
60
70
let action = self . state. settingsReceived ( settings)
61
71
self . run ( action, context: context)
72
+
62
73
default :
63
74
// We're not interested in other events.
64
- ( )
75
+ break
65
76
}
66
77
67
78
context. fireChannelRead ( data)
68
79
}
69
80
81
+ func userInboundEventTriggered( context: ChannelHandlerContext , event: Any ) {
82
+ // We intercept calls between the `NIOHTTP2ChannelHandler` and the `HTTP2StreamMultiplexer`
83
+ // to learn, how many open streams we have.
84
+ switch event {
85
+ case is StreamClosedEvent :
86
+ let action = self . state. streamClosed ( )
87
+ self . run ( action, context: context)
88
+
89
+ case is NIOHTTP2StreamCreatedEvent :
90
+ let action = self . state. streamCreated ( )
91
+ self . run ( action, context: context)
92
+
93
+ default :
94
+ // We're not interested in other events.
95
+ break
96
+ }
97
+
98
+ context. fireUserInboundEventTriggered ( event)
99
+ }
100
+
70
101
func triggerUserOutboundEvent( context: ChannelHandlerContext , event: Any , promise: EventLoopPromise < Void > ? ) {
71
102
switch event {
72
103
case HTTPConnectionEvent . closeConnection:
@@ -83,14 +114,14 @@ final class HTTP2IdleHandler: ChannelDuplexHandler {
83
114
case . nothing:
84
115
break
85
116
86
- case . notifyConnectionNewSettings ( let settings ) :
87
- self . connection . http2SettingsReceived ( settings )
117
+ case . notifyConnectionNewMaxStreamsSettings ( let maxStreams ) :
118
+ self . delegate . http2SettingsReceived ( maxStreams : maxStreams )
88
119
89
120
case . notifyConnectionStreamClosed( let currentlyAvailable) :
90
- self . connection . http2StreamClosed ( availableStreams: currentlyAvailable)
121
+ self . delegate . http2StreamClosed ( availableStreams: currentlyAvailable)
91
122
92
123
case . notifyConnectionGoAwayReceived:
93
- self . connection . http2GoAwayReceived ( )
124
+ self . delegate . http2GoAwayReceived ( )
94
125
95
126
case . close:
96
127
context. close ( mode: . all, promise: nil )
@@ -101,7 +132,7 @@ final class HTTP2IdleHandler: ChannelDuplexHandler {
101
132
extension HTTP2IdleHandler {
102
133
struct StateMachine {
103
134
enum Action {
104
- case notifyConnectionNewSettings ( HTTP2Settings )
135
+ case notifyConnectionNewMaxStreamsSettings ( Int )
105
136
case notifyConnectionGoAwayReceived( close: Bool )
106
137
case notifyConnectionStreamClosed( currentlyAvailable: Int )
107
138
case nothing
@@ -146,12 +177,12 @@ extension HTTP2IdleHandler {
146
177
// the http/2 default of 100.
147
178
let maxStreams = settings. last ( where: { $0. parameter == . maxConcurrentStreams } ) ? . value ?? 100
148
179
self . state = . active( openStreams: 0 , maxStreams: maxStreams)
149
- return . notifyConnectionNewSettings ( settings )
180
+ return . notifyConnectionNewMaxStreamsSettings ( maxStreams )
150
181
151
182
case . active( openStreams: let openStreams, maxStreams: let maxStreams) :
152
183
if let newMaxStreams = settings. last ( where: { $0. parameter == . maxConcurrentStreams } ) ? . value, newMaxStreams != maxStreams {
153
184
self . state = . active( openStreams: openStreams, maxStreams: newMaxStreams)
154
- return . notifyConnectionNewSettings ( settings )
185
+ return . notifyConnectionNewMaxStreamsSettings ( newMaxStreams )
155
186
}
156
187
return . nothing
157
188
@@ -163,7 +194,8 @@ extension HTTP2IdleHandler {
163
194
mutating func goAwayReceived( ) -> Action {
164
195
switch self . state {
165
196
case . initialized, . closed:
166
- preconditionFailure ( " Invalid state " )
197
+ preconditionFailure ( " Invalid state: \( self . state) " )
198
+
167
199
case . connected:
168
200
self . state = . closing( openStreams: 0 , maxStreams: 0 )
169
201
return . notifyConnectionGoAwayReceived( close: true )
@@ -180,7 +212,7 @@ extension HTTP2IdleHandler {
180
212
mutating func closeEventReceived( ) -> Action {
181
213
switch self . state {
182
214
case . initialized:
183
- preconditionFailure ( " " )
215
+ preconditionFailure ( " Invalid state: \( self . state ) " )
184
216
185
217
case . connected:
186
218
self . state = . closing( openStreams: 0 , maxStreams: 0 )
0 commit comments