Add a QUIC protocol flag to disable the QPACK dynamic table
in QUIC servers.

Protected by FLAGS_quic_server_disable_qpack_dynamic_table.

PiperOrigin-RevId: 583401582
diff --git a/quiche/quic/core/http/quic_spdy_session.cc b/quiche/quic/core/http/quic_spdy_session.cc
index 4319cad..c8b3196 100644
--- a/quiche/quic/core/http/quic_spdy_session.cc
+++ b/quiche/quic/core/http/quic_spdy_session.cc
@@ -170,6 +170,15 @@
   bool settings_frame_received_via_alps_ = false;
 };
 
+uint64_t GetDefaultQpackMaximumDynamicTableCapacity(Perspective perspective) {
+  if (perspective == Perspective::IS_SERVER &&
+      GetQuicFlag(quic_server_disable_qpack_dynamic_table)) {
+    return 0;
+  }
+
+  return kDefaultQpackMaxDynamicTableCapacity;
+}
+
 }  // namespace
 
 // A SpdyFramerVisitor that passes HEADERS frames to the QuicSpdyStream, and
@@ -466,7 +475,7 @@
       qpack_encoder_send_stream_(nullptr),
       qpack_decoder_send_stream_(nullptr),
       qpack_maximum_dynamic_table_capacity_(
-          kDefaultQpackMaxDynamicTableCapacity),
+          GetDefaultQpackMaximumDynamicTableCapacity(perspective())),
       qpack_maximum_blocked_streams_(kDefaultMaximumBlockedStreams),
       max_inbound_header_list_size_(kDefaultMaxUncompressedHeaderSize),
       max_outbound_header_list_size_(std::numeric_limits<size_t>::max()),
diff --git a/quiche/quic/core/http/quic_spdy_session_test.cc b/quiche/quic/core/http/quic_spdy_session_test.cc
index 015318b..0d80b6c 100644
--- a/quiche/quic/core/http/quic_spdy_session_test.cc
+++ b/quiche/quic/core/http/quic_spdy_session_test.cc
@@ -405,6 +405,7 @@
   using QuicSession::closed_streams;
   using QuicSession::pending_streams_size;
   using QuicSession::ShouldKeepConnectionAlive;
+  using QuicSpdySession::settings;
   using QuicSpdySession::UsesPendingStreamForFrame;
 
  private:
@@ -2032,6 +2033,45 @@
   session_->OnStreamFrame(data1);
 }
 
+TEST_P(QuicSpdySessionTestClient, ServerDisableQpackDynamicTable) {
+  SetQuicFlag(quic_server_disable_qpack_dynamic_table, true);
+  Initialize();
+  if (!VersionUsesHttp3(transport_version())) {
+    return;
+  }
+  CompleteHandshake();
+
+  // Use an arbitrary stream id for creating the receive control stream.
+  QuicStreamId stream_id =
+      GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3);
+  char type[] = {kControlStream};
+  QuicStreamFrame data1(stream_id, false, 0, absl::string_view(type, 1));
+  session_->OnStreamFrame(data1);
+  EXPECT_EQ(stream_id,
+            QuicSpdySessionPeer::GetReceiveControlStream(&*session_)->id());
+  // Receive the QPACK dynamic table capacity from the peer.
+  const uint64_t capacity = 512;
+  SettingsFrame settings;
+  settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = capacity;
+  std::string data = HttpEncoder::SerializeSettingsFrame(settings);
+  QuicStreamFrame frame(stream_id, false, 1, data);
+  session_->OnStreamFrame(frame);
+
+  // Verify that the encoder's dynamic table capacity is limited to the
+  // peer's value.
+  QpackEncoder* qpack_encoder = session_->qpack_encoder();
+  EXPECT_EQ(capacity, qpack_encoder->MaximumDynamicTableCapacity());
+  QpackEncoderHeaderTable* encoder_header_table =
+      QpackEncoderPeer::header_table(qpack_encoder);
+  EXPECT_EQ(capacity, encoder_header_table->dynamic_table_capacity());
+  EXPECT_EQ(capacity, encoder_header_table->maximum_dynamic_table_capacity());
+
+  // Verify that the advertised capacity is the default.
+  SettingsFrame outgoing_settings = session_->settings();
+  EXPECT_EQ(kDefaultQpackMaxDynamicTableCapacity,
+            outgoing_settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY]);
+}
+
 TEST_P(QuicSpdySessionTestServer, OnStreamFrameLost) {
   Initialize();
   CompleteHandshake();
@@ -2551,6 +2591,43 @@
   EXPECT_EQ(42u, QpackEncoderPeer::maximum_blocked_streams(qpack_encoder));
 }
 
+TEST_P(QuicSpdySessionTestServer, ServerDisableQpackDynamicTable) {
+  SetQuicFlag(quic_server_disable_qpack_dynamic_table, true);
+  Initialize();
+  if (!VersionUsesHttp3(transport_version())) {
+    return;
+  }
+  CompleteHandshake();
+
+  // Use an arbitrary stream id for creating the receive control stream.
+  QuicStreamId stream_id =
+      GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3);
+  char type[] = {kControlStream};
+  QuicStreamFrame data1(stream_id, false, 0, absl::string_view(type, 1));
+  session_->OnStreamFrame(data1);
+  EXPECT_EQ(stream_id,
+            QuicSpdySessionPeer::GetReceiveControlStream(&*session_)->id());
+  // Receive the QPACK dynamic table capacity from the peer.
+  const uint64_t capacity = 512;
+  SettingsFrame settings;
+  settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = capacity;
+  std::string data = HttpEncoder::SerializeSettingsFrame(settings);
+  QuicStreamFrame frame(stream_id, false, 1, data);
+  session_->OnStreamFrame(frame);
+
+  // Verify that the encoder's dynamic table capacity is 0.
+  QpackEncoder* qpack_encoder = session_->qpack_encoder();
+  EXPECT_EQ(capacity, qpack_encoder->MaximumDynamicTableCapacity());
+  QpackEncoderHeaderTable* encoder_header_table =
+      QpackEncoderPeer::header_table(qpack_encoder);
+  EXPECT_EQ(capacity, encoder_header_table->maximum_dynamic_table_capacity());
+  EXPECT_EQ(0, encoder_header_table->dynamic_table_capacity());
+
+  // Verify that the advertised capacity is 0.
+  SettingsFrame outgoing_settings = session_->settings();
+  EXPECT_EQ(0, outgoing_settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY]);
+}
+
 TEST_P(QuicSpdySessionTestServer, ReceiveControlStreamOutOfOrderDelivery) {
   Initialize();
   if (!VersionUsesHttp3(transport_version())) {
diff --git a/quiche/quic/core/quic_protocol_flags_list.h b/quiche/quic/core/quic_protocol_flags_list.h
index 7644cbf..bd337e9 100644
--- a/quiche/quic/core/quic_protocol_flags_list.h
+++ b/quiche/quic/core/quic_protocol_flags_list.h
@@ -222,6 +222,11 @@
 QUIC_PROTOCOL_FLAG(bool, quic_interval_set_enable_add_optimization, true,
                    "If true, enable an optimization in QuicIntervalSet")
 
+QUIC_PROTOCOL_FLAG(bool, quic_server_disable_qpack_dynamic_table, false,
+                   "If true, disables use of the QPACK dynamic table in "
+                   "servers, both for decoding context (requests) and for "
+                   "encoding context (responses).")
+
 QUIC_PROTOCOL_FLAG(
     bool, quic_enable_chaos_protection, true,
     "If true, use chaos protection to randomize client initials.")