Allow STOP_SENDING frame to be the first frame of a stream.
gfe-relnote: protected by gfe2_reloadable_flag_quic_enable_version_draft_25_v3
PiperOrigin-RevId: 299930073
Change-Id: I2ed39bf568f75498e8c2ab6d8c82f99466e8b9ed
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 0ca240e..e913ee1 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -248,38 +248,9 @@
visitor_->OnStopSendingReceived(frame);
}
- // If stream is closed, ignore the frame
- if (IsClosedStream(stream_id)) {
- QUIC_DVLOG(1)
- << ENDPOINT
- << "Received STOP_SENDING for closed or non-existent stream, id: "
- << stream_id << " Ignoring.";
- return;
- }
- // If stream is non-existent, close the connection.
- // TODO(b/148842616): IETF QUIC allows a STOP_SENDING to arrive before a
- // STREAM frame for peer-intiated bidirectional steams
- StreamMap::iterator it = stream_map_.find(stream_id);
- if (it == stream_map_.end()) {
- QUIC_DVLOG(1) << ENDPOINT
- << "Received STOP_SENDING for non-existent stream, id: "
- << stream_id << " Closing connection";
- connection()->CloseConnection(
- IETF_QUIC_PROTOCOL_VIOLATION,
- "Received STOP_SENDING for a non-existent stream",
- ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
- return;
- }
-
- QuicStream* stream = it->second.get();
- // If the stream is null, it's an implementation error.
- if (stream == nullptr) {
- QUIC_BUG << ENDPOINT
- << "Received STOP_SENDING for NULL QuicStream, stream_id: "
- << stream_id << ". Ignoring.";
- connection()->CloseConnection(
- QUIC_INTERNAL_ERROR, "Received STOP_SENDING for a null stream",
- ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ QuicStream* stream = GetOrCreateStream(stream_id);
+ if (!stream) {
+ // Errors are handled by GetOrCreateStream.
return;
}
@@ -287,7 +258,7 @@
return;
}
- // TODO(renjietang): Consider moving those code into the
+ // TODO(renjietang): Consider moving those code into the stream.
if (connection()->connected()) {
MaybeSendRstStreamFrame(
stream->id(),
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 4f8dd4f..0d7d80e 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -2695,22 +2695,40 @@
session_.OnStopSendingFrame(frame);
}
-// If stream id is a nonexistent stream, return false and close the connection.
-TEST_P(QuicSessionTestServer, OnStopSendingInputNonExistentStream) {
+// If stream id is a nonexistent local stream, return false and close the
+// connection.
+TEST_P(QuicSessionTestServer, OnStopSendingInputNonExistentLocalStream) {
if (!VersionHasIetfQuicFrames(transport_version())) {
return;
}
QuicStopSendingFrame frame(1, GetNthServerInitiatedBidirectionalId(123456),
123);
- EXPECT_CALL(
- *connection_,
- CloseConnection(IETF_QUIC_PROTOCOL_VIOLATION,
- "Received STOP_SENDING for a non-existent stream", _))
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID,
+ "Data for nonexistent stream", _))
.Times(1);
session_.OnStopSendingFrame(frame);
}
+// If a STOP_SENDING is received for a peer initiated stream, the new stream
+// will be created.
+TEST_P(QuicSessionTestServer, OnStopSendingNewStream) {
+ if (!VersionHasIetfQuicFrames(transport_version())) {
+ return;
+ }
+ QuicStopSendingFrame frame(1, GetNthClientInitiatedBidirectionalId(1), 123);
+
+ // A Rst will be sent as a response for STOP_SENDING.
+ EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1);
+ EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(1);
+ session_.OnStopSendingFrame(frame);
+
+ QuicStream* stream =
+ session_.GetOrCreateStream(GetNthClientInitiatedBidirectionalId(1));
+ EXPECT_TRUE(stream);
+ EXPECT_TRUE(stream->write_side_closed());
+}
+
// For a valid stream, ensure that all works
TEST_P(QuicSessionTestServer, OnStopSendingInputValidStream) {
if (!VersionHasIetfQuicFrames(transport_version())) {