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(); }));
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index ce9866c..e16da69 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -169,6 +169,8 @@
     RETURN_STRING_LITERAL(QUIC_HTTP_FRAME_ERROR);
     RETURN_STRING_LITERAL(QUIC_HTTP_FRAME_UNEXPECTED_ON_SPDY_STREAM);
     RETURN_STRING_LITERAL(QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM);
+    RETURN_STRING_LITERAL(QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_SPDY_STREAM);
+    RETURN_STRING_LITERAL(QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM);
     RETURN_STRING_LITERAL(QUIC_HPACK_INDEX_VARINT_ERROR);
     RETURN_STRING_LITERAL(QUIC_HPACK_NAME_LENGTH_VARINT_ERROR);
     RETURN_STRING_LITERAL(QUIC_HPACK_VALUE_LENGTH_VARINT_ERROR);
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index 7f250c3..557edfa 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -356,8 +356,16 @@
   // Internal error codes for HTTP/3 errors.
   QUIC_HTTP_FRAME_TOO_LARGE = 131,
   QUIC_HTTP_FRAME_ERROR = 132,
+  // A frame that is never allowed on a request stream is received.
   QUIC_HTTP_FRAME_UNEXPECTED_ON_SPDY_STREAM = 133,
+  // A frame that is never allowed on the control stream is received.
   QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM = 134,
+  // An invalid sequence of frames normally allowed on a request stream is
+  // received.
+  QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_SPDY_STREAM = 151,
+  // An invalid sequence of frames normally allowed on the control stream is
+  // received.
+  QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM = 152,
 
   // HPACK header block decoding errors.
   // Index varint beyond implementation limit.
@@ -394,7 +402,7 @@
   QUIC_HPACK_COMPRESSED_HEADER_SIZE_EXCEEDS_LIMIT = 150,
 
   // No error. Used as bound while iterating.
-  QUIC_LAST_ERROR = 151,
+  QUIC_LAST_ERROR = 153,
 };
 // QuicErrorCodes is encoded as four octets on-the-wire when doing Google QUIC,
 // or a varint62 when doing IETF QUIC. Ensure that its value does not exceed
diff --git a/quic/core/quic_types.cc b/quic/core/quic_types.cc
index ef5f5da..d1e76be 100644
--- a/quic/core/quic_types.cc
+++ b/quic/core/quic_types.cc
@@ -471,6 +471,14 @@
       return {false,
               {static_cast<uint64_t>(
                   QuicHttp3ErrorCode::IETF_QUIC_HTTP3_FRAME_UNEXPECTED)}};
+    case QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_SPDY_STREAM:
+      return {false,
+              {static_cast<uint64_t>(
+                  QuicHttp3ErrorCode::IETF_QUIC_HTTP3_FRAME_UNEXPECTED)}};
+    case QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM:
+      return {false,
+              {static_cast<uint64_t>(
+                  QuicHttp3ErrorCode::IETF_QUIC_HTTP3_FRAME_UNEXPECTED)}};
     case QUIC_HPACK_INDEX_VARINT_ERROR:
       return {false, {static_cast<uint64_t>(QUIC_HPACK_INDEX_VARINT_ERROR)}};
     case QUIC_HPACK_NAME_LENGTH_VARINT_ERROR: