Unify and refactor frame type logic in receiving control stream.

An error is signalled under exactly the same conditions, but the error code and
message might change.  In particular, a MAX_PUSH_ID or ACCEPT_CH frame received
by the wrong endpoint as the first frame resulted in a
QUIC_HTTP_MISSING_SETTINGS_FRAME, while a DATA or HEADERS as the first frame
resulted in a QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM before this CL.
After this CL, any forbidden frame type (for the endpoint) results in
QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM.

Error messages are also changed to make the code simpler.

OnUnrecoverableError() (used 2 times) is exactly the same as
stream_delegate()->OnStreamError() (used 9 times), this CL changes both
occurrences of the former into the latter for consistency.

HttpDecoder::Visitor methods for frame processing other than *Start() cannot be
called for disallowed frames, this CL adds QUICHE_NOTREACHED() to document that.

The motivation is to prepare for changing the SETTINGS frame logic to accomodate
a SETTINGS frame in ALPS, see
https://vasilvv.github.io/httpbis-alps/draft-vvv-httpbis-alps.html#name-use-of-alps-in-http-3.

PiperOrigin-RevId: 358846826
Change-Id: I0f361fa9899f1bd558603b804a6aad6f4f38b218
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc
index 70e7f4a..35d5902 100644
--- a/quic/core/http/quic_receive_control_stream.cc
+++ b/quic/core/http/quic_receive_control_stream.cc
@@ -62,7 +62,7 @@
 }
 
 void QuicReceiveControlStream::OnError(HttpDecoder* decoder) {
-  OnUnrecoverableError(decoder->error(), decoder->error_detail());
+  stream_delegate()->OnStreamError(decoder->error(), decoder->error_detail());
 }
 
 bool QuicReceiveControlStream::OnCancelPushFrame(const CancelPushFrame& frame) {
@@ -70,15 +70,8 @@
     spdy_session()->debug_visitor()->OnCancelPushFrameReceived(frame);
   }
 
-  if (!settings_frame_received_) {
-    stream_delegate()->OnStreamError(
-        QUIC_HTTP_MISSING_SETTINGS_FRAME,
-        "CANCEL_PUSH frame received before SETTINGS.");
-    return false;
-  }
-
   // TODO(b/151841240): Handle CANCEL_PUSH frames instead of ignoring them.
-  return true;
+  return ValidateFrameType(HttpFrameType::CANCEL_PUSH);
 }
 
 bool QuicReceiveControlStream::OnMaxPushIdFrame(const MaxPushIdFrame& frame) {
@@ -86,15 +79,7 @@
     spdy_session()->debug_visitor()->OnMaxPushIdFrameReceived(frame);
   }
 
-  if (!settings_frame_received_) {
-    stream_delegate()->OnStreamError(
-        QUIC_HTTP_MISSING_SETTINGS_FRAME,
-        "MAX_PUSH_ID frame received before SETTINGS.");
-    return false;
-  }
-
-  if (spdy_session()->perspective() == Perspective::IS_CLIENT) {
-    OnWrongFrame("Max Push Id");
+  if (!ValidateFrameType(HttpFrameType::MAX_PUSH_ID)) {
     return false;
   }
 
@@ -106,9 +91,7 @@
     spdy_session()->debug_visitor()->OnGoAwayFrameReceived(frame);
   }
 
-  if (!settings_frame_received_) {
-    stream_delegate()->OnStreamError(QUIC_HTTP_MISSING_SETTINGS_FRAME,
-                                     "GOAWAY frame received before SETTINGS.");
+  if (!ValidateFrameType(HttpFrameType::GOAWAY)) {
     return false;
   }
 
@@ -118,16 +101,7 @@
 
 bool QuicReceiveControlStream::OnSettingsFrameStart(
     QuicByteCount /*header_length*/) {
-  if (settings_frame_received_) {
-    stream_delegate()->OnStreamError(
-        QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
-        "Settings frames are received twice.");
-    return false;
-  }
-
-  settings_frame_received_ = true;
-
-  return true;
+  return ValidateFrameType(HttpFrameType::SETTINGS);
 }
 
 bool QuicReceiveControlStream::OnSettingsFrame(const SettingsFrame& frame) {
@@ -139,18 +113,17 @@
 bool QuicReceiveControlStream::OnDataFrameStart(QuicByteCount /*header_length*/,
                                                 QuicByteCount
                                                 /*payload_length*/) {
-  OnWrongFrame("Data");
-  return false;
+  return ValidateFrameType(HttpFrameType::DATA);
 }
 
 bool QuicReceiveControlStream::OnDataFramePayload(
     absl::string_view /*payload*/) {
-  OnWrongFrame("Data");
+  QUICHE_NOTREACHED();
   return false;
 }
 
 bool QuicReceiveControlStream::OnDataFrameEnd() {
-  OnWrongFrame("Data");
+  QUICHE_NOTREACHED();
   return false;
 }
 
@@ -158,55 +131,47 @@
     QuicByteCount /*header_length*/,
     QuicByteCount
     /*payload_length*/) {
-  OnWrongFrame("Headers");
-  return false;
+  return ValidateFrameType(HttpFrameType::HEADERS);
 }
 
 bool QuicReceiveControlStream::OnHeadersFramePayload(
     absl::string_view /*payload*/) {
-  OnWrongFrame("Headers");
+  QUICHE_NOTREACHED();
   return false;
 }
 
 bool QuicReceiveControlStream::OnHeadersFrameEnd() {
-  OnWrongFrame("Headers");
+  QUICHE_NOTREACHED();
   return false;
 }
 
 bool QuicReceiveControlStream::OnPushPromiseFrameStart(
     QuicByteCount /*header_length*/) {
-  OnWrongFrame("Push Promise");
-  return false;
+  return ValidateFrameType(HttpFrameType::PUSH_PROMISE);
 }
 
 bool QuicReceiveControlStream::OnPushPromiseFramePushId(
     PushId /*push_id*/,
     QuicByteCount /*push_id_length*/,
     QuicByteCount /*header_block_length*/) {
-  OnWrongFrame("Push Promise");
+  QUICHE_NOTREACHED();
   return false;
 }
 
 bool QuicReceiveControlStream::OnPushPromiseFramePayload(
     absl::string_view /*payload*/) {
-  OnWrongFrame("Push Promise");
+  QUICHE_NOTREACHED();
   return false;
 }
 
 bool QuicReceiveControlStream::OnPushPromiseFrameEnd() {
-  OnWrongFrame("Push Promise");
+  QUICHE_NOTREACHED();
   return false;
 }
 
 bool QuicReceiveControlStream::OnPriorityUpdateFrameStart(
     QuicByteCount /*header_length*/) {
-  if (!settings_frame_received_) {
-    stream_delegate()->OnStreamError(
-        QUIC_HTTP_MISSING_SETTINGS_FRAME,
-        "PRIORITY_UPDATE frame received before SETTINGS.");
-    return false;
-  }
-  return true;
+  return ValidateFrameType(HttpFrameType::PRIORITY_UPDATE);
 }
 
 bool QuicReceiveControlStream::OnPriorityUpdateFrame(
@@ -254,19 +219,7 @@
 
 bool QuicReceiveControlStream::OnAcceptChFrameStart(
     QuicByteCount /* header_length */) {
-  if (!settings_frame_received_) {
-    stream_delegate()->OnStreamError(
-        QUIC_HTTP_MISSING_SETTINGS_FRAME,
-        "ACCEPT_CH frame received before SETTINGS.");
-    return false;
-  }
-
-  if (spdy_session()->perspective() == Perspective::IS_SERVER) {
-    OnWrongFrame("ACCEPT_CH");
-    return false;
-  }
-
-  return true;
+  return ValidateFrameType(HttpFrameType::ACCEPT_CH);
 }
 
 bool QuicReceiveControlStream::OnAcceptChFrame(const AcceptChFrame& frame) {
@@ -289,13 +242,7 @@
                                                             payload_length);
   }
 
-  if (!settings_frame_received_) {
-    stream_delegate()->OnStreamError(QUIC_HTTP_MISSING_SETTINGS_FRAME,
-                                     "Unknown frame received before SETTINGS.");
-    return false;
-  }
-
-  return true;
+  return ValidateFrameType(static_cast<HttpFrameType>(frame_type));
 }
 
 bool QuicReceiveControlStream::OnUnknownFramePayload(
@@ -309,10 +256,43 @@
   return true;
 }
 
-void QuicReceiveControlStream::OnWrongFrame(absl::string_view frame_type) {
-  OnUnrecoverableError(
-      QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM,
-      absl::StrCat(frame_type, " frame received on control stream"));
+bool QuicReceiveControlStream::ValidateFrameType(HttpFrameType frame_type) {
+  // Certain frame types are forbidden.
+  if ((frame_type == HttpFrameType::DATA ||
+       frame_type == HttpFrameType::HEADERS ||
+       frame_type == HttpFrameType::PUSH_PROMISE) ||
+      (spdy_session()->perspective() == Perspective::IS_CLIENT &&
+       frame_type == HttpFrameType::MAX_PUSH_ID) ||
+      (GetQuicReloadableFlag(quic_parse_accept_ch_frame) &&
+       spdy_session()->perspective() == Perspective::IS_SERVER &&
+       frame_type == HttpFrameType::ACCEPT_CH)) {
+    stream_delegate()->OnStreamError(
+        QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM,
+        absl::StrCat("Invalid frame type ", static_cast<int>(frame_type),
+                     " received on control stream."));
+    return false;
+  }
+
+  if (settings_frame_received_) {
+    if (frame_type == HttpFrameType::SETTINGS) {
+      // SETTINGS frame may only be the first frame on the control stream.
+      stream_delegate()->OnStreamError(
+          QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
+          "SETTINGS frame can only be received once.");
+      return false;
+    }
+    return true;
+  }
+
+  if (frame_type == HttpFrameType::SETTINGS) {
+    settings_frame_received_ = true;
+    return true;
+  }
+  stream_delegate()->OnStreamError(
+      QUIC_HTTP_MISSING_SETTINGS_FRAME,
+      absl::StrCat("First frame received on control stream is type ",
+                   static_cast<int>(frame_type), ", but it must be SETTINGS."));
+  return false;
 }
 
 }  // namespace quic
