@@ -124,6 +124,19 @@ final class FlushCounter: ChannelOutboundHandler {
124
124
}
125
125
126
126
127
+ final class ReadCompleteCounter : ChannelInboundHandler {
128
+ typealias InboundIn = Any
129
+ typealias InboundOut = Any
130
+
131
+ var readCompleteCount = 0
132
+
133
+ func channelReadComplete( context: ChannelHandlerContext ) {
134
+ self . readCompleteCount += 1
135
+ context. fireChannelReadComplete ( )
136
+ }
137
+ }
138
+
139
+
127
140
/// A channel handler that sends a response in response to a HEADERS frame.
128
141
final class QuickResponseHandler : ChannelInboundHandler {
129
142
typealias InboundIn = HTTP2Frame
@@ -1497,4 +1510,118 @@ final class HTTP2StreamMultiplexerTests: XCTestCase {
1497
1510
XCTAssertEqual ( try self . channel. sentFrames ( ) . count, 10 )
1498
1511
XCTAssertEqual ( flushCounter. flushCount, 1 )
1499
1512
}
1513
+
1514
+ func testMultiplexerDoesntFireReadCompleteForEachFrame( ) {
1515
+ // We need to activate the underlying channel here.
1516
+ XCTAssertNoThrow ( try self . channel. connect ( to: SocketAddress ( ipAddress: " 127.0.0.1 " , port: 80 ) ) . wait ( ) )
1517
+
1518
+ let frameRecorder = InboundFrameRecorder ( )
1519
+ let readCompleteCounter = ReadCompleteCounter ( )
1520
+
1521
+ let multiplexer = HTTP2StreamMultiplexer ( mode: . server, channel: self . channel) { ( childChannel, _) in
1522
+ return childChannel. pipeline. addHandler ( frameRecorder) . flatMap {
1523
+ childChannel. pipeline. addHandler ( readCompleteCounter)
1524
+ }
1525
+ }
1526
+ XCTAssertNoThrow ( try self . channel. pipeline. addHandler ( multiplexer) . wait ( ) )
1527
+
1528
+ XCTAssertEqual ( frameRecorder. receivedFrames. count, 0 )
1529
+ XCTAssertEqual ( readCompleteCounter. readCompleteCount, 0 )
1530
+
1531
+ // Wake up and activate the stream.
1532
+ let requestHeaders = HPACKHeaders ( [ ( " :path " , " / " ) , ( " :method " , " GET " ) , ( " :authority " , " localhost " ) , ( " :scheme " , " https " ) ] )
1533
+ let requestFrame = HTTP2Frame ( streamID: 1 , payload: . headers( . init( headers: requestHeaders, endStream: false ) ) )
1534
+ self . channel. pipeline. fireChannelRead ( NIOAny ( requestFrame) )
1535
+ self . activateStream ( 1 )
1536
+ self . channel. embeddedEventLoop. run ( )
1537
+
1538
+ XCTAssertEqual ( frameRecorder. receivedFrames. count, 1 )
1539
+ XCTAssertEqual ( readCompleteCounter. readCompleteCount, 0 )
1540
+
1541
+ // Now we're going to send 9 data frames.
1542
+ var requestData = self . channel. allocator. buffer ( capacity: 1024 )
1543
+ requestData. writeBytes ( " Hello world! " . utf8)
1544
+ let dataFrames = repeatElement ( HTTP2Frame ( streamID: 1 , payload: . data( . init( data: . byteBuffer( requestData) , endStream: false ) ) ) , count: 9 )
1545
+
1546
+ for frame in dataFrames {
1547
+ self . channel. pipeline. fireChannelRead ( NIOAny ( frame) )
1548
+ }
1549
+
1550
+ // We should have 10 reads, and zero read completes.
1551
+ XCTAssertEqual ( frameRecorder. receivedFrames. count, 10 )
1552
+ XCTAssertEqual ( readCompleteCounter. readCompleteCount, 0 )
1553
+
1554
+ // Fire read complete on the parent and it'll propagate to the child.
1555
+ self . channel. pipeline. fireChannelReadComplete ( )
1556
+
1557
+ // We should have 10 reads, and one read complete.
1558
+ XCTAssertEqual ( frameRecorder. receivedFrames. count, 10 )
1559
+ XCTAssertEqual ( readCompleteCounter. readCompleteCount, 1 )
1560
+
1561
+ // If we fire a new read complete on the parent, the child doesn't see it this time, as it received no frames.
1562
+ self . channel. pipeline. fireChannelReadComplete ( )
1563
+ XCTAssertEqual ( frameRecorder. receivedFrames. count, 10 )
1564
+ XCTAssertEqual ( readCompleteCounter. readCompleteCount, 1 )
1565
+ }
1566
+
1567
+ func testMultiplexerCorrectlyTellsAllStreamsAboutReadComplete( ) {
1568
+ // We need to activate the underlying channel here.
1569
+ XCTAssertNoThrow ( try self . channel. connect ( to: SocketAddress ( ipAddress: " 127.0.0.1 " , port: 80 ) ) . wait ( ) )
1570
+
1571
+ // These are deliberately getting inserted to all streams. The test above confirms the single-stream
1572
+ // behaviour is correct.
1573
+ let frameRecorder = InboundFrameRecorder ( )
1574
+ let readCompleteCounter = ReadCompleteCounter ( )
1575
+
1576
+ let multiplexer = HTTP2StreamMultiplexer ( mode: . server, channel: self . channel) { ( childChannel, _) in
1577
+ return childChannel. pipeline. addHandler ( frameRecorder) . flatMap {
1578
+ childChannel. pipeline. addHandler ( readCompleteCounter)
1579
+ }
1580
+ }
1581
+ XCTAssertNoThrow ( try self . channel. pipeline. addHandler ( multiplexer) . wait ( ) )
1582
+
1583
+ XCTAssertEqual ( frameRecorder. receivedFrames. count, 0 )
1584
+ XCTAssertEqual ( readCompleteCounter. readCompleteCount, 0 )
1585
+
1586
+ // Wake up and activate the streams.
1587
+ let requestHeaders = HPACKHeaders ( [ ( " :path " , " / " ) , ( " :method " , " GET " ) , ( " :authority " , " localhost " ) , ( " :scheme " , " https " ) ] )
1588
+
1589
+ for streamID in [ HTTP2StreamID ( 1 ) , HTTP2StreamID ( 3 ) , HTTP2StreamID ( 5 ) ] {
1590
+ let requestFrame = HTTP2Frame ( streamID: streamID, payload: . headers( . init( headers: requestHeaders, endStream: false ) ) )
1591
+ self . channel. pipeline. fireChannelRead ( NIOAny ( requestFrame) )
1592
+ self . activateStream ( streamID)
1593
+ }
1594
+ self . channel. embeddedEventLoop. run ( )
1595
+
1596
+ XCTAssertEqual ( frameRecorder. receivedFrames. count, 3 )
1597
+ XCTAssertEqual ( readCompleteCounter. readCompleteCount, 0 )
1598
+
1599
+ // Firing in readComplete causes a readComplete for each stream.
1600
+ self . channel. pipeline. fireChannelReadComplete ( )
1601
+ XCTAssertEqual ( frameRecorder. receivedFrames. count, 3 )
1602
+ XCTAssertEqual ( readCompleteCounter. readCompleteCount, 3 )
1603
+
1604
+ // Now we're going to send a data frame on stream 1.
1605
+ var requestData = self . channel. allocator. buffer ( capacity: 1024 )
1606
+ requestData. writeBytes ( " Hello world! " . utf8)
1607
+ let frame = HTTP2Frame ( streamID: 1 , payload: . data( . init( data: . byteBuffer( requestData) , endStream: false ) ) )
1608
+ self . channel. pipeline. fireChannelRead ( NIOAny ( frame) )
1609
+
1610
+ // We should have 4 reads, and 3 read completes.
1611
+ XCTAssertEqual ( frameRecorder. receivedFrames. count, 4 )
1612
+ XCTAssertEqual ( readCompleteCounter. readCompleteCount, 3 )
1613
+
1614
+ // Fire read complete on the parent and it'll propagate to the child, but only to the one
1615
+ // that saw a frame.
1616
+ self . channel. pipeline. fireChannelReadComplete ( )
1617
+
1618
+ // We should have 4 reads, and 4 read completes.
1619
+ XCTAssertEqual ( frameRecorder. receivedFrames. count, 4 )
1620
+ XCTAssertEqual ( readCompleteCounter. readCompleteCount, 4 )
1621
+
1622
+ // If we fire a new read complete on the parent, the children don't see it.
1623
+ self . channel. pipeline. fireChannelReadComplete ( )
1624
+ XCTAssertEqual ( frameRecorder. receivedFrames. count, 4 )
1625
+ XCTAssertEqual ( readCompleteCounter. readCompleteCount, 4 )
1626
+ }
1500
1627
}
0 commit comments