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