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.")