@@ -1992,6 +1992,79 @@ final class HTTP2FramePayloadStreamMultiplexerTests: XCTestCase {
1992
1992
frames [ 0 ] . assertHeadersFrame ( endStream: false , streamID: 1 , headers: goodHeaders, priority: nil , type: . request)
1993
1993
frames [ 1 ] . assertHeadersFrame ( endStream: false , streamID: 3 , headers: badHeaders, priority: nil , type: . doNotValidate)
1994
1994
}
1995
+
1996
+ func testPendingReadsAreFlushedEvenWithoutUnsatisfiedReadOnChannelInactive( ) throws {
1997
+ let goodHeaders = HPACKHeaders ( [
1998
+ ( " :path " , " / " ) , ( " :method " , " GET " ) , ( " :scheme " , " https " ) , ( " :authority " , " localhost " )
1999
+ ] )
2000
+
2001
+ let multiplexer = HTTP2StreamMultiplexer ( mode: . client, channel: self . channel) { channel in
2002
+ XCTFail ( " Server push is unexpected " )
2003
+ return channel. eventLoop. makeSucceededFuture ( ( ) )
2004
+ }
2005
+ XCTAssertNoThrow ( try self . channel. pipeline. addHandler ( multiplexer) . wait ( ) )
2006
+
2007
+ // We need to activate the underlying channel here.
2008
+ XCTAssertNoThrow ( try self . channel. connect ( to: SocketAddress ( ipAddress: " 127.0.0.1 " , port: 80 ) ) . wait ( ) )
2009
+
2010
+ // Now create two child channels with error recording handlers in them. Save one, ignore the other.
2011
+ let consumer = ReadAndFrameConsumer ( )
2012
+ var childChannel : Channel !
2013
+ multiplexer. createStreamChannel ( promise: nil ) { channel in
2014
+ childChannel = channel
2015
+ return channel. pipeline. addHandler ( consumer)
2016
+ }
2017
+ self . channel. embeddedEventLoop. run ( )
2018
+
2019
+ let streamID = HTTP2StreamID ( 1 )
2020
+
2021
+ let payload = HTTP2Frame . FramePayload. Headers ( headers: goodHeaders, endStream: true )
2022
+ XCTAssertNoThrow ( try childChannel. writeAndFlush ( HTTP2Frame . FramePayload. headers ( payload) ) . wait ( ) )
2023
+
2024
+ let frames = try self . channel. sentFrames ( )
2025
+ XCTAssertEqual ( frames. count, 1 )
2026
+ frames. first? . assertHeadersFrameMatches ( this: HTTP2Frame ( streamID: streamID, payload: . headers( payload) ) )
2027
+
2028
+ XCTAssertEqual ( consumer. readCount, 1 )
2029
+
2030
+ // 1. pass header onwards
2031
+
2032
+ let responseHeaderPayload = HTTP2Frame . FramePayload. headers ( . init( headers: [ " :status " : " 200 " ] ) )
2033
+ XCTAssertNoThrow ( try self . channel. writeInbound ( HTTP2Frame ( streamID: streamID, payload: responseHeaderPayload) ) )
2034
+ XCTAssertEqual ( consumer. receivedFrames. count, 1 )
2035
+ XCTAssertEqual ( consumer. readCompleteCount, 1 )
2036
+ XCTAssertEqual ( consumer. readCount, 2 )
2037
+
2038
+ consumer. forwardRead = false
2039
+
2040
+ // 2. pass body onwards
2041
+
2042
+ let responseBody1 = HTTP2Frame . FramePayload. data ( . init( data: . byteBuffer( . init( string: " foo " ) ) ) )
2043
+ XCTAssertNoThrow ( try self . channel. writeInbound ( HTTP2Frame ( streamID: streamID, payload: responseBody1) ) )
2044
+ XCTAssertEqual ( consumer. receivedFrames. count, 2 )
2045
+ XCTAssertEqual ( consumer. readCompleteCount, 2 )
2046
+ XCTAssertEqual ( consumer. readCount, 3 )
2047
+ XCTAssertEqual ( consumer. readPending, true )
2048
+
2049
+ // 3. pass on more body - should not change a thing, since read is pending in consumer
2050
+
2051
+ let responseBody2 = HTTP2Frame . FramePayload. data ( . init( data: . byteBuffer( . init( string: " bar " ) ) , endStream: true ) )
2052
+ XCTAssertNoThrow ( try self . channel. writeInbound ( HTTP2Frame ( streamID: streamID, payload: responseBody2) ) )
2053
+ XCTAssertEqual ( consumer. receivedFrames. count, 2 )
2054
+ XCTAssertEqual ( consumer. readCompleteCount, 2 )
2055
+ XCTAssertEqual ( consumer. readCount, 3 )
2056
+ XCTAssertEqual ( consumer. readPending, true )
2057
+
2058
+ // 4. signal stream is closed – this should force forward all pending frames
2059
+
2060
+ XCTAssertEqual ( consumer. channelInactiveCount, 0 )
2061
+ self . channel. pipeline. fireUserInboundEventTriggered ( StreamClosedEvent ( streamID: streamID, reason: nil ) )
2062
+ XCTAssertEqual ( consumer. receivedFrames. count, 3 )
2063
+ XCTAssertEqual ( consumer. readCompleteCount, 3 )
2064
+ XCTAssertEqual ( consumer. readCount, 3 )
2065
+ XCTAssertEqual ( consumer. channelInactiveCount, 1 )
2066
+ XCTAssertEqual ( consumer. readPending, true )
2067
+ }
1995
2068
}
1996
2069
1997
2070
private final class ErrorRecorder : ChannelInboundHandler {
@@ -2004,3 +2077,58 @@ private final class ErrorRecorder: ChannelInboundHandler {
2004
2077
context. fireErrorCaught ( error)
2005
2078
}
2006
2079
}
2080
+
2081
+ private final class ReadAndFrameConsumer : ChannelInboundHandler , ChannelOutboundHandler {
2082
+ typealias InboundIn = HTTP2Frame . FramePayload
2083
+ typealias OutboundIn = HTTP2Frame . FramePayload
2084
+
2085
+ private( set) var receivedFrames : [ HTTP2Frame . FramePayload ] = [ ]
2086
+ private( set) var readCount = 0
2087
+ private( set) var readCompleteCount = 0
2088
+ private( set) var channelInactiveCount = 0
2089
+ private( set) var readPending = false
2090
+
2091
+ var forwardRead = true {
2092
+ didSet {
2093
+ if self . forwardRead, self . readPending {
2094
+ self . context. read ( )
2095
+ self . readPending = false
2096
+ }
2097
+ }
2098
+ }
2099
+
2100
+ var context : ChannelHandlerContext !
2101
+
2102
+ func handlerAdded( context: ChannelHandlerContext ) {
2103
+ self . context = context
2104
+ }
2105
+
2106
+ func handlerRemoved( context: ChannelHandlerContext ) {
2107
+ self . context = context
2108
+ }
2109
+
2110
+ func channelRead( context: ChannelHandlerContext , data: NIOAny ) {
2111
+ self . receivedFrames. append ( self . unwrapInboundIn ( data) )
2112
+ context. fireChannelRead ( data)
2113
+ }
2114
+
2115
+ func channelReadComplete( context: ChannelHandlerContext ) {
2116
+ self . readCompleteCount += 1
2117
+ context. fireChannelReadComplete ( )
2118
+ }
2119
+
2120
+ func channelInactive( context: ChannelHandlerContext ) {
2121
+ self . channelInactiveCount += 1
2122
+ context. fireChannelInactive ( )
2123
+ }
2124
+
2125
+ func read( context: ChannelHandlerContext ) {
2126
+ self . readCount += 1
2127
+ if forwardRead {
2128
+ context. read ( )
2129
+ self . readPending = false
2130
+ } else {
2131
+ self . readPending = true
2132
+ }
2133
+ }
2134
+ }
0 commit comments