@@ -169,6 +169,26 @@ final class ClosedEventVsFrameOrderingHandler: ChannelInboundHandler {
169169 }
170170}
171171
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+
172192class SimpleClientServerTests : XCTestCase {
173193 var clientChannel : EmbeddedChannel !
174194 var serverChannel : EmbeddedChannel !
@@ -190,6 +210,13 @@ class SimpleClientServerTests: XCTestCase {
190210 try self . assertDoHandshake ( client: self . clientChannel, server: self . serverChannel)
191211 }
192212
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+
193220 func testBasicRequestResponse( ) throws {
194221 // Begin by getting the connection up.
195222 try self . basicHTTP2Connection ( )
@@ -216,6 +243,32 @@ class SimpleClientServerTests: XCTestCase {
216243 XCTAssertNoThrow ( try self . serverChannel. finish ( ) )
217244 }
218245
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+
219272 func testManyRequestsAtOnce( ) throws {
220273 // Begin by getting the connection up.
221274 try self . basicHTTP2Connection ( )
0 commit comments