Treat HTTP/2 specific SETTING identifiers as errors in HTTP/3.

This is specified in https://datatracker.ietf.org/doc/html/draft-ietf-quic-http-30#appendix-A.3

Protected by quic_reloadable_flag_quic_reject_spdy_settings.

PiperOrigin-RevId: 333116208
Change-Id: I946f35ec70c490efb8ca10dbfb1bf9799240daf8
diff --git a/quic/core/http/quic_receive_control_stream_test.cc b/quic/core/http/quic_receive_control_stream_test.cc
index af80654..5ee5f50 100644
--- a/quic/core/http/quic_receive_control_stream_test.cc
+++ b/quic/core/http/quic_receive_control_stream_test.cc
@@ -156,7 +156,7 @@
 
 TEST_P(QuicReceiveControlStreamTest, ReceiveSettings) {
   SettingsFrame settings;
-  settings.values[3] = 2;
+  settings.values[10] = 2;
   settings.values[SETTINGS_MAX_FIELD_SECTION_SIZE] = 5;
   settings.values[SETTINGS_QPACK_BLOCKED_STREAMS] = 12;
   settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 37;
@@ -223,7 +223,7 @@
 
 TEST_P(QuicReceiveControlStreamTest, ReceiveSettingsFragments) {
   SettingsFrame settings;
-  settings.values[3] = 2;
+  settings.values[10] = 2;
   settings.values[SETTINGS_MAX_FIELD_SECTION_SIZE] = 5;
   std::string data = EncodeSettings(settings);
   std::string data1 = data.substr(0, 1);
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 50be81d..7065c3b 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -404,7 +404,8 @@
       server_push_enabled_(true),
       ietf_server_push_enabled_(
           GetQuicFlag(FLAGS_quic_enable_http3_server_push)),
-      http3_max_push_id_sent_(false) {
+      http3_max_push_id_sent_(false),
+      reject_spdy_settings_(GetQuicReloadableFlag(quic_reject_spdy_settings)) {
   h2_deframer_.set_visitor(spdy_framer_visitor_.get());
   h2_deframer_.set_debug_visitor(spdy_framer_visitor_.get());
   spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
@@ -970,6 +971,9 @@
 
 bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) {
   if (VersionUsesHttp3(transport_version())) {
+    if (reject_spdy_settings_) {
+      QUIC_RELOADABLE_FLAG_COUNT(quic_reject_spdy_settings);
+    }
     // SETTINGS frame received on the control stream.
     switch (id) {
       case SETTINGS_QPACK_MAX_TABLE_CAPACITY: {
@@ -1044,6 +1048,21 @@
         }
         break;
       }
+      case spdy::SETTINGS_ENABLE_PUSH:
+        QUIC_FALLTHROUGH_INTENDED;
+      case spdy::SETTINGS_MAX_CONCURRENT_STREAMS:
+        QUIC_FALLTHROUGH_INTENDED;
+      case spdy::SETTINGS_INITIAL_WINDOW_SIZE:
+        QUIC_FALLTHROUGH_INTENDED;
+      case spdy::SETTINGS_MAX_FRAME_SIZE:
+        if (reject_spdy_settings_) {
+          CloseConnectionWithDetails(
+              QUIC_HTTP_RECEIVE_SPDY_SETTING,
+              quiche::QuicheStrCat(
+                  "received HTTP/2 specific setting in HTTP/3 session: ", id));
+          return false;
+        }
+        break;
       default:
         QUIC_DVLOG(1) << ENDPOINT << "Unknown setting identifier " << id
                       << " received with value " << value;
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 0a71e48..db8fc6b 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -598,6 +598,9 @@
   // has been sent, in which case |max_push_id_| has the value sent in the most
   // recent MAX_PUSH_ID frame.  Once true, never goes back to false.
   bool http3_max_push_id_sent_;
+
+  // Latched value of reloadable flag quic_reject_spdy_settings.
+  const bool reject_spdy_settings_;
 };
 
 }  // namespace quic
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 7ce419a..b0e2c0c 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -2447,7 +2447,7 @@
       GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3);
   char type[] = {kControlStream};
   SettingsFrame settings;
-  settings.values[3] = 2;
+  settings.values[10] = 2;
   settings.values[SETTINGS_MAX_FIELD_SECTION_SIZE] = 5;
   std::string data = EncodeSettings(settings);
 
@@ -3104,6 +3104,25 @@
   CompleteHandshake();
 }
 
+TEST_P(QuicSpdySessionTestClient, ReceiveSpdySettingInHttp3) {
+  if (!VersionUsesHttp3(transport_version()) ||
+      !GetQuicReloadableFlag(quic_reject_spdy_settings)) {
+    return;
+  }
+
+  SettingsFrame frame;
+  frame.values[SETTINGS_MAX_FIELD_SECTION_SIZE] = 5;
+  // https://datatracker.ietf.org/doc/html/draft-ietf-quic-http-30#appendix-A.3
+  // specifies the presence of HTTP/2 setting as error.
+  frame.values[spdy::SETTINGS_INITIAL_WINDOW_SIZE] = 100;
+
+  CompleteHandshake();
+
+  EXPECT_CALL(*connection_,
+              CloseConnection(QUIC_HTTP_RECEIVE_SPDY_SETTING, _, _));
+  session_.OnSettingsFrame(frame);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index 304f2f2..54e4569 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -208,6 +208,7 @@
     RETURN_STRING_LITERAL(QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH);
     RETURN_STRING_LITERAL(QUIC_HTTP_GOAWAY_INVALID_STREAM_ID);
     RETURN_STRING_LITERAL(QUIC_HTTP_GOAWAY_ID_LARGER_THAN_PREVIOUS);
+    RETURN_STRING_LITERAL(QUIC_HTTP_RECEIVE_SPDY_SETTING);
     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);
@@ -586,6 +587,8 @@
       return {false, static_cast<uint64_t>(QuicHttp3ErrorCode::ID_ERROR)};
     case QUIC_HTTP_GOAWAY_ID_LARGER_THAN_PREVIOUS:
       return {false, static_cast<uint64_t>(QuicHttp3ErrorCode::ID_ERROR)};
+    case QUIC_HTTP_RECEIVE_SPDY_SETTING:
+      return {false, static_cast<uint64_t>(QuicHttp3ErrorCode::SETTINGS_ERROR)};
     case QUIC_HPACK_INDEX_VARINT_ERROR:
       return {true, static_cast<uint64_t>(INTERNAL_ERROR)};
     case QUIC_HPACK_NAME_LENGTH_VARINT_ERROR:
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index efeaa7c..0a415ef 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -442,6 +442,9 @@
   QUIC_HTTP_GOAWAY_INVALID_STREAM_ID = 166,
   // Received GOAWAY frame with ID that is greater than previously received ID.
   QUIC_HTTP_GOAWAY_ID_LARGER_THAN_PREVIOUS = 167,
+  // HTTP/3 session received SETTINGS frame which contains HTTP/2 specific
+  // settings.
+  QUIC_HTTP_RECEIVE_SPDY_SETTING = 169,
 
   // HPACK header block decoding errors.
   // Index varint beyond implementation limit.
@@ -493,7 +496,7 @@
   QUIC_SILENT_IDLE_TIMEOUT = 168,
 
   // No error. Used as bound while iterating.
-  QUIC_LAST_ERROR = 169,
+  QUIC_LAST_ERROR = 170,
 };
 // 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