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());