Add a regression test for b/488057588. PiperOrigin-RevId: 884597309
diff --git a/quiche/quic/core/http/quic_spdy_stream_test.cc b/quiche/quic/core/http/quic_spdy_stream_test.cc index 401fdad..8acea52 100644 --- a/quiche/quic/core/http/quic_spdy_stream_test.cc +++ b/quiche/quic/core/http/quic_spdy_stream_test.cc
@@ -30,6 +30,7 @@ #include "quiche/quic/core/quic_connection.h" #include "quiche/quic/core/quic_stream_priority.h" #include "quiche/quic/core/quic_stream_sequencer_buffer.h" +#include "quiche/quic/core/quic_types.h" #include "quiche/quic/core/quic_utils.h" #include "quiche/quic/core/quic_versions.h" #include "quiche/quic/core/quic_write_blocked_list.h" @@ -250,12 +251,21 @@ if (!should_process_data_) { return; } - char buffer[2048]; - struct iovec vec; - vec.iov_base = buffer; - vec.iov_len = ABSL_ARRAYSIZE(buffer); - size_t bytes_read = Readv(&vec, 1); - data_ += std::string(buffer, bytes_read); + if (read_side_closed()) { + return; + } + if (HasBytesToRead()) { + char buffer[2048]; + struct iovec vec; + vec.iov_base = buffer; + vec.iov_len = ABSL_ARRAYSIZE(buffer); + size_t bytes_read = Readv(&vec, 1); + data_ += std::string(buffer, bytes_read); + } + if (!sequencer()->IsClosed() || read_side_closed()) { + return; + } + OnFinRead(); } void OnSoonToBeDestroyed() override { @@ -3825,6 +3835,62 @@ } } +// Regression test for b/488057588. +TEST_P(QuicSpdyStreamTest, ReadSideNotClosedAfterStopReading) { + if (!IsIetfQuic()) { + return; + } + SetQuicReloadableFlag(quic_clear_body_manager_along_with_sequencer, true); + + Initialize(kShouldProcessData); + + QuicHeaderList headers = ProcessHeaders(false, headers_); + stream_->ConsumeHeaderList(); + + // In Envoy QUIC, application can stop processing data before getting read + // blocked. + stream_->set_should_process_data(false); + std::string body = "this is the body"; + std::string data = DataFrame(body); + QuicStreamOffset offset = 0; + QuicStreamFrame frame1(GetNthClientInitiatedBidirectionalId(0), /*fin=*/false, + offset, data); + offset += data.size(); + stream_->OnStreamFrame(frame1); + EXPECT_TRUE(stream_->sequencer()->HasBytesToRead()); + EXPECT_TRUE(QuicSpdyStreamPeer::BodyManager(stream_).HasBytesToRead()); + + // QUIC gets blocked. + stream_->sequencer()->SetBlockedUntilFlush(); + + // In Envoy QUIC, StopReading can be called after local reset. + stream_->StopReading(); + // Sequencer is not closed but the bytes in body_manager is cleared. + EXPECT_FALSE(stream_->sequencer()->IsClosed()); + EXPECT_FALSE(QuicSpdyStreamPeer::BodyManager(stream_).HasBytesToRead()); + + // FIN does not close the read side since the stream is blocked. + QuicStreamFrame frame2(GetNthClientInitiatedBidirectionalId(0), /*fin=*/true, + offset, "second body"); + stream_->OnStreamFrame(frame2); + EXPECT_FALSE(stream_->sequencer()->IsClosed()); + + // Because sequencer has bytes to read, On DataAvailable is called. + // However, since neither body_manager has bytes to read nor sequencer is + // closed, OnBodyAvailable is not called to close the read side when + // SetUnblocked is called. + EXPECT_FALSE(QuicSpdyStreamPeer::BodyManager(stream_).HasBytesToRead()); + EXPECT_FALSE(stream_->sequencer()->IsClosed()); + EXPECT_TRUE(stream_->sequencer()->HasBytesToRead()); + + // Application gets unblocked. + stream_->set_should_process_data(true); + // QUIC gets unblocked. + stream_->sequencer()->SetUnblocked(); + + EXPECT_FALSE(stream_->read_side_closed()); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quiche/quic/test_tools/quic_spdy_stream_peer.cc b/quiche/quic/test_tools/quic_spdy_stream_peer.cc index 972440b..61cea11 100644 --- a/quiche/quic/test_tools/quic_spdy_stream_peer.cc +++ b/quiche/quic/test_tools/quic_spdy_stream_peer.cc
@@ -7,6 +7,7 @@ #include <utility> #include "quiche/quic/core/http/quic_spdy_stream.h" +#include "quiche/quic/core/http/quic_spdy_stream_body_manager.h" #include "quiche/quic/test_tools/quic_test_utils.h" namespace quic { @@ -33,5 +34,10 @@ stream->header_decoding_delay_ = delay; } +QuicSpdyStreamBodyManager& QuicSpdyStreamPeer::BodyManager( + QuicSpdyStream* stream) { + return stream->body_manager_; +} + } // namespace test } // namespace quic
diff --git a/quiche/quic/test_tools/quic_spdy_stream_peer.h b/quiche/quic/test_tools/quic_spdy_stream_peer.h index f5c2e44..7b7723f 100644 --- a/quiche/quic/test_tools/quic_spdy_stream_peer.h +++ b/quiche/quic/test_tools/quic_spdy_stream_peer.h
@@ -5,6 +5,7 @@ #ifndef QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_STREAM_PEER_H_ #define QUICHE_QUIC_TEST_TOOLS_QUIC_SPDY_STREAM_PEER_H_ +#include "quiche/quic/core/http/quic_spdy_stream_body_manager.h" #include "quiche/quic/core/quic_ack_listener_interface.h" #include "quiche/quic/core/quic_interval_set.h" #include "quiche/quic/core/quic_time.h" @@ -27,6 +28,7 @@ static bool OnHeadersFrameEnd(QuicSpdyStream* stream); static void set_header_decoding_delay(QuicSpdyStream* stream, QuicTime::Delta delay); + static QuicSpdyStreamBodyManager& BodyManager(QuicSpdyStream* stream); }; } // namespace test