Introduce the concept of STOP_SENDING in gQUIC. gQUIC uses to have RST(QUIC_STREAM_NO_ERROR) to achieve STOP_SENDING semantics. This change would unify this behavior with IETF STOP_SENDING. Protected by quic_reloadable_flag_quic_split_up_send_rst. PiperOrigin-RevId: 336959906 Change-Id: Ie1daa4a96bcdd627354b1425bd736a91f153beab
diff --git a/quic/core/http/quic_spdy_server_stream_base.cc b/quic/core/http/quic_spdy_server_stream_base.cc index 2e2e2ec..aee5045 100644 --- a/quic/core/http/quic_spdy_server_stream_base.cc +++ b/quic/core/http/quic_spdy_server_stream_base.cc
@@ -28,7 +28,11 @@ DCHECK(fin_sent() || !session()->connection()->connected()); // Tell the peer to stop sending further data. QUIC_DVLOG(1) << " Server: Send QUIC_STREAM_NO_ERROR on stream " << id(); - Reset(QUIC_STREAM_NO_ERROR); + if (session()->split_up_send_rst()) { + MaybeSendStopSending(QUIC_STREAM_NO_ERROR); + } else { + Reset(QUIC_STREAM_NO_ERROR); + } } QuicSpdyStream::CloseWriteSide(); @@ -40,7 +44,11 @@ DCHECK(fin_sent()); // Tell the peer to stop sending further data. QUIC_DVLOG(1) << " Server: Send QUIC_STREAM_NO_ERROR on stream " << id(); - Reset(QUIC_STREAM_NO_ERROR); + if (session()->split_up_send_rst()) { + MaybeSendStopSending(QUIC_STREAM_NO_ERROR); + } else { + Reset(QUIC_STREAM_NO_ERROR); + } } QuicSpdyStream::StopReading(); }
diff --git a/quic/core/http/quic_spdy_server_stream_base_test.cc b/quic/core/http/quic_spdy_server_stream_base_test.cc index 1d08da3..64184a7 100644 --- a/quic/core/http/quic_spdy_server_stream_base_test.cc +++ b/quic/core/http/quic_spdy_server_stream_base_test.cc
@@ -50,7 +50,19 @@ TEST_F(QuicSpdyServerStreamBaseTest, SendQuicRstStreamNoErrorWithEarlyResponse) { stream_->StopReading(); - EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _, _)).Times(1); + + if (!session_.split_up_send_rst()) { + EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _, _)) + .Times(1); + } else { + if (session_.version().UsesHttp3()) { + EXPECT_CALL(session_, MaybeSendStopSendingFrame(_, QUIC_STREAM_NO_ERROR)) + .Times(1); + } else { + EXPECT_CALL(session_, MaybeSendRstStreamFrame(_, QUIC_STREAM_NO_ERROR, _)) + .Times(1); + } + } QuicStreamPeer::SetFinSent(stream_); stream_->CloseWriteSide(); } @@ -61,14 +73,25 @@ EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _, _)).Times(0); - EXPECT_CALL( - session_, - SendRstStream(_, + if (!session_.split_up_send_rst()) { + EXPECT_CALL( + session_, + SendRstStream(_, + VersionHasIetfQuicFrames(session_.transport_version()) + ? QUIC_STREAM_CANCELLED + : QUIC_RST_ACKNOWLEDGEMENT, + _, _)) + .Times(1); + } else { + EXPECT_CALL(session_, + MaybeSendRstStreamFrame( + _, VersionHasIetfQuicFrames(session_.transport_version()) ? QUIC_STREAM_CANCELLED : QUIC_RST_ACKNOWLEDGEMENT, - _, _)) - .Times(1); + _)) + .Times(1); + } QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), QUIC_STREAM_CANCELLED, 1234); stream_->OnStreamReset(rst_frame);
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc index 4adbaab..03afd72 100644 --- a/quic/core/http/quic_spdy_stream_test.cc +++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -514,8 +514,13 @@ stream_->OnStreamHeadersPriority( spdy::SpdyStreamPrecedence(kV3HighestPriority)); - EXPECT_CALL(*session_, - SendRstStream(stream_->id(), QUIC_HEADERS_TOO_LARGE, 0, _)); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, + SendRstStream(stream_->id(), QUIC_HEADERS_TOO_LARGE, 0, _)); + } else { + EXPECT_CALL(*session_, MaybeSendRstStreamFrame( + stream_->id(), QUIC_HEADERS_TOO_LARGE, 0)); + } stream_->OnStreamHeaderList(false, 1 << 20, headers); EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_HEADERS_TOO_LARGE)); @@ -530,8 +535,15 @@ QuicStreamFrame frame(stream_->id(), false, 0, headers); - EXPECT_CALL(*session_, - SendRstStream(stream_->id(), QUIC_HEADERS_TOO_LARGE, 0, _)); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, + SendRstStream(stream_->id(), QUIC_HEADERS_TOO_LARGE, 0, _)); + } else { + EXPECT_CALL(*session_, MaybeSendStopSendingFrame(stream_->id(), + QUIC_HEADERS_TOO_LARGE)); + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(stream_->id(), + QUIC_HEADERS_TOO_LARGE, 0)); + } auto qpack_decoder_stream = QuicSpdySessionPeer::GetQpackDecoderSendStream(session_.get()); @@ -667,8 +679,11 @@ ConnectionCloseSource source) { session_->ReallyOnConnectionClosed(frame, source); })); - EXPECT_CALL(*session_, SendRstStream(_, _, _, _)); - EXPECT_CALL(*session_, SendRstStream(_, _, _, _)); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, SendRstStream(_, _, _, _)).Times(2); + } else { + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(_, _, _)).Times(2); + } stream_->OnStreamFrame(frame); } @@ -2097,8 +2112,11 @@ ConnectionCloseSource source) { session_->ReallyOnConnectionClosed(frame, source); })); - EXPECT_CALL(*session_, SendRstStream(_, _, _, _)); - EXPECT_CALL(*session_, SendRstStream(_, _, _, _)); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, SendRstStream(_, _, _, _)).Times(2); + } else { + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(_, _, _)).Times(2); + } stream_->OnStreamFrame(frame); } @@ -2136,8 +2154,13 @@ session_->ReallyOnConnectionClosed(frame, source); })); } - EXPECT_CALL(*session_, SendRstStream(stream_->id(), _, _, _)); - EXPECT_CALL(*session_, SendRstStream(stream2_->id(), _, _, _)); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, SendRstStream(stream_->id(), _, _, _)); + EXPECT_CALL(*session_, SendRstStream(stream2_->id(), _, _, _)); + } else { + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(stream_->id(), _, _)); + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(stream2_->id(), _, _)); + } // Invalid headers: Required Insert Count is zero, but the header block // contains a dynamic table reference. @@ -2456,8 +2479,15 @@ /* offset = */ 1, _, _, _)); // Reset stream. - EXPECT_CALL(*session_, - SendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, _, _)); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, + SendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, _, _)); + } else { + EXPECT_CALL(*session_, MaybeSendStopSendingFrame(stream_->id(), + QUIC_STREAM_CANCELLED)); + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(stream_->id(), + QUIC_STREAM_CANCELLED, _)); + } stream_->Reset(QUIC_STREAM_CANCELLED); if (!GetQuicReloadableFlag(quic_abort_qpack_on_stream_close)) { @@ -2917,8 +2947,15 @@ EXPECT_CALL(*session_, WritevData(qpack_decoder_stream->id(), /* write_length = */ 1, /* offset = */ 1, _, _, _)); - EXPECT_CALL(*session_, - SendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, 0, _)); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, + SendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, _, _)); + } else { + EXPECT_CALL(*session_, MaybeSendStopSendingFrame(stream_->id(), + QUIC_STREAM_CANCELLED)); + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(stream_->id(), + QUIC_STREAM_CANCELLED, _)); + } stream_->Reset(QUIC_STREAM_CANCELLED); }
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc index 34489e0..84f0a7f 100644 --- a/quic/core/quic_session.cc +++ b/quic/core/quic_session.cc
@@ -804,15 +804,26 @@ QuicRstStreamErrorCode error, QuicStreamOffset bytes_written, bool send_rst_only) { - if (connection()->connected()) { - QuicConnection::ScopedPacketFlusher flusher(connection()); - MaybeSendRstStreamFrame(id, error, bytes_written); - if (!send_rst_only) { - MaybeSendStopSendingFrame(id, error); - } - - connection_->OnStreamReset(id, error); + DCHECK(!split_up_send_rst()); + if (!connection()->connected()) { + return; } + + QuicConnection::ScopedPacketFlusher flusher(connection()); + if (!VersionHasIetfQuicFrames(transport_version()) || + QuicUtils::GetStreamType(id, perspective(), IsIncomingStream(id), + version()) != READ_UNIDIRECTIONAL) { + control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written); + } + if (!send_rst_only) { + if (VersionHasIetfQuicFrames(transport_version()) && + QuicUtils::GetStreamType(id, perspective(), IsIncomingStream(id), + version()) != WRITE_UNIDIRECTIONAL) { + control_frame_manager_.WriteOrBufferStopSending(error, id); + } + } + + connection_->OnStreamReset(id, error); } void QuicSession::ResetStream(QuicStreamId id, QuicRstStreamErrorCode error) { @@ -829,23 +840,37 @@ return; } - SendRstStream(id, error, 0, /*send_rst_only = */ false); + if (split_up_send_rst()) { + QuicConnection::ScopedPacketFlusher flusher(connection()); + MaybeSendStopSendingFrame(id, error); + MaybeSendRstStreamFrame(id, error, 0); + } else { + SendRstStream(id, error, 0, /*send_rst_only = */ false); + } } void QuicSession::MaybeSendRstStreamFrame(QuicStreamId id, QuicRstStreamErrorCode error, QuicStreamOffset bytes_written) { - DCHECK(connection()->connected()); + DCHECK(split_up_send_rst()); + if (!connection()->connected()) { + return; + } if (!VersionHasIetfQuicFrames(transport_version()) || QuicUtils::GetStreamType(id, perspective(), IsIncomingStream(id), version()) != READ_UNIDIRECTIONAL) { control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written); } + + connection_->OnStreamReset(id, error); } void QuicSession::MaybeSendStopSendingFrame(QuicStreamId id, QuicRstStreamErrorCode error) { - DCHECK(connection()->connected()); + DCHECK(split_up_send_rst()); + if (!connection()->connected()) { + return; + } if (VersionHasIetfQuicFrames(transport_version()) && QuicUtils::GetStreamType(id, perspective(), IsIncomingStream(id), version()) != WRITE_UNIDIRECTIONAL) {
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h index 8f26290..e84124e 100644 --- a/quic/core/quic_session.h +++ b/quic/core/quic_session.h
@@ -34,6 +34,7 @@ #include "net/third_party/quiche/src/quic/core/uber_quic_stream_id_manager.h" #include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" namespace quic { @@ -216,6 +217,8 @@ // Called by stream to send RST_STREAM (and STOP_SENDING in IETF QUIC). // if |send_rst_only|, STOP_SENDING will not be sent for IETF QUIC. + // TODO(b/170233449): Delete this method when flag quic_split_up_send_rst is + // deprecated. virtual void SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error, QuicStreamOffset bytes_written, @@ -332,6 +335,8 @@ return connection_->connection_id(); } + bool split_up_send_rst() const { return split_up_send_rst_; } + // Returns the number of currently open streams, excluding static streams, and // never counting unfinished streams. size_t GetNumActiveStreams() const; @@ -473,6 +478,16 @@ return true; } + // Does actual work of sending RESET_STREAM, if the stream type allows. + // Also informs the connection so that pending stream frames can be flushed. + virtual void MaybeSendRstStreamFrame(QuicStreamId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written); + + // Sends a STOP_SENDING frame if the stream type allows. + virtual void MaybeSendStopSendingFrame(QuicStreamId id, + QuicRstStreamErrorCode error); + const absl::optional<std::string> user_agent_id() const { return user_agent_id_; } @@ -715,14 +730,6 @@ // stream. void PendingStreamOnRstStream(const QuicRstStreamFrame& frame); - // Does actual work of sending RESET_STREAM, if the stream type allows. - void MaybeSendRstStreamFrame(QuicStreamId id, - QuicRstStreamErrorCode error, - QuicStreamOffset bytes_written); - - // Sends a STOP_SENDING frame if the stream type allows. - void MaybeSendStopSendingFrame(QuicStreamId id, QuicRstStreamErrorCode error); - // Keep track of highest received byte offset of locally closed streams, while // waiting for a definitive final highest offset from the peer. QuicHashMap<QuicStreamId, QuicStreamOffset> @@ -835,6 +842,8 @@ // This indicates a liveness testing is in progress, and push back the // creation of new outgoing bidirectional streams. bool liveness_testing_in_progress_; + + const bool split_up_send_rst_ = GetQuicReloadableFlag(quic_split_up_send_rst); }; } // namespace quic
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc index be2136b..5161977 100644 --- a/quic/core/quic_session_test.cc +++ b/quic/core/quic_session_test.cc
@@ -3031,24 +3031,21 @@ .Times(1) .WillOnce(Invoke(&ClearControlFrame)); EXPECT_CALL(*connection_, OnStreamReset(read_only, _)); - session_.SendRstStream(read_only, QUIC_STREAM_CANCELLED, 0, - /*send_rst_only = */ false); + session_.ResetStream(read_only, QUIC_STREAM_CANCELLED); QuicStreamId write_only = GetNthServerInitiatedUnidirectionalId(0); EXPECT_CALL(*connection_, SendControlFrame(_)) .Times(1) .WillOnce(Invoke(&ClearControlFrame)); EXPECT_CALL(*connection_, OnStreamReset(write_only, _)); - session_.SendRstStream(write_only, QUIC_STREAM_CANCELLED, 0, - /*send_rst_only = */ false); + session_.ResetStream(write_only, QUIC_STREAM_CANCELLED); QuicStreamId bidirectional = GetNthClientInitiatedBidirectionalId(0); EXPECT_CALL(*connection_, SendControlFrame(_)) .Times(2) .WillRepeatedly(Invoke(&ClearControlFrame)); EXPECT_CALL(*connection_, OnStreamReset(bidirectional, _)); - session_.SendRstStream(bidirectional, QUIC_STREAM_CANCELLED, 0, - /*send_rst_only = */ false); + session_.ResetStream(bidirectional, QUIC_STREAM_CANCELLED); } TEST_P(QuicSessionTestServer, DecryptionKeyAvailableBeforeEncryptionKey) {
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc index f84df2b..e9daac7 100644 --- a/quic/core/quic_stream.cc +++ b/quic/core/quic_stream.cc
@@ -342,6 +342,7 @@ fin_received_(fin_received), rst_sent_(false), rst_received_(false), + stop_sending_sent_(false), flow_controller_(std::move(flow_controller)), connection_flow_controller_(connection_flow_controller), stream_contributes_to_connection_flow_control_(true), @@ -504,10 +505,15 @@ stream_error_ = code; - session()->SendRstStream(id(), code, stream_bytes_written(), - /*send_rst_only = */ true); - rst_sent_ = true; - CloseWriteSide(); + if (session()->split_up_send_rst()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_split_up_send_rst, 1, 3); + MaybeSendRstStream(code); + } else { + session()->SendRstStream(id(), code, stream_bytes_written(), + /*send_rst_only = */ true); + rst_sent_ = true; + CloseWriteSide(); + } return true; } @@ -593,15 +599,23 @@ void QuicStream::Reset(QuicRstStreamErrorCode error) { stream_error_ = error; - session()->SendRstStream(id(), error, stream_bytes_written(), - /*send_rst_only = */ false); - rst_sent_ = true; + if (session()->split_up_send_rst()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_split_up_send_rst, 2, 3); + MaybeSendStopSending(error); + MaybeSendRstStream(error); + } else { + session()->SendRstStream(id(), error, stream_bytes_written(), + /*send_rst_only = */ false); + rst_sent_ = true; + } if (read_side_closed_ && write_side_closed_ && !IsWaitingForAcks()) { session()->MaybeCloseZombieStream(id_); return; } - CloseReadSide(); - CloseWriteSide(); + if (!session()->split_up_send_rst()) { + CloseReadSide(); + CloseWriteSide(); + } } void QuicStream::OnUnrecoverableError(QuicErrorCode error, @@ -812,6 +826,44 @@ } } +void QuicStream::MaybeSendStopSending(QuicRstStreamErrorCode error) { + DCHECK(session()->split_up_send_rst()); + if (stop_sending_sent_) { + return; + } + + if (!session()->version().UsesHttp3() && error != QUIC_STREAM_NO_ERROR) { + // In gQUIC, RST with error closes both read and write side. + return; + } + + if (session()->version().UsesHttp3()) { + session()->MaybeSendStopSendingFrame(id(), error); + } else { + DCHECK_EQ(QUIC_STREAM_NO_ERROR, error); + session()->MaybeSendRstStreamFrame(id(), QUIC_STREAM_NO_ERROR, + stream_bytes_written()); + } + stop_sending_sent_ = true; + CloseReadSide(); +} + +void QuicStream::MaybeSendRstStream(QuicRstStreamErrorCode error) { + DCHECK(session()->split_up_send_rst()); + if (rst_sent_) { + return; + } + + if (!session()->version().UsesHttp3()) { + QUIC_BUG_IF(error == QUIC_STREAM_NO_ERROR); + stop_sending_sent_ = true; + CloseReadSide(); + } + session()->MaybeSendRstStreamFrame(id(), error, stream_bytes_written()); + rst_sent_ = true; + CloseWriteSide(); +} + bool QuicStream::HasBufferedData() const { DCHECK_GE(send_buffer_.stream_offset(), stream_bytes_written()); return send_buffer_.stream_offset() > stream_bytes_written(); @@ -834,14 +886,25 @@ DCHECK(read_side_closed_ && write_side_closed_); if (!fin_sent_ && !rst_sent_) { - // For flow control accounting, tell the peer how many bytes have been - // written on this stream before termination. Done here if needed, using a - // RST_STREAM frame. - QUIC_DLOG(INFO) << ENDPOINT << "Sending RST_STREAM in OnClose: " << id(); - session_->SendRstStream(id(), QUIC_RST_ACKNOWLEDGEMENT, - stream_bytes_written(), /*send_rst_only = */ false); - session_->MaybeCloseZombieStream(id_); - rst_sent_ = true; + if (!session()->split_up_send_rst()) { + // For flow control accounting, tell the peer how many bytes have been + // written on this stream before termination. Done here if needed, using a + // RST_STREAM frame. + QUIC_DLOG(INFO) << ENDPOINT << "Sending RST_STREAM in OnClose: " << id(); + session_->SendRstStream(id(), QUIC_RST_ACKNOWLEDGEMENT, + stream_bytes_written(), + /*send_rst_only = */ false); + session_->MaybeCloseZombieStream(id_); + rst_sent_ = true; + } else { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_split_up_send_rst, 3, 3); + QUIC_BUG_IF(session()->connection()->connected() && + session()->version().UsesHttp3()) + << "The stream should've already sent RST in response to " + "STOP_SENDING"; + MaybeSendRstStream(QUIC_RST_ACKNOWLEDGEMENT); + session_->MaybeCloseZombieStream(id_); + } } if (!flow_controller_.has_value() ||
diff --git a/quic/core/quic_stream.h b/quic/core/quic_stream.h index 89178c4..467cca9 100644 --- a/quic/core/quic_stream.h +++ b/quic/core/quic_stream.h
@@ -403,6 +403,12 @@ // empty. void SetFinSent(); + // Send STOP_SENDING if it hasn't been sent yet. + void MaybeSendStopSending(QuicRstStreamErrorCode error); + + // Send RESET_STREAM if it hasn't been sent yet. + void MaybeSendRstStream(QuicRstStreamErrorCode error); + // Close the write side of the socket. Further writes will fail. // Can be called by the subclass or internally. // Does not send a FIN. May cause the stream to be closed. @@ -509,6 +515,9 @@ // True if this stream has received a RST_STREAM frame. bool rst_received_; + // True if the stream has sent STOP_SENDING to the session. + bool stop_sending_sent_; + absl::optional<QuicFlowController> flow_controller_; // The connection level flow controller. Not owned.
diff --git a/quic/core/quic_stream_test.cc b/quic/core/quic_stream_test.cc index 29bbaf5..fcd0d28 100644 --- a/quic/core/quic_stream_test.cc +++ b/quic/core/quic_stream_test.cc
@@ -72,6 +72,7 @@ using QuicStream::CanWriteNewDataAfterData; using QuicStream::CloseWriteSide; using QuicStream::fin_buffered; + using QuicStream::MaybeSendStopSending; using QuicStream::OnClose; using QuicStream::WriteMemSlices; using QuicStream::WriteOrBufferData; @@ -112,8 +113,15 @@ // session_ now owns stream_. session_->ActivateStream(QuicWrapUnique(stream_)); // Ignore resetting when session_ is terminated. - EXPECT_CALL(*session_, SendRstStream(kTestStreamId, _, _, _)) - .Times(AnyNumber()); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, SendRstStream(kTestStreamId, _, _, _)) + .Times(AnyNumber()); + } else { + EXPECT_CALL(*session_, MaybeSendStopSendingFrame(kTestStreamId, _)) + .Times(AnyNumber()); + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(kTestStreamId, _, _)) + .Times(AnyNumber()); + } write_blocked_list_ = QuicSessionPeer::GetWriteBlockedStreams(session_.get()); } @@ -421,13 +429,21 @@ TEST_P(QuicStreamTest, ConnectionCloseAfterStreamClose) { Initialize(); - QuicStreamPeer::CloseReadSide(stream_); - stream_->CloseWriteSide(); - EXPECT_THAT(stream_->stream_error(), IsQuicStreamNoError()); + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED, 1234); + stream_->OnStreamReset(rst_frame); + if (VersionHasIetfQuicFrames(session_->transport_version())) { + // Create and inject a STOP SENDING frame to complete the close + // of the stream. This is only needed for version 99/IETF QUIC. + QuicStopSendingFrame stop_sending(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED); + session_->OnStopSendingFrame(stop_sending); + } + EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_STREAM_CANCELLED)); EXPECT_THAT(stream_->connection_error(), IsQuicNoError()); stream_->OnConnectionClosed(QUIC_INTERNAL_ERROR, ConnectionCloseSource::FROM_SELF); - EXPECT_THAT(stream_->stream_error(), IsQuicStreamNoError()); + EXPECT_THAT(stream_->stream_error(), IsStreamError(QUIC_STREAM_CANCELLED)); EXPECT_THAT(stream_->connection_error(), IsQuicNoError()); } @@ -452,9 +468,21 @@ EXPECT_FALSE(rst_sent()); // Now close the stream, and expect that we send a RST. - EXPECT_CALL(*session_, SendRstStream(_, _, _, _)); - QuicStreamPeer::CloseReadSide(stream_); - stream_->CloseWriteSide(); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, SendRstStream(kTestStreamId, _, _, _)); + } else { + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(kTestStreamId, _, _)); + } + QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED, 1234); + stream_->OnStreamReset(rst_frame); + if (VersionHasIetfQuicFrames(session_->transport_version())) { + // Create and inject a STOP SENDING frame to complete the close + // of the stream. This is only needed for version 99/IETF QUIC. + QuicStopSendingFrame stop_sending(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED); + session_->OnStopSendingFrame(stop_sending); + } EXPECT_FALSE(session_->HasUnackedStreamData()); EXPECT_FALSE(fin_sent()); EXPECT_TRUE(rst_sent()); @@ -497,8 +525,12 @@ EXPECT_FALSE(rst_sent()); // Reset the stream. - const int expected_resets = 1; - EXPECT_CALL(*session_, SendRstStream(_, _, _, _)).Times(expected_resets); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, SendRstStream(kTestStreamId, _, _, _)).Times(1); + } else { + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(kTestStreamId, _, _)) + .Times(1); + } stream_->Reset(QUIC_STREAM_CANCELLED); EXPECT_FALSE(fin_sent()); EXPECT_TRUE(rst_sent()); @@ -639,8 +671,6 @@ CloseConnection(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA, _, _)); stream_->OnStreamReset(rst_frame); EXPECT_TRUE(stream_->HasReceivedFinalOffset()); - QuicStreamPeer::CloseReadSide(stream_); - stream_->CloseWriteSide(); } TEST_P(QuicStreamTest, FinalByteOffsetFromZeroLengthStreamFrame) { @@ -910,7 +940,11 @@ EXPECT_TRUE(session_->HasUnackedStreamData()); EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); // Cancel stream. - stream_->Reset(QUIC_STREAM_NO_ERROR); + if (session_->split_up_send_rst()) { + stream_->MaybeSendStopSending(QUIC_STREAM_NO_ERROR); + } else { + stream_->Reset(QUIC_STREAM_NO_ERROR); + } // stream still waits for acks as the error code is QUIC_STREAM_NO_ERROR, and // data is going to be retransmitted. EXPECT_TRUE(stream_->IsWaitingForAcks()); @@ -920,12 +954,21 @@ EXPECT_CALL(*connection_, SendControlFrame(_)) .Times(AtLeast(1)) .WillRepeatedly(Invoke(&ClearControlFrame)); - EXPECT_CALL(*session_, - SendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, 9, _)) - .WillOnce(InvokeWithoutArgs([this]() { - session_->ReallySendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, - stream_->stream_bytes_written(), false); - })); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, + SendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, 9, _)) + .WillOnce(InvokeWithoutArgs([this]() { + session_->ReallySendRstStream(stream_->id(), QUIC_STREAM_CANCELLED, + stream_->stream_bytes_written(), false); + })); + } else { + EXPECT_CALL(*session_, MaybeSendRstStreamFrame(_, _, _)) + .WillOnce(InvokeWithoutArgs([this]() { + session_->ReallyMaybeSendRstStreamFrame( + stream_->id(), QUIC_STREAM_CANCELLED, + stream_->stream_bytes_written()); + })); + } stream_->Reset(QUIC_STREAM_CANCELLED); EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); @@ -956,8 +999,14 @@ // RST_STREAM received. QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), QUIC_STREAM_CANCELLED, 9); - EXPECT_CALL(*session_, - SendRstStream(stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, 9, _)); + + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, + SendRstStream(stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, 9, _)); + } else { + EXPECT_CALL(*session_, MaybeSendRstStreamFrame( + stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, 9)); + } stream_->OnStreamReset(rst_frame); EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); // Stream stops waiting for acks as it does not finish sending and rst is @@ -1000,8 +1049,14 @@ stream_->WriteOrBufferData(kData1, false, nullptr); EXPECT_TRUE(stream_->IsWaitingForAcks()); EXPECT_TRUE(session_->HasUnackedStreamData()); - EXPECT_CALL(*session_, - SendRstStream(stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, 9, _)); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, + SendRstStream(stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, 9, _)); + } else { + EXPECT_CALL(*session_, MaybeSendRstStreamFrame( + stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, 9)); + } + QuicConnectionPeer::SetConnectionClose(connection_); stream_->OnConnectionClosed(QUIC_INTERNAL_ERROR, ConnectionCloseSource::FROM_SELF); EXPECT_EQ(1u, QuicStreamPeer::SendBuffer(stream_).size()); @@ -1525,8 +1580,19 @@ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); // Verify stream gets reset because TTL expires. - EXPECT_CALL(*session_, SendRstStream(_, QUIC_STREAM_TTL_EXPIRED, _, _)) - .Times(1); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, SendRstStream(_, QUIC_STREAM_TTL_EXPIRED, _, _)) + .Times(1); + } else { + if (session_->version().UsesHttp3()) { + EXPECT_CALL(*session_, + MaybeSendStopSendingFrame(_, QUIC_STREAM_TTL_EXPIRED)) + .Times(1); + } + EXPECT_CALL(*session_, + MaybeSendRstStreamFrame(_, QUIC_STREAM_TTL_EXPIRED, _)) + .Times(1); + } stream_->OnCanWrite(); } @@ -1544,8 +1610,19 @@ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); // Verify stream gets reset because TTL expires. - EXPECT_CALL(*session_, SendRstStream(_, QUIC_STREAM_TTL_EXPIRED, _, _)) - .Times(1); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, SendRstStream(_, QUIC_STREAM_TTL_EXPIRED, _, _)) + .Times(1); + } else { + if (session_->version().UsesHttp3()) { + EXPECT_CALL(*session_, + MaybeSendStopSendingFrame(_, QUIC_STREAM_TTL_EXPIRED)) + .Times(1); + } + EXPECT_CALL(*session_, + MaybeSendRstStreamFrame(_, QUIC_STREAM_TTL_EXPIRED, _)) + .Times(1); + } stream_->RetransmitStreamData(0, 100, false, PTO_RETRANSMISSION); }
diff --git a/quic/qbone/qbone_stream_test.cc b/quic/qbone/qbone_stream_test.cc index c2b28ca..17cd2e1 100644 --- a/quic/qbone/qbone_stream_test.cc +++ b/quic/qbone/qbone_stream_test.cc
@@ -65,6 +65,16 @@ SendRstStream, (QuicStreamId, QuicRstStreamErrorCode, QuicStreamOffset, bool), (override)); + MOCK_METHOD(void, + MaybeSendRstStreamFrame, + (QuicStreamId stream_id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written), + (override)); + MOCK_METHOD(void, + MaybeSendStopSendingFrame, + (QuicStreamId stream_id, QuicRstStreamErrorCode error), + (override)); // Sets whether data is written to buffer, or else if this is write blocked. void set_writable(bool writable) { writable_ = writable; } @@ -237,8 +247,15 @@ CreateReliableQuicStream(); std::string packet = "0123456789"; int iterations = (QboneConstants::kMaxQbonePacketBytes / packet.size()) + 2; - EXPECT_CALL(*session_, - SendRstStream(kStreamId, QUIC_BAD_APPLICATION_PAYLOAD, _, _)); + if (!session_->split_up_send_rst()) { + EXPECT_CALL(*session_, + SendRstStream(kStreamId, QUIC_BAD_APPLICATION_PAYLOAD, _, _)); + } else { + EXPECT_CALL(*session_, MaybeSendStopSendingFrame( + kStreamId, QUIC_BAD_APPLICATION_PAYLOAD)); + EXPECT_CALL(*session_, MaybeSendRstStreamFrame( + kStreamId, QUIC_BAD_APPLICATION_PAYLOAD, _)); + } for (int i = 0; i < iterations; ++i) { QuicStreamFrame frame(kStreamId, i == (iterations - 1), i * packet.size(), packet);
diff --git a/quic/test_tools/quic_connection_peer.cc b/quic/test_tools/quic_connection_peer.cc index 1f4e0c8..6b092c8 100644 --- a/quic/test_tools/quic_connection_peer.cc +++ b/quic/test_tools/quic_connection_peer.cc
@@ -384,5 +384,9 @@ return connection->pending_path_challenge_payloads_; } +void QuicConnectionPeer::SetConnectionClose(QuicConnection* connection) { + connection->connected_ = false; +} + } // namespace test } // namespace quic
diff --git a/quic/test_tools/quic_connection_peer.h b/quic/test_tools/quic_connection_peer.h index 783b055..bf404fb 100644 --- a/quic/test_tools/quic_connection_peer.h +++ b/quic/test_tools/quic_connection_peer.h
@@ -159,6 +159,8 @@ static const QuicCircularDeque< std::pair<QuicPathFrameBuffer, QuicSocketAddress>>& pending_path_challenge_payloads(QuicConnection* connection); + + static void SetConnectionClose(QuicConnection* connection); }; } // namespace test
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h index b0052d8..f2ad3be 100644 --- a/quic/test_tools/quic_test_utils.h +++ b/quic/test_tools/quic_test_utils.h
@@ -836,6 +836,16 @@ QuicStreamOffset bytes_written, bool send_rst_only), (override)); + MOCK_METHOD(void, + MaybeSendRstStreamFrame, + (QuicStreamId stream_id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written), + (override)); + MOCK_METHOD(void, + MaybeSendStopSendingFrame, + (QuicStreamId stream_id, QuicRstStreamErrorCode error), + (override)); MOCK_METHOD(bool, ShouldKeepConnectionAlive, (), (const, override)); MOCK_METHOD(void, @@ -867,6 +877,12 @@ QuicSession::SendRstStream(id, error, bytes_written, send_rst_only); } + void ReallyMaybeSendRstStreamFrame(QuicStreamId id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written) { + QuicSession::MaybeSendRstStreamFrame(id, error, bytes_written); + } + private: std::unique_ptr<QuicCryptoStream> crypto_stream_; }; @@ -968,6 +984,16 @@ bool send_rst_only), (override)); MOCK_METHOD(void, + MaybeSendRstStreamFrame, + (QuicStreamId stream_id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written), + (override)); + MOCK_METHOD(void, + MaybeSendStopSendingFrame, + (QuicStreamId stream_id, QuicRstStreamErrorCode error), + (override)); + MOCK_METHOD(void, SendWindowUpdate, (QuicStreamId id, QuicStreamOffset byte_offset), (override));
diff --git a/quic/tools/quic_simple_server_stream_test.cc b/quic/tools/quic_simple_server_stream_test.cc index c5d989a..251f02d 100644 --- a/quic/tools/quic_simple_server_stream_test.cc +++ b/quic/tools/quic_simple_server_stream_test.cc
@@ -12,6 +12,7 @@ #include "absl/types/optional.h" #include "net/third_party/quiche/src/quic/core/http/http_encoder.h" #include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" @@ -158,6 +159,16 @@ QuicStreamOffset bytes_written, bool send_rst_only), (override)); + MOCK_METHOD(void, + MaybeSendRstStreamFrame, + (QuicStreamId stream_id, + QuicRstStreamErrorCode error, + QuicStreamOffset bytes_written), + (override)); + MOCK_METHOD(void, + MaybeSendStopSendingFrame, + (QuicStreamId stream_id, QuicRstStreamErrorCode error), + (override)); // Matchers cannot be used on non-copyable types like SpdyHeaderBlock. void PromisePushResources( const std::string& request_url, @@ -341,7 +352,18 @@ QuicStreamPeer::SetFinSent(stream_); stream_->CloseWriteSide(); - EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _, _)).Times(1); + if (!session_.split_up_send_rst()) { + EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _, _)) + .Times(1); + } else { + if (session_.version().UsesHttp3()) { + EXPECT_CALL(session_, MaybeSendStopSendingFrame(_, QUIC_STREAM_NO_ERROR)) + .Times(1); + } else { + EXPECT_CALL(session_, MaybeSendRstStreamFrame(_, QUIC_STREAM_NO_ERROR, _)) + .Times(1); + } + } stream_->StopReading(); } @@ -469,8 +491,16 @@ std::move(response_headers_), body); InSequence s; - EXPECT_CALL(session_, SendRstStream(promised_stream->id(), - QUIC_STREAM_CANCELLED, 0, _)); + if (!session_.split_up_send_rst()) { + EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_CANCELLED, 0, _)); + } else { + if (session_.version().UsesHttp3()) { + EXPECT_CALL(session_, MaybeSendStopSendingFrame(promised_stream->id(), + QUIC_STREAM_CANCELLED)); + } + EXPECT_CALL(session_, MaybeSendRstStreamFrame(promised_stream->id(), + QUIC_STREAM_CANCELLED, 0)); + } promised_stream->DoSendResponse(); } @@ -674,7 +704,6 @@ TEST_P(QuicSimpleServerStreamTest, DoNotSendQuicRstStreamNoErrorWithRstReceived) { - InSequence s; EXPECT_FALSE(stream_->reading_stopped()); EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _, _)).Times(0); @@ -687,16 +716,33 @@ EXPECT_CALL(session_, WritevData(qpack_decoder_stream->id(), _, _, _, _, _)) .Times(AnyNumber()); } - EXPECT_CALL(session_, SendRstStream(_, QUIC_RST_ACKNOWLEDGEMENT, _, _)) - .Times(1); + + if (!session_.split_up_send_rst()) { + EXPECT_CALL(session_, SendRstStream(_, + session_.version().UsesHttp3() + ? QUIC_STREAM_CANCELLED + : QUIC_RST_ACKNOWLEDGEMENT, + _, _)) + .Times(1); + } else { + EXPECT_CALL(session_, + MaybeSendRstStreamFrame(_, + session_.version().UsesHttp3() + ? QUIC_STREAM_CANCELLED + : QUIC_RST_ACKNOWLEDGEMENT, + _)) + .Times(1); + } QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(), QUIC_STREAM_CANCELLED, 1234); stream_->OnStreamReset(rst_frame); if (VersionHasIetfQuicFrames(connection_->transport_version())) { - // For V99 receiving a RST_STREAM causes a 1-way close; the test requires - // a full close. A CloseWriteSide closes the other half of the stream. - // Everything should then work properly. - stream_->CloseWriteSide(); + EXPECT_CALL(session_owner_, OnStopSendingReceived(_)); + // Create and inject a STOP SENDING frame to complete the close + // of the stream. This is only needed for version 99/IETF QUIC. + QuicStopSendingFrame stop_sending(kInvalidControlFrameId, stream_->id(), + QUIC_STREAM_CANCELLED); + session_.OnStopSendingFrame(stop_sending); } EXPECT_TRUE(stream_->reading_stopped()); EXPECT_TRUE(stream_->write_side_closed());