diff --git a/quic/core/http/quic_receive_control_stream.h b/quic/core/http/quic_receive_control_stream.h
index 8699021..326dba3 100644
--- a/quic/core/http/quic_receive_control_stream.h
+++ b/quic/core/http/quic_receive_control_stream.h
@@ -69,7 +69,10 @@
   QuicSpdySession* spdy_session() { return spdy_session_; }
 
  private:
-  void OnWrongFrame(absl::string_view frame_type);
+  // Called when a frame of allowed type is received.  Returns true if the frame
+  // is allowed in this position.  Returns false and resets the stream
+  // otherwise.
+  bool ValidateFrameType(HttpFrameType frame_type);
 
   // 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 fdd74b0..3e4532f 100644
--- a/quic/core/http/quic_receive_control_stream_test.cc
+++ b/quic/core/http/quic_receive_control_stream_test.cc
@@ -206,7 +206,7 @@
   EXPECT_CALL(
       *connection_,
       CloseConnection(QUIC_HTTP_INVALID_FRAME_SEQUENCE_ON_CONTROL_STREAM,
-                      "Settings frames are received twice.", _))
+                      "SETTINGS frame can only be received once.", _))
       .WillOnce(
           Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
   EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
@@ -258,10 +258,11 @@
   QuicStreamFrame data(receive_control_stream_->id(), /* fin = */ false,
                        /* offset = */ 1, serialized_frame);
 
-  EXPECT_CALL(
-      *connection_,
-      CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME,
-                      "PRIORITY_UPDATE frame received before SETTINGS.", _))
+  EXPECT_CALL(*connection_,
+              CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME,
+                              "First frame received on control stream is type "
+                              "15, but it must be SETTINGS.",
+                              _))
       .WillOnce(
           Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
   EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
@@ -385,7 +386,9 @@
 
   EXPECT_CALL(*connection_,
               CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME,
-                              "CANCEL_PUSH frame received before SETTINGS.", _))
+                              "First frame received on control stream is type "
+                              "3, but it must be SETTINGS.",
+                              _))
       .WillOnce(
           Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
   EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
@@ -401,14 +404,23 @@
       "4089"  // type (ACCEPT_CH)
       "00");  // length
 
-  EXPECT_CALL(*connection_,
-              CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME,
-                              GetQuicReloadableFlag(quic_parse_accept_ch_frame)
-                                  ? "ACCEPT_CH frame received before SETTINGS."
-                                  : "Unknown frame received before SETTINGS.",
-                              _))
-      .WillOnce(
-          Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
+  if (GetQuicReloadableFlag(quic_parse_accept_ch_frame) &&
+      perspective() == Perspective::IS_SERVER) {
+    EXPECT_CALL(*connection_,
+                CloseConnection(
+                    QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM,
+                    "Invalid frame type 137 received on control stream.", _))
+        .WillOnce(
+            Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
+  } else {
+    EXPECT_CALL(*connection_,
+                CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME,
+                                "First frame received on control stream is "
+                                "type 137, but it must be SETTINGS.",
+                                _))
+        .WillOnce(
+            Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
+  }
   EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
   EXPECT_CALL(session_, OnConnectionClosed(_, _));
 
@@ -441,10 +453,10 @@
     if (perspective() == Perspective::IS_CLIENT) {
       EXPECT_CALL(debug_visitor, OnAcceptChFrameReceived(_));
     } else {
-      EXPECT_CALL(
-          *connection_,
-          CloseConnection(QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM,
-                          "ACCEPT_CH frame received on control stream", _))
+      EXPECT_CALL(*connection_,
+                  CloseConnection(
+                      QUIC_HTTP_FRAME_UNEXPECTED_ON_CONTROL_STREAM,
+                      "Invalid frame type 137 received on control stream.", _))
           .WillOnce(
               Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
       EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));
@@ -468,7 +480,9 @@
 
   EXPECT_CALL(*connection_,
               CloseConnection(QUIC_HTTP_MISSING_SETTINGS_FRAME,
-                              "Unknown frame received before SETTINGS.", _))
+                              "First frame received on control stream is type "
+                              "33, but it must be SETTINGS.",
+                              _))
       .WillOnce(
           Invoke(connection_, &MockQuicConnection::ReallyCloseConnection));
   EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _));