Send Set Dynamic Table Capacity instruction.

Do not change dynamic table capacity in
QpackHeaderTable::SetMaximumDynamicTableCapacity(), because according to
https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#eviction, "The initial
capacity of the dynamic table is zero.".  Instead, add
QpackEncoder::SetDynamicTableCapacity() to send Set Dynamic Table Capacity
instruction.

Roundtrip tests were passing because QpackHeaderTable::SetMaximumDynamicTableCapacity() incorrectly updated the capacity for both the encoder and decoder context.

gfe-relnote: n/a, change to QUIC v99-only code.  Protected by existing disabled gfe2_reloadable_flag_quic_enable_version_99.
PiperOrigin-RevId: 266358801
Change-Id: I23f10f224139dee79e9305c42c85b75cba61a999
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 101b33d..73f63bb 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -694,9 +694,13 @@
         QUIC_DVLOG(1)
             << "SETTINGS_QPACK_MAX_TABLE_CAPACITY received with value "
             << value;
-        // TODO(b/112770235): Limit value to
-        // qpack_maximum_dynamic_table_capacity_.
+        // Communicate |value| to encoder, because it is used for encoding
+        // Required Insert Count.
         qpack_encoder_->SetMaximumDynamicTableCapacity(value);
+        // However, limit the dynamic table capacity to
+        // |qpack_maximum_dynamic_table_capacity_|.
+        qpack_encoder_->SetDynamicTableCapacity(
+            std::min(value, qpack_maximum_dynamic_table_capacity_));
         break;
       case SETTINGS_MAX_HEADER_LIST_SIZE:
         QUIC_DVLOG(1) << "SETTINGS_MAX_HEADER_LIST_SIZE received with value "
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 3798461..e09cbf4 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -1921,6 +1921,7 @@
 
   testing::InSequence s;
   Initialize(kShouldProcessData);
+  session_->qpack_decoder()->OnSetDynamicTableCapacity(1024);
 
   auto decoder_send_stream =
       QuicSpdySessionPeer::GetQpackDecoderSendStream(session_.get());
@@ -1983,6 +1984,7 @@
 
   testing::InSequence s;
   Initialize(kShouldProcessData);
+  session_->qpack_decoder()->OnSetDynamicTableCapacity(1024);
 
   // HEADERS frame referencing first dynamic table entry.
   std::string headers = HeadersFrame(QuicTextUtils::HexDecode("020080"));
@@ -2040,6 +2042,7 @@
   }
 
   Initialize(kShouldProcessData);
+  session_->qpack_decoder()->OnSetDynamicTableCapacity(1024);
 
   // HEADERS frame only referencing entry with absolute index 0 but with
   // Required Insert Count = 2, which is incorrect.
@@ -2078,6 +2081,7 @@
 
   testing::InSequence s;
   Initialize(kShouldProcessData);
+  session_->qpack_decoder()->OnSetDynamicTableCapacity(1024);
 
   // HEADERS frame referencing first dynamic table entry.
   std::string headers = HeadersFrame(QuicTextUtils::HexDecode("020080"));
diff --git a/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc b/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
index 624eca5..a26a6c3 100644
--- a/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
+++ b/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
@@ -45,6 +45,10 @@
     encoder_.set_qpack_stream_sender_delegate(delegate);
   }
 
+  void SetDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity) {
+    encoder_.SetDynamicTableCapacity(maximum_dynamic_table_capacity);
+  }
+
   QpackStreamReceiver* decoder_stream_receiver() {
     return encoder_.decoder_stream_receiver();
   }
@@ -596,6 +600,10 @@
       decoder.encoder_stream_receiver(), &provider);
   encoder.set_qpack_stream_sender_delegate(&encoder_stream_transmitter);
 
+  // Use a dynamic table as large as the peer allows.  This sends data on the
+  // encoder stream, so it can only be done after delegate is set.
+  encoder.SetDynamicTableCapacity(maximum_dynamic_table_capacity);
+
   // Transmit decoder stream data from encoder to decoder.
   DelayedStreamDataTransmitter decoder_stream_transmitter(
       encoder.decoder_stream_receiver(), &provider);
diff --git a/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc b/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
index 2a0a222..1d60660 100644
--- a/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
+++ b/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
@@ -134,6 +134,8 @@
   EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("020080")));
   EXPECT_EQ(Status::kBlocked, accumulator_.EndHeaderBlock());
 
+  // Set dynamic table capacity.
+  qpack_decoder_.OnSetDynamicTableCapacity(kMaxDynamicTableCapacity);
   // Adding dynamic table entry unblocks decoding.
   EXPECT_CALL(decoder_stream_sender_delegate_,
               WriteStreamData(Eq(kHeaderAcknowledgement)));
