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