gfe-relnote: Enforce HTTP/3 frame ordering requirements on request and control streams.  Protected by gfe2_reloadable_flag_quic_enable_version_draft_25_v3 and gfe2_reloadable_flag_quic_enable_version_draft_27.

Add new error codes and use them in request and control streams.  Add new code
to control stream to enforce that first frame must be SETTINGS.

PiperOrigin-RevId: 300217136
Change-Id: Ib3ced012961f34c912ccd2061a3dc913f7bb96e6
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc
index 4f21498..d87ec83 100644
--- a/quic/core/http/quic_receive_control_stream.cc
+++ b/quic/core/http/quic_receive_control_stream.cc
@@ -122,8 +122,7 @@
 
   bool OnUnknownFrameStart(uint64_t /* frame_type */,
                            QuicByteCount /* header_length */) override {
-    // Ignore unknown frame types.
-    return true;
+    return stream_->OnUnknownFrameStart();
   }
 
   bool OnUnknownFramePayload(quiche::QuicheStringPiece /* payload */) override {
@@ -190,9 +189,9 @@
 bool QuicReceiveControlStream::OnSettingsFrameStart(
     QuicByteCount /* header_length */) {
   if (settings_frame_received_) {
-    // TODO(renjietang): Change error code to HTTP_UNEXPECTED_FRAME.
-    stream_delegate()->OnStreamError(QUIC_INVALID_STREAM_ID,
-                                     "Settings frames are received twice.");
+    stream_delegate()->OnStreamError(
+        QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
+        "Settings frames are received twice.");
     return false;
   }
 
@@ -217,7 +216,7 @@
     QuicByteCount /* header_length */) {
   if (!settings_frame_received_) {
     stream_delegate()->OnStreamError(
-        QUIC_INVALID_STREAM_ID,
+        QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
         "PRIORITY_UPDATE frame received before SETTINGS.");
     return false;
   }
@@ -263,4 +262,15 @@
   return true;
 }
 
+bool QuicReceiveControlStream::OnUnknownFrameStart() {
+  if (!settings_frame_received_) {
+    stream_delegate()->OnStreamError(
+        QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
+        "Unknown frame received before SETTINGS.");
+    return false;
+  }
+
+  return true;
+}
+
 }  // namespace quic
diff --git a/quic/core/http/quic_receive_control_stream.h b/quic/core/http/quic_receive_control_stream.h
index e77ccf9..d24bcd0 100644
--- a/quic/core/http/quic_receive_control_stream.h
+++ b/quic/core/http/quic_receive_control_stream.h
@@ -43,6 +43,7 @@
   bool OnSettingsFrame(const SettingsFrame& settings);
   bool OnPriorityUpdateFrameStart(QuicByteCount header_length);
   bool OnPriorityUpdateFrame(const PriorityUpdateFrame& priority);
+  bool OnUnknownFrameStart();
 
   // False until a SETTINGS frame is received.
   bool settings_frame_received_;
diff --git a/quic/core/http/quic_receive_control_stream_test.cc b/quic/core/http/quic_receive_control_stream_test.cc
index 67a9a17..0997fc0 100644
--- a/quic/core/http/quic_receive_control_stream_test.cc
+++ b/quic/core/http/quic_receive_control_stream_test.cc
@@ -197,9 +197,10 @@
   EXPECT_EQ(settings_frame.size() + 1, NumBytesConsumed());
 
   // Second SETTINGS frame causes the connection to be closed.
-  EXPECT_CALL(*connection_,
-              CloseConnection(QUIC_INVALID_STREAM_ID,
-                              "Settings frames are received twice.", _))
+  EXPECT_CALL(
+      *connection_,
+      CloseConnection(QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
+                      "Settings frames are received twice.", _))
       .WillOnce(
           Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
   EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
@@ -255,7 +256,7 @@
 
   EXPECT_CALL(
       *connection_,
-      CloseConnection(QUIC_INVALID_STREAM_ID,
+      CloseConnection(QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
                       "PRIORITY_UPDATE frame received before SETTINGS.", _))
       .WillOnce(
           Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
@@ -310,18 +311,51 @@
 
 // Regression test for b/137554973: unknown frames should be consumed.
 TEST_P(QuicReceiveControlStreamTest, ConsumeUnknownFrame) {
+  EXPECT_EQ(1u, NumBytesConsumed());
+
+  // Receive SETTINGS frame.
+  SettingsFrame settings;
+  std::string settings_frame = EncodeSettings(settings);
+  receive_control_stream_->OnStreamFrame(
+      QuicStreamFrame(receive_control_stream_->id(), /* fin = */ false,
+                      /* offset = */ 1, settings_frame));
+
+  // SETTINGS frame is consumed.
+  EXPECT_EQ(1 + settings_frame.size(), NumBytesConsumed());
+
+  // Receive unknown frame.
   std::string unknown_frame = quiche::QuicheTextUtils::HexDecode(
       "21"        // reserved frame type
       "03"        // payload length
       "666f6f");  // payload "foo"
 
-  EXPECT_EQ(1u, NumBytesConsumed());
+  receive_control_stream_->OnStreamFrame(
+      QuicStreamFrame(receive_control_stream_->id(), /* fin = */ false,
+                      /* offset = */ 1 + settings_frame.size(), unknown_frame));
+
+  // Unknown frame is consumed.
+  EXPECT_EQ(1 + settings_frame.size() + unknown_frame.size(),
+            NumBytesConsumed());
+}
+
+TEST_P(QuicReceiveControlStreamTest, UnknownFrameBeforeSettings) {
+  std::string unknown_frame = quiche::QuicheTextUtils::HexDecode(
+      "21"        // reserved frame type
+      "03"        // payload length
+      "666f6f");  // payload "foo"
+
+  EXPECT_CALL(
+      *connection_,
+      CloseConnection(QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
+                      "Unknown frame received before SETTINGS.", _))
+      .WillOnce(
+          Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
+  EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _));
+  EXPECT_CALL(session_, OnConnectionClosed(_, _));
 
   receive_control_stream_->OnStreamFrame(
       QuicStreamFrame(receive_control_stream_->id(), /* fin = */ false,
                       /* offset = */ 1, unknown_frame));
-
-  EXPECT_EQ(unknown_frame.size() + 1, NumBytesConsumed());
 }
 
 }  // namespace
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index b019b1e..9d2772a 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -844,9 +844,9 @@
 bool QuicSpdyStream::OnDataFrameStart(QuicByteCount header_length) {
   DCHECK(VersionUsesHttp3(transport_version()));
   if (!headers_decompressed_ || trailers_decompressed_) {
-    // TODO(b/124216424): Change error code to HTTP_UNEXPECTED_FRAME.
-    stream_delegate()->OnStreamError(QUIC_INVALID_HEADERS_STREAM_DATA,
-                                     "Unexpected DATA frame received.");
+    stream_delegate()->OnStreamError(
+        QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_SPDY_STREAM,
+        "Unexpected DATA frame received.");
     return false;
   }
 
@@ -925,9 +925,8 @@
   DCHECK(!qpack_decoded_headers_accumulator_);
 
   if (trailers_decompressed_) {
-    // TODO(b/124216424): Change error code to HTTP_UNEXPECTED_FRAME.
     stream_delegate()->OnStreamError(
-        QUIC_INVALID_HEADERS_STREAM_DATA,
+        QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_SPDY_STREAM,
         "HEADERS frame received after trailing HEADERS.");
     return false;
   }
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 1d5305b..f1cb316 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -2473,10 +2473,9 @@
 
   // Closing the connection is mocked out in tests.  Instead, simply stop
   // reading data at the stream level to prevent QuicSpdyStream from blowing up.
-  // TODO(b/124216424): Change error code to HTTP_UNEXPECTED_FRAME.
   EXPECT_CALL(
       *connection_,
-      CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+      CloseConnection(QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_SPDY_STREAM,
                       "Unexpected DATA frame received.",
                       ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET))
       .WillOnce(InvokeWithoutArgs([this]() { stream_->StopReading(); }));
@@ -2523,10 +2522,9 @@
 
   // Closing the connection is mocked out in tests.  Instead, simply stop
   // reading data at the stream level to prevent QuicSpdyStream from blowing up.
-  // TODO(b/124216424): Change error code to HTTP_UNEXPECTED_FRAME.
   EXPECT_CALL(
       *connection_,
-      CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+      CloseConnection(QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_SPDY_STREAM,
                       "HEADERS frame received after trailing HEADERS.",
                       ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET))
       .WillOnce(InvokeWithoutArgs([this]() { stream_->StopReading(); }));
@@ -2574,10 +2572,9 @@
 
   // Closing the connection is mocked out in tests.  Instead, simply stop
   // reading data at the stream level to prevent QuicSpdyStream from blowing up.
-  // TODO(b/124216424): Change error code to HTTP_UNEXPECTED_FRAME.
   EXPECT_CALL(
       *connection_,
-      CloseConnection(QUIC_INVALID_HEADERS_STREAM_DATA,
+      CloseConnection(QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_SPDY_STREAM,
                       "Unexpected DATA frame received.",
                       ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET))
       .WillOnce(InvokeWithoutArgs([this]() { stream_->StopReading(); }));