@@ -18,9 +18,14 @@ import NIOHTTP2
18
18
19
19
protocol HTTP2ConnectionDelegate {
20
20
func http2ConnectionStreamClosed( _: HTTP2Connection , availableStreams: Int )
21
+ func http2ConnectionGoAwayReceived( _: HTTP2Connection )
21
22
func http2ConnectionClosed( _: HTTP2Connection )
22
23
}
23
24
25
+ struct HTTP2PushNotSupportedError : Error { }
26
+
27
+ struct HTTP2ReceivedGoAwayBeforeSettingsError : Error { }
28
+
24
29
class HTTP2Connection {
25
30
let channel : Channel
26
31
let multiplexer : HTTP2StreamMultiplexer
@@ -30,22 +35,20 @@ class HTTP2Connection {
30
35
let delegate : HTTP2ConnectionDelegate
31
36
32
37
enum State {
38
+ case initialized
33
39
case starting( EventLoopPromise < Void > )
34
40
case active( HTTP2Settings )
41
+ case closing
35
42
case closed
36
43
}
37
44
38
- var readyToAcceptConnectionsFuture : EventLoopFuture < Void >
39
-
40
45
var settings : HTTP2Settings ? {
41
46
self . channel. eventLoop. assertInEventLoop ( )
42
47
switch self . state {
43
- case . starting:
48
+ case . initialized , . starting, . closing , . closed :
44
49
return nil
45
50
case . active( let settings) :
46
51
return settings
47
- case . closed:
48
- return nil
49
52
}
50
53
}
51
54
@@ -55,12 +58,10 @@ class HTTP2Connection {
55
58
init ( channel: Channel ,
56
59
connectionID: HTTPConnectionPool . Connection . ID ,
57
60
delegate: HTTP2ConnectionDelegate ,
58
- logger: Logger ) throws {
61
+ logger: Logger ) {
59
62
precondition ( channel. isActive)
60
63
channel. eventLoop. preconditionInEventLoop ( )
61
64
62
- let readyToAcceptConnectionsPromise = channel. eventLoop. makePromise ( of: Void . self)
63
-
64
65
self . channel = channel
65
66
self . id = connectionID
66
67
self . logger = logger
@@ -71,32 +72,30 @@ class HTTP2Connection {
71
72
outboundBufferSizeHighWatermark: 8196 ,
72
73
outboundBufferSizeLowWatermark: 4092 ,
73
74
inboundStreamInitializer: { ( channel) -> EventLoopFuture < Void > in
74
- struct HTTP2PushNotsupportedError : Error { }
75
- return channel. eventLoop. makeFailedFuture ( HTTP2PushNotsupportedError ( ) )
75
+
76
+ return channel. eventLoop. makeFailedFuture ( HTTP2PushNotSupportedError ( ) )
76
77
}
77
78
)
78
79
self . delegate = delegate
79
- self . state = . starting( readyToAcceptConnectionsPromise)
80
- self . readyToAcceptConnectionsFuture = readyToAcceptConnectionsPromise. futureResult
81
-
82
- // 1. Modify channel pipeline and add http2 handlers
83
- let sync = channel. pipeline. syncOperations
84
-
85
- let http2Handler = NIOHTTP2Handler ( mode: . client, initialSettings: nioDefaultSettings)
86
- let idleHandler = HTTP2IdleHandler ( connection: self , logger: self . logger)
87
-
88
- try sync. addHandler ( http2Handler, position: . last)
89
- try sync. addHandler ( idleHandler, position: . last)
90
- try sync. addHandler ( self . multiplexer, position: . last)
91
-
92
- // 2. set properties
93
-
94
- // with this we create an intended retain cycle...
95
- channel. closeFuture. whenComplete { _ in
96
- self . state = . closed
97
- self . delegate. http2ConnectionClosed ( self )
80
+ self . state = . initialized
81
+ }
82
+
83
+ deinit {
84
+ guard case . closed = self . state else {
85
+ preconditionFailure ( " " )
98
86
}
99
87
}
88
+
89
+ static func start(
90
+ channel: Channel ,
91
+ connectionID: HTTPConnectionPool . Connection . ID ,
92
+ delegate: HTTP2ConnectionDelegate ,
93
+ configuration: HTTPClient . Configuration ,
94
+ logger: Logger
95
+ ) -> EventLoopFuture < HTTP2Connection > {
96
+ let connection = HTTP2Connection ( channel: channel, connectionID: connectionID, delegate: delegate, logger: logger)
97
+ return connection. start ( ) . map { _ in connection }
98
+ }
100
99
101
100
func execute( request: HTTPExecutableRequest ) {
102
101
let createStreamChannelPromise = self . channel. eventLoop. makePromise ( of: Channel . self)
@@ -128,19 +127,68 @@ class HTTP2Connection {
128
127
self . channel. eventLoop. assertInEventLoop ( )
129
128
130
129
switch self . state {
130
+ case . initialized, . closed:
131
+ preconditionFailure ( " Invalid state: \( self . state) " )
131
132
case . starting( let promise) :
132
133
self . state = . active( settings)
133
134
promise. succeed ( ( ) )
134
135
case . active:
135
136
self . state = . active( settings)
136
- case . closed:
137
- preconditionFailure ( " Invalid state " )
137
+ case . closing:
138
+ // ignore. we only wait for all connections to be closed anyway.
139
+ break
138
140
}
139
141
}
140
142
141
- func http2GoAwayReceived( ) { }
143
+ func http2GoAwayReceived( ) {
144
+ self . channel. eventLoop. assertInEventLoop ( )
145
+
146
+ switch self . state {
147
+ case . initialized, . closed:
148
+ preconditionFailure ( " Invalid state: \( self . state) " )
149
+
150
+ case . starting( let promise) :
151
+ self . state = . closing
152
+ promise. fail ( HTTP2ReceivedGoAwayBeforeSettingsError ( ) )
153
+
154
+ case . active:
155
+ self . state = . closing
156
+ self . delegate. http2ConnectionGoAwayReceived ( self )
157
+
158
+ case . closing:
159
+ // we are already closing. Nothing new
160
+ break
161
+ }
162
+ }
142
163
143
164
func http2StreamClosed( availableStreams: Int ) {
144
165
self . delegate. http2ConnectionStreamClosed ( self , availableStreams: availableStreams)
145
166
}
167
+
168
+ private func start( ) -> EventLoopFuture < Void > {
169
+
170
+ let readyToAcceptConnectionsPromise = channel. eventLoop. makePromise ( of: Void . self)
171
+
172
+ self . state = . starting( readyToAcceptConnectionsPromise)
173
+ self . channel. closeFuture. whenComplete { _ in
174
+ self . state = . closed
175
+ self . delegate. http2ConnectionClosed ( self )
176
+ }
177
+
178
+ do {
179
+ let sync = channel. pipeline. syncOperations
180
+
181
+ let http2Handler = NIOHTTP2Handler ( mode: . client, initialSettings: nioDefaultSettings)
182
+ let idleHandler = HTTP2IdleHandler ( connection: self , logger: self . logger)
183
+
184
+ try sync. addHandler ( http2Handler, position: . last)
185
+ try sync. addHandler ( idleHandler, position: . last)
186
+ try sync. addHandler ( self . multiplexer, position: . last)
187
+ } catch {
188
+ self . channel. close ( mode: . all, promise: nil )
189
+ readyToAcceptConnectionsPromise. fail ( error)
190
+ }
191
+
192
+ return readyToAcceptConnectionsPromise. futureResult
193
+ }
146
194
}
0 commit comments