@@ -169,6 +169,26 @@ final class ClosedEventVsFrameOrderingHandler: ChannelInboundHandler {
169
169
}
170
170
}
171
171
172
+ /// A simple channel handler that adds the HTTP2Parser handler dynamically
173
+ /// after a read event has been triggered.
174
+ class HTTP2ParserProxyHandler : ChannelInboundHandler {
175
+ typealias InboundIn = ByteBuffer
176
+
177
+ private let maxCachedClosedStreams : Int
178
+
179
+ init ( maxCachedClosedStreams: Int ) {
180
+ self . maxCachedClosedStreams = maxCachedClosedStreams
181
+ }
182
+
183
+ func channelRead( ctx: ChannelHandlerContext , data: NIOAny ) {
184
+ XCTAssertNoThrow ( try ctx. pipeline. add (
185
+ handler: HTTP2Parser ( mode: . server, maxCachedClosedStreams: maxCachedClosedStreams
186
+ ) ) . wait ( ) )
187
+ ctx. fireChannelRead ( data)
188
+ _ = ctx. pipeline. remove ( ctx: ctx)
189
+ }
190
+ }
191
+
172
192
class SimpleClientServerTests : XCTestCase {
173
193
var clientChannel : EmbeddedChannel !
174
194
var serverChannel : EmbeddedChannel !
@@ -190,6 +210,13 @@ class SimpleClientServerTests: XCTestCase {
190
210
try self . assertDoHandshake ( client: self . clientChannel, server: self . serverChannel)
191
211
}
192
212
213
+ /// Establish a basic HTTP/2 connection where the HTTP2Parser handler is added after the channel has been activated.
214
+ func basicHTTP2DynamicPipelineConnection( maxCachedClosedStreams: Int = 1024 ) throws {
215
+ XCTAssertNoThrow ( try self . clientChannel. pipeline. add ( handler: HTTP2Parser ( mode: . client, maxCachedClosedStreams: maxCachedClosedStreams) ) . wait ( ) )
216
+ XCTAssertNoThrow ( try self . serverChannel. pipeline. add ( handler: HTTP2ParserProxyHandler ( maxCachedClosedStreams: maxCachedClosedStreams) ) . wait ( ) )
217
+ try self . assertDoHandshake ( client: self . clientChannel, server: self . serverChannel)
218
+ }
219
+
193
220
func testBasicRequestResponse( ) throws {
194
221
// Begin by getting the connection up.
195
222
try self . basicHTTP2Connection ( )
@@ -216,6 +243,32 @@ class SimpleClientServerTests: XCTestCase {
216
243
XCTAssertNoThrow ( try self . serverChannel. finish ( ) )
217
244
}
218
245
246
+ func testBasicRequestResponseWithDynamicPipeline( ) throws {
247
+ // Begin by getting the connection up.
248
+ try self . basicHTTP2DynamicPipelineConnection ( )
249
+
250
+ // We're now going to try to send a request from the client to the server.
251
+ let headers = HTTPHeaders ( [ ( " :path " , " / " ) , ( " :method " , " POST " ) , ( " :scheme " , " https " ) , ( " :authority " , " localhost " ) ] )
252
+ var requestBody = self . clientChannel. allocator. buffer ( capacity: 128 )
253
+ requestBody. write ( staticString: " A simple HTTP/2 request. " )
254
+
255
+ let clientStreamID = HTTP2StreamID ( )
256
+ let reqFrame = HTTP2Frame ( streamID: clientStreamID, payload: . headers( headers) )
257
+ var reqBodyFrame = HTTP2Frame ( streamID: clientStreamID, payload: . data( . byteBuffer( requestBody) ) )
258
+ reqBodyFrame. flags. insert ( . endStream)
259
+
260
+ let serverStreamID = try self . assertFramesRoundTrip ( frames: [ reqFrame, reqBodyFrame] , sender: self . clientChannel, receiver: self . serverChannel) . first!. streamID
261
+
262
+ // Let's send a quick response back.
263
+ let responseHeaders = HTTPHeaders ( [ ( " :status " , " 200 " ) , ( " content-length " , " 0 " ) ] )
264
+ var respFrame = HTTP2Frame ( streamID: serverStreamID, payload: . headers( responseHeaders) )
265
+ respFrame. flags. insert ( . endStream)
266
+ try self . assertFramesRoundTrip ( frames: [ respFrame] , sender: self . serverChannel, receiver: self . clientChannel)
267
+
268
+ XCTAssertNoThrow ( try self . clientChannel. finish ( ) )
269
+ XCTAssertNoThrow ( try self . serverChannel. finish ( ) )
270
+ }
271
+
219
272
func testManyRequestsAtOnce( ) throws {
220
273
// Begin by getting the connection up.
221
274
try self . basicHTTP2Connection ( )
0 commit comments