@@ -147,6 +149,8 @@
   // Reference to dynamic table entry not yet received.
   EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("020080")));
 
+  // Set dynamic table capacity.
+  qpack_decoder_.OnSetDynamicTableCapacity(kMaxDynamicTableCapacity);
   // Adding dynamic table entry unblocks decoding.
   qpack_decoder_.OnInsertWithoutNameReference("foo", "bar");
 
diff --git a/quic/core/qpack/qpack_decoder_test.cc b/quic/core/qpack/qpack_decoder_test.cc
index 0a4e5e2..ef711b9 100644
--- a/quic/core/qpack/qpack_decoder_test.cc
+++ b/quic/core/qpack/qpack_decoder_test.cc
@@ -301,6 +301,7 @@
 
 TEST_P(QpackDecoderTest, DynamicTable) {
   DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+      "3fe107"          // Set dynamic table capacity to 1024.
       "6294e703626172"  // Add literal entry with name "foo" and value "bar".
       "80035a5a5a"      // Add entry with name of dynamic table entry index 0
                         // (relative index) and value "ZZZ".
@@ -382,6 +383,8 @@
 }
 
 TEST_P(QpackDecoderTest, DecreasingDynamicTableCapacityEvictsEntries) {
+  // Set dynamic table capacity to 1024.
+  DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107"));
   // Add literal entry with name "foo" and value "bar".
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
 
@@ -431,6 +434,7 @@
               OnEncoderStreamError(Eq("Invalid relative index.")));
 
   DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+      "3fe107"          // Set dynamic table capacity to 1024.
       "6294e703626172"  // Add literal entry with name "foo" and value "bar".
       "8100"));  // Address dynamic table entry with relative index 1.  Such
                  // entry does not exist.  The most recently added and only
@@ -442,6 +446,7 @@
               OnEncoderStreamError(Eq("Invalid relative index.")));
 
   DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+      "3fe107"          // Set dynamic table capacity to 1024.
       "6294e703626172"  // Add literal entry with name "foo" and value "bar".
       "01"));  // Duplicate dynamic table entry with relative index 1.  Such
                // entry does not exist.  The most recently added and only
@@ -458,6 +463,8 @@
 TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIsZero) {
   EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
 
+  // Set dynamic table capacity to 1024.
+  DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107"));
   // Add literal entry with name "foo" and value "bar".
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
 
@@ -477,6 +484,8 @@
 }
 
 TEST_P(QpackDecoderTest, InvalidDynamicEntryByRelativeIndex) {
+  // Set dynamic table capacity to 1024.
+  DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107"));
   // Add literal entry with name "foo" and value "bar".
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
 
@@ -585,6 +594,8 @@
   // Maximum dynamic table capacity is 1024.
   // MaxEntries is 1024 / 32 = 32.
 
+  // Set dynamic table capacity to 1024.
+  DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107"));
   // Add literal entry with name "foo" and a 600 byte long value.  This will fit
   // in the dynamic table once but not twice.
   DecodeEncoderStreamData(
@@ -611,6 +622,8 @@
 }
 
 TEST_P(QpackDecoderTest, NonZeroRequiredInsertCountButNoDynamicEntries) {
+  // Set dynamic table capacity to 1024.
+  DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107"));
   // Add literal entry with name "foo" and value "bar".
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
 
@@ -624,6 +637,8 @@
 }
 
 TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) {
+  // Set dynamic table capacity to 1024.
+  DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107"));
   // Add literal entry with name "foo" and value "bar".
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
 
@@ -680,6 +695,8 @@
 }
 
 TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) {
+  // Set dynamic table capacity to 1024.
+  DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107"));
   // Add literal entry with name "foo" and value "bar".
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
   // Duplicate entry twice so that decoding of header blocks with Required
@@ -748,6 +765,8 @@
   EXPECT_CALL(decoder_stream_sender_delegate_,
               WriteStreamData(Eq(kHeaderAcknowledgement)));
 
+  // Set dynamic table capacity to 1024.
+  DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107"));
   // Add literal entry with name "foo" and value "bar".
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
 }
@@ -767,6 +786,10 @@
   // the already consumed part of the header block.
   EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
   EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET")));
+
+  // Set dynamic table capacity to 1024.
+  DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe107"));
+  // Add literal entry with name "foo" and value "bar".
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
   Mock::VerifyAndClearExpectations(&handler_);
 
