Prevent duplicate HTTP/3 control stream. gfe-relnote: v99 only, not protected. PiperOrigin-RevId: 263618125 Change-Id: I456046655711cae92f28aff4d648a15bd7184dc0
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc index e6ec954..1da419e 100644 --- a/quic/core/http/quic_spdy_session.cc +++ b/quic/core/http/quic_spdy_session.cc
@@ -880,6 +880,14 @@ switch (stream_type) { case kControlStream: { // HTTP/3 control stream. + if (receive_control_stream_) { + QUIC_PEER_BUG + << "Received a duplicate control stream: Closing connection."; + // TODO(renjietang): Change to HTTP_STREAM_CREATION_ERROR. + CloseConnectionWithDetails(QUIC_INVALID_STREAM_ID, + "Control stream is received twice."); + return false; + } auto receive_stream = QuicMakeUnique<QuicReceiveControlStream>(pending); receive_control_stream_ = receive_stream.get(); RegisterStaticStream(std::move(receive_stream),
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc index a9bcfcc..e58a651 100644 --- a/quic/core/http/quic_spdy_session_test.cc +++ b/quic/core/http/quic_spdy_session_test.cc
@@ -2377,6 +2377,27 @@ EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); } +TEST_P(QuicSpdySessionTestClient, DuplicateHttp3UnidirectionalStreams) { + if (!VersionHasStreamType(transport_version())) { + return; + } + QuicStreamId id1 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); + char type[] = {kControlStream}; + + QuicStreamFrame data1(id1, false, 0, QuicStringPiece(type, 1)); + session_.OnStreamFrame(data1); + QuicStreamId id2 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 1); + QuicStreamFrame data2(id2, false, 0, QuicStringPiece(type, 1)); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "Control stream is received twice.", _)); + EXPECT_QUIC_PEER_BUG( + session_.OnStreamFrame(data2), + "Received a duplicate control stream: Closing connection."); +} + } // namespace } // namespace test } // namespace quic