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