Index: net/quic/quic_session_test.cc |
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc |
index 9b85143f7bab627a41f05d26bf18eb18facc419c..1522f201d7d245ce10719d4262c824b76a6b8580 100644 |
--- a/net/quic/quic_session_test.cc |
+++ b/net/quic/quic_session_test.cc |
@@ -729,7 +729,7 @@ TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedHeadersStream) { |
EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
QuicStreamId stream_id = 5; |
// Write until the header stream is flow control blocked. |
- while (!headers_stream->flow_controller()->IsBlocked() && stream_id < 2000) { |
+ while (!headers_stream->flow_controller()->IsBlocked() && stream_id < 2010) { |
EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
SpdyHeaderBlock headers; |
@@ -744,7 +744,8 @@ TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedHeadersStream) { |
EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); |
EXPECT_FALSE(session_.HasDataToWrite()); |
- EXPECT_TRUE(headers_stream->HasBufferedData()); |
+ // TODO(rtenneti): crbug.com/423586 headers_stream->HasBufferedData is flaky. |
+ // EXPECT_TRUE(headers_stream->HasBufferedData()); |
ramant (doing other things)
2014/10/20 19:05:06
rjshade@: HandshakeUnblocksFlowControlBlockedHeade
|
// Now complete the crypto handshake, resulting in an increased flow control |
// send window. |
@@ -775,6 +776,141 @@ TEST_P(QuicSessionTest, InvalidFlowControlWindowInHandshake) { |
session_.OnConfigNegotiated(); |
} |
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstOutOfOrder) { |
+ // Test that when we receive an out of order stream RST we correctly adjust |
+ // our connection level flow control receive window. |
+ // On close, the stream should mark as consumed all bytes between the highest |
+ // byte consumed so far and the final byte offset from the RST frame. |
+ TestStream* stream = session_.CreateOutgoingDataStream(); |
+ |
+ const QuicStreamOffset kByteOffset = |
+ 1 + kInitialSessionFlowControlWindowForTest / 2; |
+ |
+ // Expect no stream WINDOW_UPDATE frames, as stream read side closed. |
+ EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0); |
+ // We do expect a connection level WINDOW_UPDATE when the stream is reset. |
+ EXPECT_CALL(*connection_, |
+ SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest + |
+ kByteOffset)).Times(1); |
+ |
+ QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED, |
+ kByteOffset); |
+ session_.OnRstStream(rst_frame); |
+ session_.PostProcessAfterData(); |
+ EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed()); |
+} |
+ |
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAndLocalReset) { |
+ // Test the situation where we receive a FIN on a stream, and before we fully |
+ // consume all the data from the sequencer buffer we locally RST the stream. |
+ // The bytes between highest consumed byte, and the final byte offset that we |
+ // determined when the FIN arrived, should be marked as consumed at the |
+ // connection level flow controller when the stream is reset. |
+ TestStream* stream = session_.CreateOutgoingDataStream(); |
+ |
+ const QuicStreamOffset kByteOffset = |
+ 1 + kInitialSessionFlowControlWindowForTest / 2; |
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, IOVector()); |
+ vector<QuicStreamFrame> frames; |
+ frames.push_back(frame); |
+ session_.OnStreamFrames(frames); |
+ session_.PostProcessAfterData(); |
+ |
+ EXPECT_EQ(0u, stream->flow_controller()->bytes_consumed()); |
+ EXPECT_EQ(kByteOffset, |
+ stream->flow_controller()->highest_received_byte_offset()); |
+ |
+ // We only expect to see a connection WINDOW_UPDATE when talking |
+ // QUIC_VERSION_19, as in this case both stream and session flow control |
+ // windows are the same size. In later versions we will not see a connection |
+ // level WINDOW_UPDATE when exhausting a stream, as the stream flow control |
+ // limit is much lower than the connection flow control limit. |
+ if (version() == QUIC_VERSION_19) { |
+ // Expect no stream WINDOW_UPDATE frames, as stream read side closed. |
+ EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0); |
+ // We do expect a connection level WINDOW_UPDATE when the stream is reset. |
+ EXPECT_CALL(*connection_, |
+ SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest + |
+ kByteOffset)).Times(1); |
+ } |
+ |
+ // Reset stream locally. |
+ stream->Reset(QUIC_STREAM_CANCELLED); |
+ EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed()); |
+} |
+ |
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAfterRst) { |
+ // Test that when we RST the stream (and tear down stream state), and then |
+ // receive a FIN from the peer, we correctly adjust our connection level flow |
+ // control receive window. |
+ |
+ // Connection starts with some non-zero highest received byte offset, |
+ // due to other active streams. |
+ const uint64 kInitialConnectionBytesConsumed = 567; |
+ const uint64 kInitialConnectionHighestReceivedOffset = 1234; |
+ EXPECT_LT(kInitialConnectionBytesConsumed, |
+ kInitialConnectionHighestReceivedOffset); |
+ session_.flow_controller()->UpdateHighestReceivedOffset( |
+ kInitialConnectionHighestReceivedOffset); |
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed); |
+ |
+ // Reset our stream: this results in the stream being closed locally. |
+ TestStream* stream = session_.CreateOutgoingDataStream(); |
+ stream->Reset(QUIC_STREAM_CANCELLED); |
+ |
+ // Now receive a response from the peer with a FIN. We should handle this by |
+ // adjusting the connection level flow control receive window to take into |
+ // account the total number of bytes sent by the peer. |
+ const QuicStreamOffset kByteOffset = 5678; |
+ string body = "hello"; |
+ IOVector data = MakeIOVector(body); |
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, data); |
+ vector<QuicStreamFrame> frames; |
+ frames.push_back(frame); |
+ session_.OnStreamFrames(frames); |
+ |
+ QuicStreamOffset total_stream_bytes_sent_by_peer = |
+ kByteOffset + body.length(); |
+ EXPECT_EQ(kInitialConnectionBytesConsumed + total_stream_bytes_sent_by_peer, |
+ session_.flow_controller()->bytes_consumed()); |
+ EXPECT_EQ( |
+ kInitialConnectionHighestReceivedOffset + total_stream_bytes_sent_by_peer, |
+ session_.flow_controller()->highest_received_byte_offset()); |
+} |
+ |
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstAfterRst) { |
+ // Test that when we RST the stream (and tear down stream state), and then |
+ // receive a RST from the peer, we correctly adjust our connection level flow |
+ // control receive window. |
+ |
+ // Connection starts with some non-zero highest received byte offset, |
+ // due to other active streams. |
+ const uint64 kInitialConnectionBytesConsumed = 567; |
+ const uint64 kInitialConnectionHighestReceivedOffset = 1234; |
+ EXPECT_LT(kInitialConnectionBytesConsumed, |
+ kInitialConnectionHighestReceivedOffset); |
+ session_.flow_controller()->UpdateHighestReceivedOffset( |
+ kInitialConnectionHighestReceivedOffset); |
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed); |
+ |
+ // Reset our stream: this results in the stream being closed locally. |
+ TestStream* stream = session_.CreateOutgoingDataStream(); |
+ stream->Reset(QUIC_STREAM_CANCELLED); |
+ |
+ // Now receive a RST from the peer. We should handle this by adjusting the |
+ // connection level flow control receive window to take into account the total |
+ // number of bytes sent by the peer. |
+ const QuicStreamOffset kByteOffset = 5678; |
+ QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED, |
+ kByteOffset); |
+ session_.OnRstStream(rst_frame); |
+ |
+ EXPECT_EQ(kInitialConnectionBytesConsumed + kByteOffset, |
+ session_.flow_controller()->bytes_consumed()); |
+ EXPECT_EQ(kInitialConnectionHighestReceivedOffset + kByteOffset, |
+ session_.flow_controller()->highest_received_byte_offset()); |
+} |
+ |
TEST_P(QuicSessionTest, InvalidStreamFlowControlWindowInHandshake) { |
// Test that receipt of an invalid (< default) stream flow control window from |
// the peer results in the connection being torn down. |
@@ -856,6 +992,35 @@ TEST_P(QuicSessionTest, WindowUpdateUnblocksHeadersStream) { |
EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
} |
+TEST_P(QuicSessionTest, TooManyUnfinishedStreamsCauseConnectionClose) { |
+ // If a buggy/malicious peer creates too many streams that are not ended with |
+ // a FIN or RST then we send a connection close. |
+ FLAGS_close_quic_connection_unfinished_streams_2 = true; |
+ |
+ EXPECT_CALL(*connection_, |
+ SendConnectionClose(QUIC_TOO_MANY_UNFINISHED_STREAMS)).Times(1); |
+ |
+ const int kMaxStreams = 5; |
+ QuicSessionPeer::SetMaxOpenStreams(&session_, kMaxStreams); |
+ |
+ // Create kMaxStreams + 1 data streams, and close them all without receiving a |
+ // FIN or a RST from the client. |
+ const int kFirstStreamId = kClientDataStreamId1; |
+ const int kFinalStreamId = kClientDataStreamId1 + 2 * kMaxStreams + 1; |
+ for (int i = kFirstStreamId; i < kFinalStreamId; i += 2) { |
+ QuicStreamFrame data1(i, false, 0, MakeIOVector("HT")); |
+ vector<QuicStreamFrame> frames; |
+ frames.push_back(data1); |
+ session_.OnStreamFrames(frames); |
+ EXPECT_EQ(1u, session_.GetNumOpenStreams()); |
+ session_.CloseStream(i); |
+ } |
+ |
+ // Called after any new data is received by the session, and triggers the call |
+ // to close the connection. |
+ session_.PostProcessAfterData(); |
+} |
+ |
} // namespace |
} // namespace test |
} // namespace net |