diff --git a/quic/core/qpack/qpack_encoder.cc b/quic/core/qpack/qpack_encoder.cc
index 370887a..d1736f8 100644
--- a/quic/core/qpack/qpack_encoder.cc
+++ b/quic/core/qpack/qpack_encoder.cc
@@ -318,11 +318,15 @@
 
 void QpackEncoder::SetMaximumDynamicTableCapacity(
     uint64_t maximum_dynamic_table_capacity) {
-  // TODO(b/112770235): Send set dynamic table capacity instruction on encoder
-  // stream.
   header_table_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity);
 }
 
+void QpackEncoder::SetDynamicTableCapacity(uint64_t dynamic_table_capacity) {
+  encoder_stream_sender_.SendSetDynamicTableCapacity(dynamic_table_capacity);
+  bool success = header_table_.SetDynamicTableCapacity(dynamic_table_capacity);
+  DCHECK(success);
+}
+
 void QpackEncoder::SetMaximumBlockedStreams(uint64_t maximum_blocked_streams) {
   maximum_blocked_streams_ = maximum_blocked_streams;
 }
diff --git a/quic/core/qpack/qpack_encoder.h b/quic/core/qpack/qpack_encoder.h
index 993b76e..4aa69c1 100644
--- a/quic/core/qpack/qpack_encoder.h
+++ b/quic/core/qpack/qpack_encoder.h
@@ -53,12 +53,17 @@
   std::string EncodeHeaderList(QuicStreamId stream_id,
                                const spdy::SpdyHeaderBlock& header_list);
 
-  // Set maximum capacity of dynamic table, measured in bytes.
-  // Called when SETTINGS_QPACK_MAX_TABLE_CAPACITY is received.
-  // Sends set dynamic table capacity instruction on encoder stream.
-  // TODO(b/112770235): Actually send set dynamic table capacity instruction.
+  // Set maximum dynamic table capacity to |maximum_dynamic_table_capacity|,
+  // measured in bytes.  Called when SETTINGS_QPACK_MAX_TABLE_CAPACITY is
+  // received.  Encoder needs to know this value so that it can calculate
+  // MaxEntries, used as a modulus to encode Required Insert Count.
   void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity);
 
+  // Set dynamic table capacity to |dynamic_table_capacity|.
+  // |dynamic_table_capacity| must not exceed maximum dynamic table capacity.
+  // Also sends Set Dynamic Table Capacity instruction on encoder stream.
+  void SetDynamicTableCapacity(uint64_t dynamic_table_capacity);
+
   // Set maximum number of blocked streams.
   // Called when SETTINGS_QPACK_BLOCKED_STREAMS is received.
   void SetMaximumBlockedStreams(uint64_t maximum_blocked_streams);
diff --git a/quic/core/qpack/qpack_encoder_test.cc b/quic/core/qpack/qpack_encoder_test.cc
index f23cb8b..0da6f3e 100644
--- a/quic/core/qpack/qpack_encoder_test.cc
+++ b/quic/core/qpack/qpack_encoder_test.cc
@@ -11,6 +11,8 @@
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/qpack_encoder_peer.h"
+#include "net/third_party/quiche/src/quic/test_tools/qpack_header_table_peer.h"
 
 using ::testing::_;
 using ::testing::Eq;
@@ -182,8 +184,13 @@
 }
 
 TEST_F(QpackEncoderTest, DynamicTable) {
-  encoder_.SetMaximumDynamicTableCapacity(4096);
   encoder_.SetMaximumBlockedStreams(1);
+  encoder_.SetMaximumDynamicTableCapacity(4096);
+
+  // Set Dynamic Table Capacity instruction.
+  EXPECT_CALL(encoder_stream_sender_delegate_,
+              WriteStreamData(Eq(QuicTextUtils::HexDecode("3fe11f"))));
+  encoder_.SetDynamicTableCapacity(4096);
 
   spdy::SpdyHeaderBlock header_list;
   header_list["foo"] = "bar";
@@ -214,8 +221,13 @@
 
 // There is no room in the dynamic table after inserting the first entry.
 TEST_F(QpackEncoderTest, SmallDynamicTable) {
-  encoder_.SetMaximumDynamicTableCapacity(QpackEntry::Size("foo", "bar"));
   encoder_.SetMaximumBlockedStreams(1);
+  encoder_.SetMaximumDynamicTableCapacity(QpackEntry::Size("foo", "bar"));
+
+  // Set Dynamic Table Capacity instruction.
+  EXPECT_CALL(encoder_stream_sender_delegate_,
+              WriteStreamData(Eq(QuicTextUtils::HexDecode("3f07"))));
+  encoder_.SetDynamicTableCapacity(QpackEntry::Size("foo", "bar"));
 
   spdy::SpdyHeaderBlock header_list;
   header_list["foo"] = "bar";
@@ -243,8 +255,13 @@
 }
 
 TEST_F(QpackEncoderTest, BlockedStream) {
-  encoder_.SetMaximumDynamicTableCapacity(4096);
   encoder_.SetMaximumBlockedStreams(1);
+  encoder_.SetMaximumDynamicTableCapacity(4096);
+
+  // Set Dynamic Table Capacity instruction.
+  EXPECT_CALL(encoder_stream_sender_delegate_,
+              WriteStreamData(Eq(QuicTextUtils::HexDecode("3fe11f"))));
+  encoder_.SetDynamicTableCapacity(4096);
 
   spdy::SpdyHeaderBlock header_list1;
   header_list1["foo"] = "bar";
@@ -362,6 +379,10 @@
   maximum_dynamic_table_capacity += QpackEntry::Size("one", "foo");
   encoder_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity);
 
+  // Set Dynamic Table Capacity instruction.
+  EXPECT_CALL(encoder_stream_sender_delegate_, WriteStreamData(_));
+  encoder_.SetDynamicTableCapacity(maximum_dynamic_table_capacity);
+
   // Insert ten entries into the dynamic table.
   EXPECT_CALL(encoder_stream_sender_delegate_, WriteStreamData(_)).Times(10);
 
@@ -399,6 +420,21 @@
             Encode(header_list3));
 }
 
+TEST_F(QpackEncoderTest, DynamicTableCapacityLessThanMaximum) {
+  encoder_.SetMaximumDynamicTableCapacity(1024);
+
+  // Set Dynamic Table Capacity instruction.
+  EXPECT_CALL(encoder_stream_sender_delegate_,
+              WriteStreamData(Eq(QuicTextUtils::HexDecode("3e"))));
+  encoder_.SetDynamicTableCapacity(30);
+
+  QpackHeaderTable* header_table = QpackEncoderPeer::header_table(&encoder_);
+
+  EXPECT_EQ(1024u,
+            QpackHeaderTablePeer::maximum_dynamic_table_capacity(header_table));
+  EXPECT_EQ(30u, QpackHeaderTablePeer::dynamic_table_capacity(header_table));
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/qpack/qpack_header_table.cc b/quic/core/qpack/qpack_header_table.cc
index 3c9296d..bb28f72 100644
--- a/quic/core/qpack/qpack_header_table.cc
+++ b/quic/core/qpack/qpack_header_table.cc
@@ -184,7 +184,6 @@
   DCHECK_EQ(0u, maximum_dynamic_table_capacity_);
   DCHECK_EQ(0u, max_entries_);
 
-  dynamic_table_capacity_ = maximum_dynamic_table_capacity;
   maximum_dynamic_table_capacity_ = maximum_dynamic_table_capacity;
   max_entries_ = maximum_dynamic_table_capacity / 32;
 }
diff --git a/quic/core/qpack/qpack_header_table_test.cc b/quic/core/qpack/qpack_header_table_test.cc
index 592d2cb..00f36e5 100644
--- a/quic/core/qpack/qpack_header_table_test.cc
+++ b/quic/core/qpack/qpack_header_table_test.cc
@@ -30,6 +30,7 @@
   QpackHeaderTableTest() {
     table_.SetMaximumDynamicTableCapacity(
         kMaximumDynamicTableCapacityForTesting);
+    table_.SetDynamicTableCapacity(kMaximumDynamicTableCapacityForTesting);
   }
   ~QpackHeaderTableTest() override = default;
 
@@ -371,6 +372,7 @@
   const uint64_t dynamic_table_capacity = 100;
   QpackHeaderTable table;
   table.SetMaximumDynamicTableCapacity(dynamic_table_capacity);
+  EXPECT_TRUE(table.SetDynamicTableCapacity(dynamic_table_capacity));
 
   // Empty table can take an entry up to its capacity.
   EXPECT_EQ(dynamic_table_capacity,
@@ -455,7 +457,9 @@
 
 TEST_F(QpackHeaderTableTest, DrainingIndex) {
   QpackHeaderTable table;
-  table.SetMaximumDynamicTableCapacity(4 * QpackEntry::Size("foo", "bar"));
+  table.SetMaximumDynamicTableCapacity(kMaximumDynamicTableCapacityForTesting);
+  EXPECT_TRUE(
+      table.SetDynamicTableCapacity(4 * QpackEntry::Size("foo", "bar")));
 
   // Empty table: no draining entry.
   EXPECT_EQ(0u, table.draining_index(0.0));