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