Prevent QPACK streams from being created more than once. gfe-relnote: v99 only, not protected. PiperOrigin-RevId: 264666019 Change-Id: I01b408b87ab7c19f0c505753660ef0c1ffad53f4
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc index 9cc28ba..619c881 100644 --- a/quic/core/http/quic_spdy_session.cc +++ b/quic/core/http/quic_spdy_session.cc
@@ -899,11 +899,7 @@ 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."); + CloseConnectionOnDuplicateHttp3UnidirectionalStreams("Control"); return false; } auto receive_stream = QuicMakeUnique<QuicReceiveControlStream>(pending); @@ -920,6 +916,10 @@ return true; } case kQpackEncoderStream: { // QPACK encoder stream. + if (qpack_encoder_receive_stream_) { + CloseConnectionOnDuplicateHttp3UnidirectionalStreams("QPACK encoder"); + return false; + } auto encoder_receive = QuicMakeUnique<QpackReceiveStream>( pending, qpack_decoder_->encoder_stream_receiver()); qpack_encoder_receive_stream_ = encoder_receive.get(); @@ -930,6 +930,10 @@ return true; } case kQpackDecoderStream: { // QPACK decoder stream. + if (qpack_decoder_receive_stream_) { + CloseConnectionOnDuplicateHttp3UnidirectionalStreams("QPACK decoder"); + return false; + } auto decoder_receive = QuicMakeUnique<QpackReceiveStream>( pending, qpack_encoder_->decoder_stream_receiver()); qpack_decoder_receive_stream_ = decoder_receive.get(); @@ -1008,4 +1012,13 @@ send_control_stream_->SendMaxPushIdFrame(max_allowed_push_id); } +void QuicSpdySession::CloseConnectionOnDuplicateHttp3UnidirectionalStreams( + QuicStringPiece type) { + QUIC_PEER_BUG << QuicStrCat("Received a duplicate ", type, + " stream: Closing connection."); + // TODO(b/124216424): Change to HTTP_STREAM_CREATION_ERROR. + CloseConnectionWithDetails(QUIC_INVALID_STREAM_ID, + QuicStrCat(type, " stream is received twice.")); +} + } // namespace quic
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h index ce69b89..e16e09a 100644 --- a/quic/core/http/quic_spdy_session.h +++ b/quic/core/http/quic_spdy_session.h
@@ -282,6 +282,9 @@ void OnPriority(spdy::SpdyStreamId stream_id, const spdy::SpdyStreamPrecedence& precedence); + void CloseConnectionOnDuplicateHttp3UnidirectionalStreams( + QuicStringPiece type); + std::unique_ptr<QpackEncoder> qpack_encoder_; std::unique_ptr<QpackDecoder> qpack_decoder_;
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc index 33f3d57..c75ffa5 100644 --- a/quic/core/http/quic_spdy_session_test.cc +++ b/quic/core/http/quic_spdy_session_test.cc
@@ -11,6 +11,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" #include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h" #include "net/third_party/quiche/src/quic/core/http/http_constants.h" #include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h" #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" @@ -2387,19 +2388,53 @@ } QuicStreamId id1 = GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); - char type[] = {kControlStream}; + char type1[] = {kControlStream}; - QuicStreamFrame data1(id1, false, 0, QuicStringPiece(type, 1)); + QuicStreamFrame data1(id1, false, 0, QuicStringPiece(type1, 1)); session_.OnStreamFrame(data1); QuicStreamId id2 = GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 1); - QuicStreamFrame data2(id2, false, 0, QuicStringPiece(type, 1)); + QuicStreamFrame data2(id2, false, 0, QuicStringPiece(type1, 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."); + "Received a duplicate Control stream: Closing connection."); + + QuicStreamId id3 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 2); + char type2[]{kQpackEncoderStream}; + + QuicStreamFrame data3(id3, false, 0, QuicStringPiece(type2, 1)); + session_.OnStreamFrame(data3); + + QuicStreamId id4 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3); + QuicStreamFrame data4(id4, false, 0, QuicStringPiece(type2, 1)); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "QPACK encoder stream is received twice.", _)); + EXPECT_QUIC_PEER_BUG( + session_.OnStreamFrame(data4), + "Received a duplicate QPACK encoder stream: Closing connection."); + + QuicStreamId id5 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 4); + char type3[]{kQpackDecoderStream}; + + QuicStreamFrame data5(id5, false, 0, QuicStringPiece(type3, 1)); + session_.OnStreamFrame(data5); + + QuicStreamId id6 = + GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 5); + QuicStreamFrame data6(id6, false, 0, QuicStringPiece(type3, 1)); + EXPECT_CALL(*connection_, + CloseConnection(QUIC_INVALID_STREAM_ID, + "QPACK decoder stream is received twice.", _)); + EXPECT_QUIC_PEER_BUG( + session_.OnStreamFrame(data6), + "Received a duplicate QPACK decoder stream: Closing connection."); } } // namespace