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