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