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