@@ -183,9 +183,9 @@ class SimpleClientServerTests: XCTestCase {
183183 }
184184
185185 /// Establish a basic HTTP/2 connection.
186- func basicHTTP2Connection( ) throws {
187- XCTAssertNoThrow ( try self . clientChannel. pipeline. add ( handler: HTTP2Parser ( mode: . client) ) . wait ( ) )
188- XCTAssertNoThrow ( try self . serverChannel. pipeline. add ( handler: HTTP2Parser ( mode: . server) ) . wait ( ) )
186+ func basicHTTP2Connection( maxCachedClosedStreams : Int = 1024 ) throws {
187+ XCTAssertNoThrow ( try self . clientChannel. pipeline. add ( handler: HTTP2Parser ( mode: . client, maxCachedClosedStreams : maxCachedClosedStreams ) ) . wait ( ) )
188+ XCTAssertNoThrow ( try self . serverChannel. pipeline. add ( handler: HTTP2Parser ( mode: . server, maxCachedClosedStreams : maxCachedClosedStreams ) ) . wait ( ) )
189189 try self . assertDoHandshake ( client: self . clientChannel, server: self . serverChannel)
190190 }
191191
@@ -1230,4 +1230,91 @@ class SimpleClientServerTests: XCTestCase {
12301230 XCTAssertNoThrow ( try self . clientChannel. finish ( ) )
12311231 XCTAssertNoThrow ( try self . serverChannel. finish ( ) )
12321232 }
1233+
1234+ func testManyConcurrentInactiveStreams( ) throws {
1235+ let maxCachedClosedStreams = 128
1236+
1237+ // Begin by getting the connection up.
1238+ try self . basicHTTP2Connection ( maxCachedClosedStreams: maxCachedClosedStreams)
1239+
1240+ // Obtain some request data.
1241+ let requestHeaders = HTTPHeaders ( [ ( " :path " , " / " ) , ( " :method " , " POST " ) , ( " :scheme " , " https " ) , ( " :authority " , " localhost " ) ] )
1242+ var requestBody = self . clientChannel. allocator. buffer ( capacity: 128 )
1243+ requestBody. write ( staticString: " A simple HTTP/2 request. " )
1244+ let responseHeaders = HTTPHeaders ( [ ( " :status " , " 200 " ) , ( " content-length " , " 0 " ) ] )
1245+
1246+ // We're going to initiate and then close more than maxCachedClosedStreams streams.
1247+ // Nothing bad should happen here.
1248+ for _ in 0 ... maxCachedClosedStreams {
1249+ // We're now going to try to send a request from the client to the server.
1250+ let clientStreamID = HTTP2StreamID ( )
1251+ let reqFrame = HTTP2Frame ( streamID: clientStreamID, payload: . headers( requestHeaders) )
1252+ var reqBodyFrame = HTTP2Frame ( streamID: clientStreamID, payload: . data( . byteBuffer( requestBody) ) )
1253+ reqBodyFrame. endStream = true
1254+
1255+ let serverStreamID = try self . assertFramesRoundTrip ( frames: [ reqFrame, reqBodyFrame] , sender: self . clientChannel, receiver: self . serverChannel) . first!. streamID
1256+
1257+ // Let's send a quick response back.
1258+ var respFrame = HTTP2Frame ( streamID: serverStreamID, payload: . headers( responseHeaders) )
1259+ respFrame. endStream = true
1260+ try self . assertFramesRoundTrip ( frames: [ respFrame] , sender: self . serverChannel, receiver: self . clientChannel)
1261+ }
1262+
1263+ XCTAssertNoThrow ( try self . clientChannel. finish ( ) )
1264+ XCTAssertNoThrow ( try self . serverChannel. finish ( ) )
1265+ }
1266+
1267+ func testDontRemoveActiveStreams( ) throws {
1268+ // This is a bit of a regression test, not a generally useful one. See https://github.com/apple/swift-nio-http2/pull/11/
1269+ // for more.
1270+ let maxCachedClosedStreams = 128
1271+
1272+ // Begin by getting the connection up.
1273+ try self . basicHTTP2Connection ( maxCachedClosedStreams: maxCachedClosedStreams)
1274+
1275+ // Obtain some request data.
1276+ let requestHeaders = HTTPHeaders ( [ ( " :path " , " / " ) , ( " :method " , " POST " ) , ( " :scheme " , " https " ) , ( " :authority " , " localhost " ) ] )
1277+ var requestBody = self . clientChannel. allocator. buffer ( capacity: 128 )
1278+ requestBody. write ( staticString: " A simple HTTP/2 request. " )
1279+ let responseHeaders = HTTPHeaders ( [ ( " :status " , " 200 " ) , ( " content-length " , " 0 " ) ] )
1280+
1281+ // We're going to initiate and then close one fewer than maxCachedClosedStreams streams.
1282+ // Nothing bad should happen here.
1283+ for _ in 0 ..< ( maxCachedClosedStreams - 2 ) {
1284+ let clientStreamID = HTTP2StreamID ( )
1285+ let reqFrame = HTTP2Frame ( streamID: clientStreamID, payload: . headers( requestHeaders) )
1286+ var reqBodyFrame = HTTP2Frame ( streamID: clientStreamID, payload: . data( . byteBuffer( requestBody) ) )
1287+ reqBodyFrame. endStream = true
1288+
1289+ let serverStreamID = try self . assertFramesRoundTrip ( frames: [ reqFrame, reqBodyFrame] , sender: self . clientChannel, receiver: self . serverChannel) . first!. streamID
1290+
1291+ // Let's send a quick response back.
1292+ var respFrame = HTTP2Frame ( streamID: serverStreamID, payload: . headers( responseHeaders) )
1293+ respFrame. endStream = true
1294+ try self . assertFramesRoundTrip ( frames: [ respFrame] , sender: self . serverChannel, receiver: self . clientChannel)
1295+ }
1296+
1297+ // Ok, now we're going to open *two* streams. In the old, broken code, the opening of the second
1298+ // stream would discard the first *open* stream, instead of one of the dead old ones.
1299+ let clientStreamIDs = ( 0 ..< 2 ) . map { _ in HTTP2StreamID ( ) }
1300+ let clientFrames = clientStreamIDs. map { HTTP2Frame ( streamID: $0, payload: . headers( requestHeaders) ) }
1301+ let serverStreamIDs = try self . assertFramesRoundTrip ( frames: clientFrames, sender: self . clientChannel, receiver: self . serverChannel) . map { $0. streamID }
1302+
1303+ // Now we're going to send the two data frames for these streams.
1304+ // In the old broken version of this code, this will fail because we accidentally deleted one of our two *open*
1305+ // stream states, instead of one of the 1024 closed ones. Booleans are hard.
1306+ let clientDataFrames = clientStreamIDs. map { HTTP2Frame ( streamID: $0, payload: . data( . byteBuffer( requestBody) ) ) }
1307+ try self . assertFramesRoundTrip ( frames: clientDataFrames, sender: self . clientChannel, receiver: self . serverChannel)
1308+
1309+ // Clean it up with the server now.
1310+ let serverFrames = serverStreamIDs. map { streamID -> HTTP2Frame in
1311+ var respFrame = HTTP2Frame ( streamID: streamID, payload: . headers( responseHeaders) )
1312+ respFrame. endStream = true
1313+ return respFrame
1314+ }
1315+ try self . assertFramesRoundTrip ( frames: serverFrames, sender: self . serverChannel, receiver: self . clientChannel)
1316+
1317+ XCTAssertNoThrow ( try self . clientChannel. finish ( ) )
1318+ XCTAssertNoThrow ( try self . serverChannel. finish ( ) )
1319+ }
12331320}
0 commit comments