xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/qpack/qpack_encoder_test.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/quic/core/qpack/qpack_encoder.h"
6 
7 #include <limits>
8 #include <string>
9 
10 #include "absl/strings/escaping.h"
11 #include "absl/strings/str_cat.h"
12 #include "absl/strings/string_view.h"
13 #include "quiche/quic/platform/api/quic_flags.h"
14 #include "quiche/quic/platform/api/quic_test.h"
15 #include "quiche/quic/test_tools/qpack/qpack_encoder_peer.h"
16 #include "quiche/quic/test_tools/qpack/qpack_test_utils.h"
17 
18 using ::testing::_;
19 using ::testing::Eq;
20 using ::testing::Return;
21 using ::testing::StrictMock;
22 
23 namespace quic {
24 namespace test {
25 namespace {
26 
27 // A number larger than kMaxBytesBufferedByStream in
28 // qpack_encoder_stream_sender.cc.  Returning this value from NumBytesBuffered()
29 // will instruct QpackEncoder not to generate any instructions for the encoder
30 // stream.
31 constexpr uint64_t kTooManyBytesBuffered = 1024 * 1024;
32 
33 // Mock QpackEncoder::DecoderStreamErrorDelegate implementation.
34 class MockDecoderStreamErrorDelegate
35     : public QpackEncoder::DecoderStreamErrorDelegate {
36  public:
37   ~MockDecoderStreamErrorDelegate() override = default;
38 
39   MOCK_METHOD(void, OnDecoderStreamError,
40               (QuicErrorCode error_code, absl::string_view error_message),
41               (override));
42 };
43 
44 class QpackEncoderTest : public QuicTestWithParam<bool> {
45  protected:
QpackEncoderTest()46   QpackEncoderTest()
47       : encoder_(&decoder_stream_error_delegate_, HuffmanEncoding()),
48         encoder_stream_sent_byte_count_(0) {
49     encoder_.set_qpack_stream_sender_delegate(&encoder_stream_sender_delegate_);
50     encoder_.SetMaximumBlockedStreams(1);
51   }
52 
53   ~QpackEncoderTest() override = default;
54 
DisableHuffmanEncoding()55   bool DisableHuffmanEncoding() { return GetParam(); }
HuffmanEncoding()56   HuffmanEncoding HuffmanEncoding() {
57     return DisableHuffmanEncoding() ? HuffmanEncoding::kDisabled
58                                     : HuffmanEncoding::kEnabled;
59   }
60 
Encode(const spdy::Http2HeaderBlock & header_list)61   std::string Encode(const spdy::Http2HeaderBlock& header_list) {
62     return encoder_.EncodeHeaderList(/* stream_id = */ 1, header_list,
63                                      &encoder_stream_sent_byte_count_);
64   }
65 
66   StrictMock<MockDecoderStreamErrorDelegate> decoder_stream_error_delegate_;
67   StrictMock<MockQpackStreamSenderDelegate> encoder_stream_sender_delegate_;
68   QpackEncoder encoder_;
69   QuicByteCount encoder_stream_sent_byte_count_;
70 };
71 
72 INSTANTIATE_TEST_SUITE_P(DisableHuffmanEncoding, QpackEncoderTest,
73                          testing::Values(false, true));
74 
TEST_P(QpackEncoderTest,Empty)75 TEST_P(QpackEncoderTest, Empty) {
76   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
77       .WillRepeatedly(Return(0));
78   spdy::Http2HeaderBlock header_list;
79   std::string output = Encode(header_list);
80 
81   std::string expected_output;
82   ASSERT_TRUE(absl::HexStringToBytes("0000", &expected_output));
83   EXPECT_EQ(expected_output, output);
84 }
85 
TEST_P(QpackEncoderTest,EmptyName)86 TEST_P(QpackEncoderTest, EmptyName) {
87   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
88       .WillRepeatedly(Return(0));
89   spdy::Http2HeaderBlock header_list;
90   header_list[""] = "foo";
91   std::string output = Encode(header_list);
92 
93   std::string expected_output;
94   if (DisableHuffmanEncoding()) {
95     ASSERT_TRUE(absl::HexStringToBytes("00002003666f6f", &expected_output));
96   } else {
97     ASSERT_TRUE(absl::HexStringToBytes("0000208294e7", &expected_output));
98   }
99   EXPECT_EQ(expected_output, output);
100 }
101 
TEST_P(QpackEncoderTest,EmptyValue)102 TEST_P(QpackEncoderTest, EmptyValue) {
103   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
104       .WillRepeatedly(Return(0));
105   spdy::Http2HeaderBlock header_list;
106   header_list["foo"] = "";
107   std::string output = Encode(header_list);
108 
109   std::string expected_output;
110   if (DisableHuffmanEncoding()) {
111     ASSERT_TRUE(absl::HexStringToBytes("000023666f6f00", &expected_output));
112   } else {
113     ASSERT_TRUE(absl::HexStringToBytes("00002a94e700", &expected_output));
114   }
115   EXPECT_EQ(expected_output, output);
116 }
117 
TEST_P(QpackEncoderTest,EmptyNameAndValue)118 TEST_P(QpackEncoderTest, EmptyNameAndValue) {
119   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
120       .WillRepeatedly(Return(0));
121   spdy::Http2HeaderBlock header_list;
122   header_list[""] = "";
123   std::string output = Encode(header_list);
124 
125   std::string expected_output;
126   ASSERT_TRUE(absl::HexStringToBytes("00002000", &expected_output));
127   EXPECT_EQ(expected_output, output);
128 }
129 
TEST_P(QpackEncoderTest,Simple)130 TEST_P(QpackEncoderTest, Simple) {
131   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
132       .WillRepeatedly(Return(0));
133   spdy::Http2HeaderBlock header_list;
134   header_list["foo"] = "bar";
135   std::string output = Encode(header_list);
136 
137   std::string expected_output;
138   if (DisableHuffmanEncoding()) {
139     ASSERT_TRUE(
140         absl::HexStringToBytes("000023666f6f03626172", &expected_output));
141   } else {
142     ASSERT_TRUE(absl::HexStringToBytes("00002a94e703626172", &expected_output));
143   }
144   EXPECT_EQ(expected_output, output);
145 }
146 
TEST_P(QpackEncoderTest,Multiple)147 TEST_P(QpackEncoderTest, Multiple) {
148   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
149       .WillRepeatedly(Return(0));
150   spdy::Http2HeaderBlock header_list;
151   header_list["foo"] = "bar";
152   // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
153   header_list["ZZZZZZZ"] = std::string(127, 'Z');
154   std::string output = Encode(header_list);
155 
156   std::string expected_output_hex;
157   if (DisableHuffmanEncoding()) {
158     expected_output_hex =
159         "0000"               // prefix
160         "23666f6f03626172";  // foo: bar
161   } else {
162     expected_output_hex =
163         "0000"             // prefix
164         "2a94e703626172";  // foo: bar
165   }
166   expected_output_hex +=
167       "27005a5a5a5a5a5a5a"  // 7 octet long header name, the smallest number
168                             // that does not fit on a 3-bit prefix.
169       "7f005a5a5a5a5a5a5a"  // 127 octet long header value, the smallest
170       "5a5a5a5a5a5a5a5a5a"  // number that does not fit on a 7-bit prefix.
171       "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
172       "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
173       "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
174       "5a5a5a5a5a5a5a5a5a";
175   std::string expected_output;
176   ASSERT_TRUE(absl::HexStringToBytes(expected_output_hex, &expected_output));
177   EXPECT_EQ(expected_output, output);
178 }
179 
TEST_P(QpackEncoderTest,StaticTable)180 TEST_P(QpackEncoderTest, StaticTable) {
181   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
182       .WillRepeatedly(Return(0));
183   {
184     spdy::Http2HeaderBlock header_list;
185     header_list[":method"] = "GET";
186     header_list["accept-encoding"] = "gzip, deflate, br";
187     header_list["location"] = "";
188 
189     std::string output = Encode(header_list);
190     std::string expected_output;
191     ASSERT_TRUE(absl::HexStringToBytes("0000d1dfcc", &expected_output));
192     EXPECT_EQ(expected_output, output);
193   }
194   {
195     spdy::Http2HeaderBlock header_list;
196     header_list[":method"] = "POST";
197     header_list["accept-encoding"] = "compress";
198     header_list["location"] = "foo";
199 
200     std::string output = Encode(header_list);
201     std::string expected_output;
202     if (DisableHuffmanEncoding()) {
203       ASSERT_TRUE(absl::HexStringToBytes(
204           "0000d45f1008636f6d70726573735c03666f6f", &expected_output));
205     } else {
206       ASSERT_TRUE(absl::HexStringToBytes("0000d45f108621e9aec2a11f5c8294e7",
207                                          &expected_output));
208     }
209     EXPECT_EQ(expected_output, output);
210   }
211   {
212     spdy::Http2HeaderBlock header_list;
213     header_list[":method"] = "TRACE";
214     header_list["accept-encoding"] = "";
215 
216     std::string output = Encode(header_list);
217     std::string expected_output;
218     ASSERT_TRUE(
219         absl::HexStringToBytes("00005f000554524143455f1000", &expected_output));
220     EXPECT_EQ(expected_output, output);
221   }
222 }
223 
TEST_P(QpackEncoderTest,DecoderStreamError)224 TEST_P(QpackEncoderTest, DecoderStreamError) {
225   EXPECT_CALL(decoder_stream_error_delegate_,
226               OnDecoderStreamError(QUIC_QPACK_DECODER_STREAM_INTEGER_TOO_LARGE,
227                                    Eq("Encoded integer too large.")));
228 
229   QpackEncoder encoder(&decoder_stream_error_delegate_, HuffmanEncoding());
230   encoder.set_qpack_stream_sender_delegate(&encoder_stream_sender_delegate_);
231   std::string input;
232   ASSERT_TRUE(absl::HexStringToBytes("ffffffffffffffffffffff", &input));
233   encoder.decoder_stream_receiver()->Decode(input);
234 }
235 
TEST_P(QpackEncoderTest,SplitAlongNullCharacter)236 TEST_P(QpackEncoderTest, SplitAlongNullCharacter) {
237   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
238       .WillRepeatedly(Return(0));
239   spdy::Http2HeaderBlock header_list;
240   header_list["foo"] = absl::string_view("bar\0bar\0baz", 11);
241   std::string output = Encode(header_list);
242 
243   std::string expected_output;
244   if (DisableHuffmanEncoding()) {
245     ASSERT_TRUE(
246         absl::HexStringToBytes("0000"               // prefix
247                                "23666f6f03626172"   // foo: bar
248                                "23666f6f03626172"   // foo: bar
249                                "23666f6f0362617a",  // foo: bar
250                                &expected_output));
251   } else {
252     ASSERT_TRUE(
253         absl::HexStringToBytes("0000"             // prefix
254                                "2a94e703626172"   // foo: bar
255                                "2a94e703626172"   // foo: bar
256                                "2a94e70362617a",  // foo: baz
257                                &expected_output));
258   }
259   EXPECT_EQ(expected_output, output);
260 }
261 
TEST_P(QpackEncoderTest,ZeroInsertCountIncrement)262 TEST_P(QpackEncoderTest, ZeroInsertCountIncrement) {
263   // Encoder receives insert count increment with forbidden value 0.
264   EXPECT_CALL(
265       decoder_stream_error_delegate_,
266       OnDecoderStreamError(QUIC_QPACK_DECODER_STREAM_INVALID_ZERO_INCREMENT,
267                            Eq("Invalid increment value 0.")));
268   encoder_.OnInsertCountIncrement(0);
269 }
270 
TEST_P(QpackEncoderTest,TooLargeInsertCountIncrement)271 TEST_P(QpackEncoderTest, TooLargeInsertCountIncrement) {
272   // Encoder receives insert count increment with value that increases Known
273   // Received Count to a value (one) which is larger than the number of dynamic
274   // table insertions sent (zero).
275   EXPECT_CALL(
276       decoder_stream_error_delegate_,
277       OnDecoderStreamError(QUIC_QPACK_DECODER_STREAM_IMPOSSIBLE_INSERT_COUNT,
278                            Eq("Increment value 1 raises known received count "
279                               "to 1 exceeding inserted entry count 0")));
280   encoder_.OnInsertCountIncrement(1);
281 }
282 
283 // Regression test for https://crbug.com/1014372.
TEST_P(QpackEncoderTest,InsertCountIncrementOverflow)284 TEST_P(QpackEncoderTest, InsertCountIncrementOverflow) {
285   QpackEncoderHeaderTable* header_table =
286       QpackEncoderPeer::header_table(&encoder_);
287 
288   // Set dynamic table capacity large enough to hold one entry.
289   header_table->SetMaximumDynamicTableCapacity(4096);
290   header_table->SetDynamicTableCapacity(4096);
291   // Insert one entry into the header table.
292   header_table->InsertEntry("foo", "bar");
293 
294   // Receive Insert Count Increment instruction with increment value 1.
295   encoder_.OnInsertCountIncrement(1);
296 
297   // Receive Insert Count Increment instruction that overflows the known
298   // received count.  This must result in an error instead of a crash.
299   EXPECT_CALL(decoder_stream_error_delegate_,
300               OnDecoderStreamError(
301                   QUIC_QPACK_DECODER_STREAM_INCREMENT_OVERFLOW,
302                   Eq("Insert Count Increment instruction causes overflow.")));
303   encoder_.OnInsertCountIncrement(std::numeric_limits<uint64_t>::max());
304 }
305 
TEST_P(QpackEncoderTest,InvalidHeaderAcknowledgement)306 TEST_P(QpackEncoderTest, InvalidHeaderAcknowledgement) {
307   // Encoder receives header acknowledgement for a stream on which no header
308   // block with dynamic table entries was ever sent.
309   EXPECT_CALL(
310       decoder_stream_error_delegate_,
311       OnDecoderStreamError(QUIC_QPACK_DECODER_STREAM_INCORRECT_ACKNOWLEDGEMENT,
312                            Eq("Header Acknowledgement received for stream 0 "
313                               "with no outstanding header blocks.")));
314   encoder_.OnHeaderAcknowledgement(/* stream_id = */ 0);
315 }
316 
TEST_P(QpackEncoderTest,DynamicTable)317 TEST_P(QpackEncoderTest, DynamicTable) {
318   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
319       .WillRepeatedly(Return(0));
320   encoder_.SetMaximumBlockedStreams(1);
321   encoder_.SetMaximumDynamicTableCapacity(4096);
322   encoder_.SetDynamicTableCapacity(4096);
323 
324   spdy::Http2HeaderBlock header_list;
325   header_list["foo"] = "bar";
326   header_list.AppendValueOrAddHeader("foo",
327                                      "baz");  // name matches dynamic entry
328   header_list["cookie"] = "baz";              // name matches static entry
329 
330   // Set Dynamic Table Capacity instruction.
331   std::string set_dyanamic_table_capacity;
332   ASSERT_TRUE(absl::HexStringToBytes("3fe11f", &set_dyanamic_table_capacity));
333   // Insert three entries into the dynamic table.
334   std::string insert_entries_hex;
335   if (DisableHuffmanEncoding()) {
336     insert_entries_hex =
337         "43"       // insert without name reference
338         "666f6f";  // Huffman-encoded name "foo"
339   } else {
340     insert_entries_hex =
341         "62"     // insert without name reference
342         "94e7";  // Huffman-encoded name "foo"
343   }
344   insert_entries_hex +=
345       "03626172"   // value "bar"
346       "80"         // insert with name reference, dynamic index 0
347       "0362617a"   // value "baz"
348       "c5"         // insert with name reference, static index 5
349       "0362617a";  // value "baz"
350   std::string insert_entries;
351   ASSERT_TRUE(absl::HexStringToBytes(insert_entries_hex, &insert_entries));
352   EXPECT_CALL(encoder_stream_sender_delegate_,
353               WriteStreamData(Eq(
354                   absl::StrCat(set_dyanamic_table_capacity, insert_entries))));
355 
356   std::string expected_output;
357   ASSERT_TRUE(absl::HexStringToBytes(
358       "0400"     // prefix
359       "828180",  // dynamic entries with relative index 0, 1, and 2
360       &expected_output));
361   EXPECT_EQ(expected_output, Encode(header_list));
362 
363   EXPECT_EQ(insert_entries.size(), encoder_stream_sent_byte_count_);
364 }
365 
366 // There is no room in the dynamic table after inserting the first entry.
TEST_P(QpackEncoderTest,SmallDynamicTable)367 TEST_P(QpackEncoderTest, SmallDynamicTable) {
368   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
369       .WillRepeatedly(Return(0));
370   encoder_.SetMaximumBlockedStreams(1);
371   encoder_.SetMaximumDynamicTableCapacity(QpackEntry::Size("foo", "bar"));
372   encoder_.SetDynamicTableCapacity(QpackEntry::Size("foo", "bar"));
373 
374   spdy::Http2HeaderBlock header_list;
375   header_list["foo"] = "bar";
376   header_list.AppendValueOrAddHeader("foo",
377                                      "baz");  // name matches dynamic entry
378   header_list["cookie"] = "baz";              // name matches static entry
379   header_list["bar"] = "baz";                 // no match
380 
381   // Set Dynamic Table Capacity instruction.
382   std::string set_dyanamic_table_capacity;
383   ASSERT_TRUE(absl::HexStringToBytes("3f07", &set_dyanamic_table_capacity));
384   // Insert one entry into the dynamic table.
385   std::string insert_entry;
386   if (DisableHuffmanEncoding()) {
387     ASSERT_TRUE(
388         absl::HexStringToBytes("43"         // insert without name reference
389                                "666f6f"     // Huffman-encoded name "foo"
390                                "03626172",  // value "bar"
391                                &insert_entry));
392   } else {
393     ASSERT_TRUE(
394         absl::HexStringToBytes("62"         // insert without name reference
395                                "94e7"       // Huffman-encoded name "foo"
396                                "03626172",  // value "bar"
397                                &insert_entry));
398   }
399   EXPECT_CALL(encoder_stream_sender_delegate_,
400               WriteStreamData(
401                   Eq(absl::StrCat(set_dyanamic_table_capacity, insert_entry))));
402 
403   std::string expected_output;
404   ASSERT_TRUE(
405       absl::HexStringToBytes("0200"       // prefix
406                              "80"         // dynamic entry 0
407                              "40"         // reference to dynamic entry 0 name
408                              "0362617a"   // with literal value "baz"
409                              "55"         // reference to static entry 5 name
410                              "0362617a"   // with literal value "baz"
411                              "23626172"   // literal name "bar"
412                              "0362617a",  // with literal value "baz"
413                              &expected_output));
414   EXPECT_EQ(expected_output, Encode(header_list));
415 
416   EXPECT_EQ(insert_entry.size(), encoder_stream_sent_byte_count_);
417 }
418 
TEST_P(QpackEncoderTest,BlockedStream)419 TEST_P(QpackEncoderTest, BlockedStream) {
420   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
421       .WillRepeatedly(Return(0));
422   encoder_.SetMaximumBlockedStreams(1);
423   encoder_.SetMaximumDynamicTableCapacity(4096);
424   encoder_.SetDynamicTableCapacity(4096);
425 
426   spdy::Http2HeaderBlock header_list1;
427   header_list1["foo"] = "bar";
428 
429   // Set Dynamic Table Capacity instruction.
430   std::string set_dyanamic_table_capacity;
431   ASSERT_TRUE(absl::HexStringToBytes("3fe11f", &set_dyanamic_table_capacity));
432   // Insert one entry into the dynamic table.
433   std::string insert_entry1;
434   if (DisableHuffmanEncoding()) {
435     ASSERT_TRUE(
436         absl::HexStringToBytes("43"         // insert without name reference
437                                "666f6f"     // Huffman-encoded name "foo"
438                                "03626172",  // value "bar"
439                                &insert_entry1));
440   } else {
441     ASSERT_TRUE(
442         absl::HexStringToBytes("62"         // insert without name reference
443                                "94e7"       // Huffman-encoded name "foo"
444                                "03626172",  // value "bar"
445                                &insert_entry1));
446   }
447   EXPECT_CALL(encoder_stream_sender_delegate_,
448               WriteStreamData(Eq(
449                   absl::StrCat(set_dyanamic_table_capacity, insert_entry1))));
450 
451   std::string expected_output;
452   ASSERT_TRUE(
453       absl::HexStringToBytes("0200"  // prefix
454                              "80",   // dynamic entry 0
455                              &expected_output));
456   EXPECT_EQ(expected_output,
457             encoder_.EncodeHeaderList(/* stream_id = */ 1, header_list1,
458                                       &encoder_stream_sent_byte_count_));
459   EXPECT_EQ(insert_entry1.size(), encoder_stream_sent_byte_count_);
460 
461   // Stream 1 is blocked.  Stream 2 is not allowed to block.
462   spdy::Http2HeaderBlock header_list2;
463   header_list2["foo"] = "bar";  // name and value match dynamic entry
464   header_list2.AppendValueOrAddHeader("foo",
465                                       "baz");  // name matches dynamic entry
466   header_list2["cookie"] = "baz";              // name matches static entry
467   header_list2["bar"] = "baz";                 // no match
468 
469   std::string entries;
470   if (DisableHuffmanEncoding()) {
471     ASSERT_TRUE(
472         absl::HexStringToBytes("0000"       // prefix
473                                "23666f6f"   // literal name "foo"
474                                "03626172"   // with literal value "bar"
475                                "23666f6f"   // literal name "foo"
476                                "0362617a"   // with literal value "baz"
477                                "55"         // name of static entry 5
478                                "0362617a"   // with literal value "baz"
479                                "23626172"   // literal name "bar"
480                                "0362617a",  // with literal value "baz"
481                                &entries));
482   } else {
483     ASSERT_TRUE(
484         absl::HexStringToBytes("0000"       // prefix
485                                "2a94e7"     // literal name "foo"
486                                "03626172"   // with literal value "bar"
487                                "2a94e7"     // literal name "foo"
488                                "0362617a"   // with literal value "baz"
489                                "55"         // name of static entry 5
490                                "0362617a"   // with literal value "baz"
491                                "23626172"   // literal name "bar"
492                                "0362617a",  // with literal value "baz"
493                                &entries));
494   }
495   EXPECT_EQ(entries,
496             encoder_.EncodeHeaderList(/* stream_id = */ 2, header_list2,
497                                       &encoder_stream_sent_byte_count_));
498   EXPECT_EQ(0u, encoder_stream_sent_byte_count_);
499 
500   // Peer acknowledges receipt of one dynamic table entry.
501   // Stream 1 is no longer blocked.
502   encoder_.OnInsertCountIncrement(1);
503 
504   // Insert three entries into the dynamic table.
505   std::string insert_entries;
506   ASSERT_TRUE(absl::HexStringToBytes(
507       "80"         // insert with name reference, dynamic index 0
508       "0362617a"   // value "baz"
509       "c5"         // insert with name reference, static index 5
510       "0362617a"   // value "baz"
511       "43"         // insert without name reference
512       "626172"     // name "bar"
513       "0362617a",  // value "baz"
514       &insert_entries));
515   EXPECT_CALL(encoder_stream_sender_delegate_,
516               WriteStreamData(Eq(insert_entries)));
517 
518   ASSERT_TRUE(
519       absl::HexStringToBytes("0500"       // prefix
520                              "83828180",  // dynamic entries
521                              &expected_output));
522   EXPECT_EQ(expected_output,
523             encoder_.EncodeHeaderList(/* stream_id = */ 3, header_list2,
524                                       &encoder_stream_sent_byte_count_));
525   EXPECT_EQ(insert_entries.size(), encoder_stream_sent_byte_count_);
526 
527   // Stream 3 is blocked.  Stream 4 is not allowed to block, but it can
528   // reference already acknowledged dynamic entry 0.
529   std::string expected2;
530   if (DisableHuffmanEncoding()) {
531     ASSERT_TRUE(
532         absl::HexStringToBytes("0200"            // prefix
533                                "80"              // dynamic entry 0
534                                "23666f6f"        // literal name "foo"
535                                "0362617a"        // with literal value "baz"
536                                "26636f6f6b6965"  // literal name "cookie"
537                                "0362617a"        // with literal value "baz"
538                                "23626172"        // literal name "bar"
539                                "0362617a",       // with literal value "baz"
540                                &expected2));
541   } else {
542     ASSERT_TRUE(
543         absl::HexStringToBytes("0200"        // prefix
544                                "80"          // dynamic entry 0
545                                "2a94e7"      // literal name "foo"
546                                "0362617a"    // with literal value "baz"
547                                "2c21cfd4c5"  // literal name "cookie"
548                                "0362617a"    // with literal value "baz"
549                                "23626172"    // literal name "bar"
550                                "0362617a",   // with literal value "baz"
551                                &expected2));
552   }
553   EXPECT_EQ(expected2,
554             encoder_.EncodeHeaderList(/* stream_id = */ 4, header_list2,
555                                       &encoder_stream_sent_byte_count_));
556   EXPECT_EQ(0u, encoder_stream_sent_byte_count_);
557 
558   // Peer acknowledges receipt of two more dynamic table entries.
559   // Stream 3 is still blocked.
560   encoder_.OnInsertCountIncrement(2);
561 
562   // Stream 5 is not allowed to block, but it can reference already acknowledged
563   // dynamic entries 0, 1, and 2.
564   std::string expected3;
565   ASSERT_TRUE(
566       absl::HexStringToBytes("0400"       // prefix
567                              "828180"     // dynamic entries
568                              "23626172"   // literal name "bar"
569                              "0362617a",  // with literal value "baz"
570                              &expected3));
571   EXPECT_EQ(expected3,
572             encoder_.EncodeHeaderList(/* stream_id = */ 5, header_list2,
573                                       &encoder_stream_sent_byte_count_));
574   EXPECT_EQ(0u, encoder_stream_sent_byte_count_);
575 
576   // Peer acknowledges decoding header block on stream 3.
577   // Stream 3 is not blocked any longer.
578   encoder_.OnHeaderAcknowledgement(3);
579 
580   std::string expected4;
581   ASSERT_TRUE(
582       absl::HexStringToBytes("0500"       // prefix
583                              "83828180",  // dynamic entries
584                              &expected4));
585   EXPECT_EQ(expected4,
586             encoder_.EncodeHeaderList(/* stream_id = */ 6, header_list2,
587                                       &encoder_stream_sent_byte_count_));
588   EXPECT_EQ(0u, encoder_stream_sent_byte_count_);
589 }
590 
TEST_P(QpackEncoderTest,Draining)591 TEST_P(QpackEncoderTest, Draining) {
592   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
593       .WillRepeatedly(Return(0));
594   spdy::Http2HeaderBlock header_list1;
595   header_list1["one"] = "foo";
596   header_list1["two"] = "foo";
597   header_list1["three"] = "foo";
598   header_list1["four"] = "foo";
599   header_list1["five"] = "foo";
600   header_list1["six"] = "foo";
601   header_list1["seven"] = "foo";
602   header_list1["eight"] = "foo";
603   header_list1["nine"] = "foo";
604   header_list1["ten"] = "foo";
605 
606   // Make just enough room in the dynamic table for the header list plus the
607   // first entry duplicated.  This will ensure that the oldest entries are
608   // draining.
609   uint64_t maximum_dynamic_table_capacity = 0;
610   for (const auto& header_field : header_list1) {
611     maximum_dynamic_table_capacity +=
612         QpackEntry::Size(header_field.first, header_field.second);
613   }
614   maximum_dynamic_table_capacity += QpackEntry::Size("one", "foo");
615   encoder_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity);
616   encoder_.SetDynamicTableCapacity(maximum_dynamic_table_capacity);
617 
618   // Set Dynamic Table Capacity instruction and insert ten entries into the
619   // dynamic table.
620   EXPECT_CALL(encoder_stream_sender_delegate_, WriteStreamData(_));
621 
622   std::string expected_output;
623   ASSERT_TRUE(
624       absl::HexStringToBytes("0b00"                   // prefix
625                              "89888786858483828180",  // dynamic entries
626                              &expected_output));
627   EXPECT_EQ(expected_output, Encode(header_list1));
628 
629   // Entry is identical to oldest one, which is draining.  It will be
630   // duplicated and referenced.
631   spdy::Http2HeaderBlock header_list2;
632   header_list2["one"] = "foo";
633 
634   // Duplicate oldest entry.
635   ASSERT_TRUE(absl::HexStringToBytes("09", &expected_output));
636   EXPECT_CALL(encoder_stream_sender_delegate_,
637               WriteStreamData(Eq(expected_output)));
638 
639   ASSERT_TRUE(
640       absl::HexStringToBytes("0c00"  // prefix
641                              "80",   // most recent dynamic table entry
642                              &expected_output));
643   EXPECT_EQ(expected_output, Encode(header_list2));
644 
645   spdy::Http2HeaderBlock header_list3;
646   // Entry is identical to second oldest one, which is draining.  There is no
647   // room to duplicate, it will be encoded with string literals.
648   header_list3.AppendValueOrAddHeader("two", "foo");
649   // Entry has name identical to second oldest one, which is draining.  There is
650   // no room to insert new entry, it will be encoded with string literals.
651   header_list3.AppendValueOrAddHeader("two", "bar");
652 
653   std::string entries =
654       "0000"       // prefix
655       "2374776f";  // literal name "two"
656   if (DisableHuffmanEncoding()) {
657     entries += "03666f6f";  // literal name "foo"
658   } else {
659     entries += "8294e7";  // literal value "foo"
660   }
661   entries +=
662       "2374776f"   // literal name "two"
663       "03626172";  // literal value "bar"
664   ASSERT_TRUE(absl::HexStringToBytes(entries, &expected_output));
665   EXPECT_EQ(expected_output, Encode(header_list3));
666 }
667 
TEST_P(QpackEncoderTest,DynamicTableCapacityLessThanMaximum)668 TEST_P(QpackEncoderTest, DynamicTableCapacityLessThanMaximum) {
669   encoder_.SetMaximumDynamicTableCapacity(1024);
670   encoder_.SetDynamicTableCapacity(30);
671 
672   QpackEncoderHeaderTable* header_table =
673       QpackEncoderPeer::header_table(&encoder_);
674 
675   EXPECT_EQ(1024u, header_table->maximum_dynamic_table_capacity());
676   EXPECT_EQ(30u, header_table->dynamic_table_capacity());
677 }
678 
TEST_P(QpackEncoderTest,EncoderStreamWritesDisallowedThenAllowed)679 TEST_P(QpackEncoderTest, EncoderStreamWritesDisallowedThenAllowed) {
680   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
681       .WillRepeatedly(Return(kTooManyBytesBuffered));
682   encoder_.SetMaximumBlockedStreams(1);
683   encoder_.SetMaximumDynamicTableCapacity(4096);
684   encoder_.SetDynamicTableCapacity(4096);
685 
686   spdy::Http2HeaderBlock header_list1;
687   header_list1["foo"] = "bar";
688   header_list1.AppendValueOrAddHeader("foo", "baz");
689   header_list1["cookie"] = "baz";  // name matches static entry
690 
691   // Encoder is not allowed to write on the encoder stream.
692   // No Set Dynamic Table Capacity or Insert instructions are sent.
693   // Headers are encoded as string literals.
694   std::string entries;
695   if (DisableHuffmanEncoding()) {
696     ASSERT_TRUE(
697         absl::HexStringToBytes("0000"       // prefix
698                                "23666f6f"   // literal name "foo"
699                                "03626172"   // with literal value "bar"
700                                "23666f6f"   // literal name "foo"
701                                "0362617a"   // with literal value "baz"
702                                "55"         // name of static entry 5
703                                "0362617a",  // with literal value "baz"
704                                &entries));
705   } else {
706     ASSERT_TRUE(
707         absl::HexStringToBytes("0000"       // prefix
708                                "2a94e7"     // literal name "foo"
709                                "03626172"   // with literal value "bar"
710                                "2a94e7"     // literal name "foo"
711                                "0362617a"   // with literal value "baz"
712                                "55"         // name of static entry 5
713                                "0362617a",  // with literal value "baz"
714                                &entries));
715   }
716   EXPECT_EQ(entries, Encode(header_list1));
717 
718   EXPECT_EQ(0u, encoder_stream_sent_byte_count_);
719 
720   // If number of bytes buffered by encoder stream goes under the threshold,
721   // then QpackEncoder will resume emitting encoder stream instructions.
722   ::testing::Mock::VerifyAndClearExpectations(&encoder_stream_sender_delegate_);
723   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
724       .WillRepeatedly(Return(0));
725 
726   spdy::Http2HeaderBlock header_list2;
727   header_list2["foo"] = "bar";
728   header_list2.AppendValueOrAddHeader("foo",
729                                       "baz");  // name matches dynamic entry
730   header_list2["cookie"] = "baz";              // name matches static entry
731 
732   // Set Dynamic Table Capacity instruction.
733   std::string set_dyanamic_table_capacity;
734   ASSERT_TRUE(absl::HexStringToBytes("3fe11f", &set_dyanamic_table_capacity));
735   // Insert three entries into the dynamic table.
736   std::string insert_entries_hex;
737   if (DisableHuffmanEncoding()) {
738     insert_entries_hex =
739         "43"       // insert without name reference
740         "666f6f";  // name "foo"
741   } else {
742     insert_entries_hex =
743         "62"     // insert without name reference
744         "94e7";  // Huffman-encoded name "foo"
745   }
746   insert_entries_hex +=
747       "03626172"   // value "bar"
748       "80"         // insert with name reference, dynamic index 0
749       "0362617a"   // value "baz"
750       "c5"         // insert with name reference, static index 5
751       "0362617a";  // value "baz"
752   std::string insert_entries;
753   ASSERT_TRUE(absl::HexStringToBytes(insert_entries_hex, &insert_entries));
754   EXPECT_CALL(encoder_stream_sender_delegate_,
755               WriteStreamData(Eq(
756                   absl::StrCat(set_dyanamic_table_capacity, insert_entries))));
757 
758   std::string expected_output;
759   ASSERT_TRUE(absl::HexStringToBytes(
760       "0400"     // prefix
761       "828180",  // dynamic entries with relative index 0, 1, and 2
762       &expected_output));
763   EXPECT_EQ(expected_output, Encode(header_list2));
764 
765   EXPECT_EQ(insert_entries.size(), encoder_stream_sent_byte_count_);
766 }
767 
TEST_P(QpackEncoderTest,EncoderStreamWritesAllowedThenDisallowed)768 TEST_P(QpackEncoderTest, EncoderStreamWritesAllowedThenDisallowed) {
769   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
770       .WillRepeatedly(Return(0));
771   encoder_.SetMaximumBlockedStreams(1);
772   encoder_.SetMaximumDynamicTableCapacity(4096);
773   encoder_.SetDynamicTableCapacity(4096);
774 
775   spdy::Http2HeaderBlock header_list1;
776   header_list1["foo"] = "bar";
777   header_list1.AppendValueOrAddHeader("foo",
778                                       "baz");  // name matches dynamic entry
779   header_list1["cookie"] = "baz";              // name matches static entry
780 
781   // Set Dynamic Table Capacity instruction.
782   std::string set_dyanamic_table_capacity;
783   ASSERT_TRUE(absl::HexStringToBytes("3fe11f", &set_dyanamic_table_capacity));
784   // Insert three entries into the dynamic table.
785   std::string insert_entries_hex;
786   if (DisableHuffmanEncoding()) {
787     insert_entries_hex =
788         "43"       // insert without name reference
789         "666f6f";  // name "foo"
790   } else {
791     insert_entries_hex =
792         "62"     // insert without name reference
793         "94e7";  // Huffman-encoded name "foo"
794   }
795   insert_entries_hex +=
796       "03626172"   // value "bar"
797       "80"         // insert with name reference, dynamic index 0
798       "0362617a"   // value "baz"
799       "c5"         // insert with name reference, static index 5
800       "0362617a";  // value "baz"
801   std::string insert_entries;
802   ASSERT_TRUE(absl::HexStringToBytes(insert_entries_hex, &insert_entries));
803   EXPECT_CALL(encoder_stream_sender_delegate_,
804               WriteStreamData(Eq(
805                   absl::StrCat(set_dyanamic_table_capacity, insert_entries))));
806 
807   std::string expected_output;
808   ASSERT_TRUE(absl::HexStringToBytes(
809       "0400"     // prefix
810       "828180",  // dynamic entries with relative index 0, 1, and 2
811       &expected_output));
812   EXPECT_EQ(expected_output, Encode(header_list1));
813 
814   EXPECT_EQ(insert_entries.size(), encoder_stream_sent_byte_count_);
815 
816   // If number of bytes buffered by encoder stream goes over the threshold,
817   // then QpackEncoder will stop emitting encoder stream instructions.
818   ::testing::Mock::VerifyAndClearExpectations(&encoder_stream_sender_delegate_);
819   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
820       .WillRepeatedly(Return(kTooManyBytesBuffered));
821 
822   spdy::Http2HeaderBlock header_list2;
823   header_list2["foo"] = "bar";  // matches previously inserted dynamic entry
824   header_list2["bar"] = "baz";
825   header_list2["cookie"] = "baz";  // name matches static entry
826 
827   // Encoder is not allowed to write on the encoder stream.
828   // No Set Dynamic Table Capacity or Insert instructions are sent.
829   // Headers are encoded as string literals.
830   ASSERT_TRUE(
831       absl::HexStringToBytes("0400"      // prefix
832                              "82"        // dynamic entry with relative index 0
833                              "23626172"  // literal name "bar"
834                              "0362617a"  // with literal value "baz"
835                              "80",       // dynamic entry with relative index 2
836                              &expected_output));
837   EXPECT_EQ(expected_output, Encode(header_list2));
838 
839   EXPECT_EQ(0u, encoder_stream_sent_byte_count_);
840 }
841 
842 // Regression test for https://crbug.com/1441880.
TEST_P(QpackEncoderTest,UnackedEntryCannotBeEvicted)843 TEST_P(QpackEncoderTest, UnackedEntryCannotBeEvicted) {
844   EXPECT_CALL(encoder_stream_sender_delegate_, NumBytesBuffered())
845       .WillRepeatedly(Return(0));
846   encoder_.SetMaximumBlockedStreams(2);
847   // With 32 byte overhead per entry, only one entry fits in the dynamic table.
848   encoder_.SetMaximumDynamicTableCapacity(40);
849   encoder_.SetDynamicTableCapacity(40);
850 
851   QpackEncoderHeaderTable* header_table =
852       QpackEncoderPeer::header_table(&encoder_);
853   EXPECT_EQ(0u, header_table->inserted_entry_count());
854   EXPECT_EQ(0u, header_table->dropped_entry_count());
855 
856   spdy::Http2HeaderBlock header_list1;
857   header_list1["foo"] = "bar";
858 
859   // Set Dynamic Table Capacity instruction.
860   std::string set_dyanamic_table_capacity;
861   ASSERT_TRUE(absl::HexStringToBytes("3f09", &set_dyanamic_table_capacity));
862   // Insert one entry into the dynamic table.
863   std::string insert_entries1;
864   if (DisableHuffmanEncoding()) {
865     ASSERT_TRUE(
866         absl::HexStringToBytes("43"         // insert without name reference
867                                "666f6f"     // Huffman-encoded name "foo"
868                                "03626172",  // value "bar"
869                                &insert_entries1));
870   } else {
871     ASSERT_TRUE(
872         absl::HexStringToBytes("62"         // insert without name reference
873                                "94e7"       // Huffman-encoded name "foo"
874                                "03626172",  // value "bar"
875                                &insert_entries1));
876   }
877   EXPECT_CALL(encoder_stream_sender_delegate_,
878               WriteStreamData(Eq(
879                   absl::StrCat(set_dyanamic_table_capacity, insert_entries1))));
880 
881   std::string expected_output;
882   ASSERT_TRUE(
883       absl::HexStringToBytes("0200"  // prefix
884                              "80",   // dynamic entry with relative index 0
885                              &expected_output));
886   EXPECT_EQ(expected_output,
887             encoder_.EncodeHeaderList(/* stream_id = */ 1, header_list1,
888                                       &encoder_stream_sent_byte_count_));
889 
890   EXPECT_EQ(1u, header_table->inserted_entry_count());
891   EXPECT_EQ(0u, header_table->dropped_entry_count());
892 
893   encoder_.OnStreamCancellation(/* stream_id = */ 1);
894 
895   // At this point, entry 0 has no references to it, because stream 1 is
896   // cancelled.  However, this entry is unacknowledged, therefore it must not be
897   // evicted according to RFC 9204 Section 2.1.1.
898 
899   spdy::Http2HeaderBlock header_list2;
900   header_list2["bar"] = "baz";
901 
902   ASSERT_TRUE(
903       absl::HexStringToBytes("0000"       // prefix
904                              "23626172"   // literal name "bar"
905                              "0362617a",  // literal value "baz"
906                              &expected_output));
907   EXPECT_EQ(expected_output,
908             encoder_.EncodeHeaderList(/* stream_id = */ 2, header_list2,
909                                       &encoder_stream_sent_byte_count_));
910 
911   EXPECT_EQ(1u, header_table->inserted_entry_count());
912   EXPECT_EQ(0u, header_table->dropped_entry_count());
913 }
914 
915 }  // namespace
916 }  // namespace test
917 }  // namespace quic
918