1 // Copyright 2017 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 "net/third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.h"
6
7 // Tests of HpackDecoderAdapter.
8
9 #include <stdint.h>
10
11 #include <string>
12 #include <tuple>
13 #include <utility>
14 #include <vector>
15
16 #include "absl/base/macros.h"
17 #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state.h"
18 #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables.h"
19 #include "net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h"
20 #include "net/third_party/quiche/src/http2/test_tools/http2_random.h"
21 #include "net/third_party/quiche/src/common/platform/api/quiche_test.h"
22 #include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
23 #include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h"
24 #include "net/third_party/quiche/src/spdy/core/hpack/hpack_output_stream.h"
25 #include "net/third_party/quiche/src/spdy/core/recording_headers_handler.h"
26 #include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
27 #include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
28 #include "net/third_party/quiche/src/spdy/platform/api/spdy_string_utils.h"
29
30 using ::http2::HpackEntryType;
31 using ::http2::HpackString;
32 using ::http2::HpackStringPair;
33 using ::http2::test::HpackBlockBuilder;
34 using ::http2::test::HpackDecoderPeer;
35 using ::testing::ElementsAre;
36 using ::testing::Pair;
37
38 namespace http2 {
39 namespace test {
40
41 class HpackDecoderStatePeer {
42 public:
GetDecoderTables(HpackDecoderState * state)43 static HpackDecoderTables* GetDecoderTables(HpackDecoderState* state) {
44 return &state->decoder_tables_;
45 }
46 };
47
48 class HpackDecoderPeer {
49 public:
GetDecoderState(HpackDecoder * decoder)50 static HpackDecoderState* GetDecoderState(HpackDecoder* decoder) {
51 return &decoder->decoder_state_;
52 }
GetDecoderTables(HpackDecoder * decoder)53 static HpackDecoderTables* GetDecoderTables(HpackDecoder* decoder) {
54 return HpackDecoderStatePeer::GetDecoderTables(GetDecoderState(decoder));
55 }
56 };
57
58 } // namespace test
59 } // namespace http2
60
61 namespace spdy {
62 namespace test {
63
64 class HpackDecoderAdapterPeer {
65 public:
HpackDecoderAdapterPeer(HpackDecoderAdapter * decoder)66 explicit HpackDecoderAdapterPeer(HpackDecoderAdapter* decoder)
67 : decoder_(decoder) {}
68
HandleHeaderRepresentation(absl::string_view name,absl::string_view value)69 void HandleHeaderRepresentation(absl::string_view name,
70 absl::string_view value) {
71 decoder_->listener_adapter_.OnHeader(HpackString(name), HpackString(value));
72 }
73
GetDecoderTables()74 http2::HpackDecoderTables* GetDecoderTables() {
75 return HpackDecoderPeer::GetDecoderTables(&decoder_->hpack_decoder_);
76 }
77
GetTableEntry(uint32_t index)78 const HpackStringPair* GetTableEntry(uint32_t index) {
79 return GetDecoderTables()->Lookup(index);
80 }
81
current_header_table_size()82 size_t current_header_table_size() {
83 return GetDecoderTables()->current_header_table_size();
84 }
85
header_table_size_limit()86 size_t header_table_size_limit() {
87 return GetDecoderTables()->header_table_size_limit();
88 }
89
set_header_table_size_limit(size_t size)90 void set_header_table_size_limit(size_t size) {
91 return GetDecoderTables()->DynamicTableSizeUpdate(size);
92 }
93
94 private:
95 HpackDecoderAdapter* decoder_;
96 };
97
98 class HpackEncoderPeer {
99 public:
CookieToCrumbs(const HpackEncoder::Representation & cookie,HpackEncoder::Representations * crumbs_out)100 static void CookieToCrumbs(const HpackEncoder::Representation& cookie,
101 HpackEncoder::Representations* crumbs_out) {
102 HpackEncoder::CookieToCrumbs(cookie, crumbs_out);
103 }
104 };
105
106 namespace {
107
108 const bool kNoCheckDecodedSize = false;
109 const char* kCookieKey = "cookie";
110
111 // Is HandleControlFrameHeadersStart to be called, and with what value?
112 enum StartChoice { START_WITH_HANDLER, START_WITHOUT_HANDLER, NO_START };
113
114 class HpackDecoderAdapterTest
115 : public QuicheTestWithParam<std::tuple<StartChoice, bool>> {
116 protected:
HpackDecoderAdapterTest()117 HpackDecoderAdapterTest() : decoder_(), decoder_peer_(&decoder_) {}
118
SetUp()119 void SetUp() override {
120 std::tie(start_choice_, randomly_split_input_buffer_) = GetParam();
121 }
122
HandleControlFrameHeadersStart()123 void HandleControlFrameHeadersStart() {
124 bytes_passed_in_ = 0;
125 switch (start_choice_) {
126 case START_WITH_HANDLER:
127 decoder_.HandleControlFrameHeadersStart(&handler_);
128 break;
129 case START_WITHOUT_HANDLER:
130 decoder_.HandleControlFrameHeadersStart(nullptr);
131 break;
132 case NO_START:
133 break;
134 }
135 }
136
HandleControlFrameHeadersData(absl::string_view str)137 bool HandleControlFrameHeadersData(absl::string_view str) {
138 SPDY_VLOG(3) << "HandleControlFrameHeadersData:\n" << SpdyHexDump(str);
139 bytes_passed_in_ += str.size();
140 return decoder_.HandleControlFrameHeadersData(str.data(), str.size());
141 }
142
HandleControlFrameHeadersComplete(size_t * size)143 bool HandleControlFrameHeadersComplete(size_t* size) {
144 bool rc = decoder_.HandleControlFrameHeadersComplete(size);
145 if (size != nullptr) {
146 EXPECT_EQ(*size, bytes_passed_in_);
147 }
148 return rc;
149 }
150
DecodeHeaderBlock(absl::string_view str,bool check_decoded_size=true)151 bool DecodeHeaderBlock(absl::string_view str,
152 bool check_decoded_size = true) {
153 // Don't call this again if HandleControlFrameHeadersData failed previously.
154 EXPECT_FALSE(decode_has_failed_);
155 HandleControlFrameHeadersStart();
156 if (randomly_split_input_buffer_) {
157 do {
158 // Decode some fragment of the remaining bytes.
159 size_t bytes = str.size();
160 if (!str.empty()) {
161 bytes = random_.Uniform(str.size()) + 1;
162 }
163 EXPECT_LE(bytes, str.size());
164 if (!HandleControlFrameHeadersData(str.substr(0, bytes))) {
165 decode_has_failed_ = true;
166 return false;
167 }
168 str.remove_prefix(bytes);
169 } while (!str.empty());
170 } else if (!HandleControlFrameHeadersData(str)) {
171 decode_has_failed_ = true;
172 return false;
173 }
174 // Want to get out the number of compressed bytes that were decoded,
175 // so pass in a pointer if no handler.
176 size_t total_hpack_bytes = 0;
177 if (start_choice_ == START_WITH_HANDLER) {
178 if (!HandleControlFrameHeadersComplete(nullptr)) {
179 decode_has_failed_ = true;
180 return false;
181 }
182 total_hpack_bytes = handler_.compressed_header_bytes();
183 } else {
184 if (!HandleControlFrameHeadersComplete(&total_hpack_bytes)) {
185 decode_has_failed_ = true;
186 return false;
187 }
188 }
189 EXPECT_EQ(total_hpack_bytes, bytes_passed_in_);
190 if (check_decoded_size && start_choice_ == START_WITH_HANDLER) {
191 EXPECT_EQ(handler_.uncompressed_header_bytes(),
192 SizeOfHeaders(decoded_block()));
193 }
194 return true;
195 }
196
EncodeAndDecodeDynamicTableSizeUpdates(size_t first,size_t second)197 bool EncodeAndDecodeDynamicTableSizeUpdates(size_t first, size_t second) {
198 HpackBlockBuilder hbb;
199 hbb.AppendDynamicTableSizeUpdate(first);
200 if (second != first) {
201 hbb.AppendDynamicTableSizeUpdate(second);
202 }
203 return DecodeHeaderBlock(hbb.buffer());
204 }
205
decoded_block() const206 const SpdyHeaderBlock& decoded_block() const {
207 if (start_choice_ == START_WITH_HANDLER) {
208 return handler_.decoded_block();
209 } else {
210 return decoder_.decoded_block();
211 }
212 }
213
SizeOfHeaders(const SpdyHeaderBlock & headers)214 static size_t SizeOfHeaders(const SpdyHeaderBlock& headers) {
215 size_t size = 0;
216 for (const auto& kv : headers) {
217 if (kv.first == kCookieKey) {
218 HpackEncoder::Representations crumbs;
219 HpackEncoderPeer::CookieToCrumbs(kv, &crumbs);
220 for (const auto& crumb : crumbs) {
221 size += crumb.first.size() + crumb.second.size();
222 }
223 } else {
224 size += kv.first.size() + kv.second.size();
225 }
226 }
227 return size;
228 }
229
DecodeBlockExpectingSuccess(absl::string_view str)230 const SpdyHeaderBlock& DecodeBlockExpectingSuccess(absl::string_view str) {
231 EXPECT_TRUE(DecodeHeaderBlock(str));
232 return decoded_block();
233 }
234
expectEntry(size_t index,size_t size,const std::string & name,const std::string & value)235 void expectEntry(size_t index,
236 size_t size,
237 const std::string& name,
238 const std::string& value) {
239 const HpackStringPair* entry = decoder_peer_.GetTableEntry(index);
240 EXPECT_EQ(name, entry->name) << "index " << index;
241 EXPECT_EQ(value, entry->value);
242 EXPECT_EQ(size, entry->size());
243 }
244
MakeHeaderBlock(const std::vector<std::pair<std::string,std::string>> & headers)245 SpdyHeaderBlock MakeHeaderBlock(
246 const std::vector<std::pair<std::string, std::string>>& headers) {
247 SpdyHeaderBlock result;
248 for (const auto& kv : headers) {
249 result.AppendValueOrAddHeader(kv.first, kv.second);
250 }
251 return result;
252 }
253
254 http2::test::Http2Random random_;
255 HpackDecoderAdapter decoder_;
256 test::HpackDecoderAdapterPeer decoder_peer_;
257 RecordingHeadersHandler handler_;
258 StartChoice start_choice_;
259 bool randomly_split_input_buffer_;
260 bool decode_has_failed_ = false;
261 size_t bytes_passed_in_;
262 };
263
264 INSTANTIATE_TEST_SUITE_P(
265 NoHandler,
266 HpackDecoderAdapterTest,
267 ::testing::Combine(::testing::Values(START_WITHOUT_HANDLER, NO_START),
268 ::testing::Bool()));
269
270 INSTANTIATE_TEST_SUITE_P(
271 WithHandler,
272 HpackDecoderAdapterTest,
273 ::testing::Combine(::testing::Values(START_WITH_HANDLER),
274 ::testing::Bool()));
275
TEST_P(HpackDecoderAdapterTest,AddHeaderDataWithHandleControlFrameHeadersData)276 TEST_P(HpackDecoderAdapterTest,
277 AddHeaderDataWithHandleControlFrameHeadersData) {
278 // The hpack decode buffer size is limited in size. This test verifies that
279 // adding encoded data under that limit is accepted, and data that exceeds the
280 // limit is rejected.
281 HandleControlFrameHeadersStart();
282 const size_t kMaxBufferSizeBytes = 50;
283 const std::string a_value = std::string(49, 'x');
284 decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
285 HpackBlockBuilder hbb;
286 hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
287 false, "a", false, a_value);
288 const std::string& s = hbb.buffer();
289 EXPECT_GT(s.size(), kMaxBufferSizeBytes);
290
291 // Any one in input buffer must not exceed kMaxBufferSizeBytes.
292 EXPECT_TRUE(HandleControlFrameHeadersData(s.substr(0, s.size() / 2)));
293 EXPECT_TRUE(HandleControlFrameHeadersData(s.substr(s.size() / 2)));
294
295 EXPECT_FALSE(HandleControlFrameHeadersData(s));
296 SpdyHeaderBlock expected_block = MakeHeaderBlock({{"a", a_value}});
297 EXPECT_EQ(expected_block, decoded_block());
298 }
299
TEST_P(HpackDecoderAdapterTest,NameTooLong)300 TEST_P(HpackDecoderAdapterTest, NameTooLong) {
301 // Verify that a name longer than the allowed size generates an error.
302 const size_t kMaxBufferSizeBytes = 50;
303 const std::string name = std::string(2 * kMaxBufferSizeBytes, 'x');
304 const std::string value = "abc";
305
306 decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
307
308 HpackBlockBuilder hbb;
309 hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
310 false, name, false, value);
311
312 const size_t fragment_size = (3 * kMaxBufferSizeBytes) / 2;
313 const std::string fragment = hbb.buffer().substr(0, fragment_size);
314
315 HandleControlFrameHeadersStart();
316 EXPECT_FALSE(HandleControlFrameHeadersData(fragment));
317 }
318
TEST_P(HpackDecoderAdapterTest,HeaderTooLongToBuffer)319 TEST_P(HpackDecoderAdapterTest, HeaderTooLongToBuffer) {
320 // Verify that a header longer than the allowed size generates an error if
321 // it isn't all in one input buffer.
322 const std::string name = "some-key";
323 const std::string value = "some-value";
324 const size_t kMaxBufferSizeBytes = name.size() + value.size() - 2;
325 decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes);
326
327 HpackBlockBuilder hbb;
328 hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
329 false, name, false, value);
330 const size_t fragment_size = hbb.size() - 1;
331 const std::string fragment = hbb.buffer().substr(0, fragment_size);
332
333 HandleControlFrameHeadersStart();
334 EXPECT_FALSE(HandleControlFrameHeadersData(fragment));
335 }
336
337 // Verify that a header block that exceeds the maximum length is rejected.
TEST_P(HpackDecoderAdapterTest,HeaderBlockTooLong)338 TEST_P(HpackDecoderAdapterTest, HeaderBlockTooLong) {
339 const std::string name = "some-key";
340 const std::string value = "some-value";
341 const size_t kMaxBufferSizeBytes = 1024;
342
343 HpackBlockBuilder hbb;
344 hbb.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader, false,
345 name, false, value);
346 while (hbb.size() < kMaxBufferSizeBytes) {
347 hbb.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader, false,
348 "", false, "");
349 }
350 // With no limit on the maximum header block size, the decoder handles the
351 // entire block successfully.
352 HandleControlFrameHeadersStart();
353 EXPECT_TRUE(HandleControlFrameHeadersData(hbb.buffer()));
354 size_t total_bytes;
355 EXPECT_TRUE(HandleControlFrameHeadersComplete(&total_bytes));
356
357 // When a total byte limit is imposed, the decoder bails before the end of the
358 // block.
359 decoder_.set_max_header_block_bytes(kMaxBufferSizeBytes);
360 HandleControlFrameHeadersStart();
361 EXPECT_FALSE(HandleControlFrameHeadersData(hbb.buffer()));
362 }
363
364 // Decode with incomplete data in buffer.
TEST_P(HpackDecoderAdapterTest,DecodeWithIncompleteData)365 TEST_P(HpackDecoderAdapterTest, DecodeWithIncompleteData) {
366 HandleControlFrameHeadersStart();
367
368 // No need to wait for more data.
369 EXPECT_TRUE(HandleControlFrameHeadersData("\x82\x85\x82"));
370 std::vector<std::pair<std::string, std::string>> expected_headers = {
371 {":method", "GET"}, {":path", "/index.html"}, {":method", "GET"}};
372
373 SpdyHeaderBlock expected_block1 = MakeHeaderBlock(expected_headers);
374 EXPECT_EQ(expected_block1, decoded_block());
375
376 // Full and partial headers, won't add partial to the headers.
377 EXPECT_TRUE(
378 HandleControlFrameHeadersData("\x40\x03goo"
379 "\x03gar\xbe\x40\x04spam"));
380 expected_headers.push_back({"goo", "gar"});
381 expected_headers.push_back({"goo", "gar"});
382
383 SpdyHeaderBlock expected_block2 = MakeHeaderBlock(expected_headers);
384 EXPECT_EQ(expected_block2, decoded_block());
385
386 // Add the needed data.
387 EXPECT_TRUE(HandleControlFrameHeadersData("\x04gggs"));
388
389 size_t size = 0;
390 EXPECT_TRUE(HandleControlFrameHeadersComplete(&size));
391 EXPECT_EQ(24u, size);
392
393 expected_headers.push_back({"spam", "gggs"});
394
395 SpdyHeaderBlock expected_block3 = MakeHeaderBlock(expected_headers);
396 EXPECT_EQ(expected_block3, decoded_block());
397 }
398
TEST_P(HpackDecoderAdapterTest,HandleHeaderRepresentation)399 TEST_P(HpackDecoderAdapterTest, HandleHeaderRepresentation) {
400 // Make sure the decoder is properly initialized.
401 HandleControlFrameHeadersStart();
402 HandleControlFrameHeadersData("");
403
404 // All cookie crumbs are joined.
405 decoder_peer_.HandleHeaderRepresentation("cookie", " part 1");
406 decoder_peer_.HandleHeaderRepresentation("cookie", "part 2 ");
407 decoder_peer_.HandleHeaderRepresentation("cookie", "part3");
408
409 // Already-delimited headers are passed through.
410 decoder_peer_.HandleHeaderRepresentation("passed-through",
411 std::string("foo\0baz", 7));
412
413 // Other headers are joined on \0. Case matters.
414 decoder_peer_.HandleHeaderRepresentation("joined", "joined");
415 decoder_peer_.HandleHeaderRepresentation("joineD", "value 1");
416 decoder_peer_.HandleHeaderRepresentation("joineD", "value 2");
417
418 // Empty headers remain empty.
419 decoder_peer_.HandleHeaderRepresentation("empty", "");
420
421 // Joined empty headers work as expected.
422 decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
423 decoder_peer_.HandleHeaderRepresentation("empty-joined", "foo");
424 decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
425 decoder_peer_.HandleHeaderRepresentation("empty-joined", "");
426
427 // Non-contiguous cookie crumb.
428 decoder_peer_.HandleHeaderRepresentation("cookie", " fin!");
429
430 // Finish and emit all headers.
431 decoder_.HandleControlFrameHeadersComplete(nullptr);
432
433 // Resulting decoded headers are in the same order as the inputs.
434 EXPECT_THAT(
435 decoded_block(),
436 ElementsAre(
437 Pair("cookie", " part 1; part 2 ; part3; fin!"),
438 Pair("passed-through", absl::string_view("foo\0baz", 7)),
439 Pair("joined", absl::string_view("joined\0value 1\0value 2", 22)),
440 Pair("empty", ""),
441 Pair("empty-joined", absl::string_view("\0foo\0\0", 6))));
442 }
443
444 // Decoding indexed static table field should work.
TEST_P(HpackDecoderAdapterTest,IndexedHeaderStatic)445 TEST_P(HpackDecoderAdapterTest, IndexedHeaderStatic) {
446 // Reference static table entries #2 and #5.
447 const SpdyHeaderBlock& header_set1 = DecodeBlockExpectingSuccess("\x82\x85");
448 SpdyHeaderBlock expected_header_set1;
449 expected_header_set1[":method"] = "GET";
450 expected_header_set1[":path"] = "/index.html";
451 EXPECT_EQ(expected_header_set1, header_set1);
452
453 // Reference static table entry #2.
454 const SpdyHeaderBlock& header_set2 = DecodeBlockExpectingSuccess("\x82");
455 SpdyHeaderBlock expected_header_set2;
456 expected_header_set2[":method"] = "GET";
457 EXPECT_EQ(expected_header_set2, header_set2);
458 }
459
TEST_P(HpackDecoderAdapterTest,IndexedHeaderDynamic)460 TEST_P(HpackDecoderAdapterTest, IndexedHeaderDynamic) {
461 // First header block: add an entry to header table.
462 const SpdyHeaderBlock& header_set1 = DecodeBlockExpectingSuccess(
463 "\x40\x03"
464 "foo"
465 "\x03"
466 "bar");
467 SpdyHeaderBlock expected_header_set1;
468 expected_header_set1["foo"] = "bar";
469 EXPECT_EQ(expected_header_set1, header_set1);
470
471 // Second header block: add another entry to header table.
472 const SpdyHeaderBlock& header_set2 = DecodeBlockExpectingSuccess(
473 "\xbe\x40\x04"
474 "spam"
475 "\x04"
476 "eggs");
477 SpdyHeaderBlock expected_header_set2;
478 expected_header_set2["foo"] = "bar";
479 expected_header_set2["spam"] = "eggs";
480 EXPECT_EQ(expected_header_set2, header_set2);
481
482 // Third header block: refer to most recently added entry.
483 const SpdyHeaderBlock& header_set3 = DecodeBlockExpectingSuccess("\xbe");
484 SpdyHeaderBlock expected_header_set3;
485 expected_header_set3["spam"] = "eggs";
486 EXPECT_EQ(expected_header_set3, header_set3);
487 }
488
489 // Test a too-large indexed header.
TEST_P(HpackDecoderAdapterTest,InvalidIndexedHeader)490 TEST_P(HpackDecoderAdapterTest, InvalidIndexedHeader) {
491 // High-bit set, and a prefix of one more than the number of static entries.
492 EXPECT_FALSE(DecodeHeaderBlock("\xbe"));
493 }
494
TEST_P(HpackDecoderAdapterTest,ContextUpdateMaximumSize)495 TEST_P(HpackDecoderAdapterTest, ContextUpdateMaximumSize) {
496 EXPECT_EQ(kDefaultHeaderTableSizeSetting,
497 decoder_peer_.header_table_size_limit());
498 std::string input;
499 {
500 // Maximum-size update with size 126. Succeeds.
501 HpackOutputStream output_stream;
502 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
503 output_stream.AppendUint32(126);
504
505 output_stream.TakeString(&input);
506 EXPECT_TRUE(DecodeHeaderBlock(absl::string_view(input)));
507 EXPECT_EQ(126u, decoder_peer_.header_table_size_limit());
508 }
509 {
510 // Maximum-size update with kDefaultHeaderTableSizeSetting. Succeeds.
511 HpackOutputStream output_stream;
512 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
513 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting);
514
515 output_stream.TakeString(&input);
516 EXPECT_TRUE(DecodeHeaderBlock(absl::string_view(input)));
517 EXPECT_EQ(kDefaultHeaderTableSizeSetting,
518 decoder_peer_.header_table_size_limit());
519 }
520 {
521 // Maximum-size update with kDefaultHeaderTableSizeSetting + 1. Fails.
522 HpackOutputStream output_stream;
523 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
524 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting + 1);
525
526 output_stream.TakeString(&input);
527 EXPECT_FALSE(DecodeHeaderBlock(absl::string_view(input)));
528 EXPECT_EQ(kDefaultHeaderTableSizeSetting,
529 decoder_peer_.header_table_size_limit());
530 }
531 }
532
533 // Two HeaderTableSizeUpdates may appear at the beginning of the block
TEST_P(HpackDecoderAdapterTest,TwoTableSizeUpdates)534 TEST_P(HpackDecoderAdapterTest, TwoTableSizeUpdates) {
535 std::string input;
536 {
537 // Should accept two table size updates, update to second one
538 HpackOutputStream output_stream;
539 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
540 output_stream.AppendUint32(0);
541 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
542 output_stream.AppendUint32(122);
543
544 output_stream.TakeString(&input);
545 EXPECT_TRUE(DecodeHeaderBlock(absl::string_view(input)));
546 EXPECT_EQ(122u, decoder_peer_.header_table_size_limit());
547 }
548 }
549
550 // Three HeaderTableSizeUpdates should result in an error
TEST_P(HpackDecoderAdapterTest,ThreeTableSizeUpdatesError)551 TEST_P(HpackDecoderAdapterTest, ThreeTableSizeUpdatesError) {
552 std::string input;
553 {
554 // Should reject three table size updates, update to second one
555 HpackOutputStream output_stream;
556 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
557 output_stream.AppendUint32(5);
558 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
559 output_stream.AppendUint32(10);
560 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
561 output_stream.AppendUint32(15);
562
563 output_stream.TakeString(&input);
564
565 EXPECT_FALSE(DecodeHeaderBlock(absl::string_view(input)));
566 EXPECT_EQ(10u, decoder_peer_.header_table_size_limit());
567 }
568 }
569
570 // HeaderTableSizeUpdates may only appear at the beginning of the block
571 // Any other updates should result in an error
TEST_P(HpackDecoderAdapterTest,TableSizeUpdateSecondError)572 TEST_P(HpackDecoderAdapterTest, TableSizeUpdateSecondError) {
573 std::string input;
574 {
575 // Should reject a table size update appearing after a different entry
576 // The table size should remain as the default
577 HpackOutputStream output_stream;
578 output_stream.AppendBytes("\x82\x85");
579 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
580 output_stream.AppendUint32(123);
581
582 output_stream.TakeString(&input);
583
584 EXPECT_FALSE(DecodeHeaderBlock(absl::string_view(input)));
585 EXPECT_EQ(kDefaultHeaderTableSizeSetting,
586 decoder_peer_.header_table_size_limit());
587 }
588 }
589
590 // HeaderTableSizeUpdates may only appear at the beginning of the block
591 // Any other updates should result in an error
TEST_P(HpackDecoderAdapterTest,TableSizeUpdateFirstThirdError)592 TEST_P(HpackDecoderAdapterTest, TableSizeUpdateFirstThirdError) {
593 std::string input;
594 {
595 // Should reject the second table size update
596 // if a different entry appears after the first update
597 // The table size should update to the first but not the second
598 HpackOutputStream output_stream;
599 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
600 output_stream.AppendUint32(60);
601 output_stream.AppendBytes("\x82\x85");
602 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode);
603 output_stream.AppendUint32(125);
604
605 output_stream.TakeString(&input);
606
607 EXPECT_FALSE(DecodeHeaderBlock(absl::string_view(input)));
608 EXPECT_EQ(60u, decoder_peer_.header_table_size_limit());
609 }
610 }
611
612 // Decoding two valid encoded literal headers with no indexing should
613 // work.
TEST_P(HpackDecoderAdapterTest,LiteralHeaderNoIndexing)614 TEST_P(HpackDecoderAdapterTest, LiteralHeaderNoIndexing) {
615 // First header with indexed name, second header with string literal
616 // name.
617 const char input[] = "\x04\x0c/sample/path\x00\x06:path2\x0e/sample/path/2";
618 const SpdyHeaderBlock& header_set = DecodeBlockExpectingSuccess(
619 absl::string_view(input, ABSL_ARRAYSIZE(input) - 1));
620
621 SpdyHeaderBlock expected_header_set;
622 expected_header_set[":path"] = "/sample/path";
623 expected_header_set[":path2"] = "/sample/path/2";
624 EXPECT_EQ(expected_header_set, header_set);
625 }
626
627 // Decoding two valid encoded literal headers with incremental
628 // indexing and string literal names should work.
TEST_P(HpackDecoderAdapterTest,LiteralHeaderIncrementalIndexing)629 TEST_P(HpackDecoderAdapterTest, LiteralHeaderIncrementalIndexing) {
630 const char input[] = "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2";
631 const SpdyHeaderBlock& header_set = DecodeBlockExpectingSuccess(
632 absl::string_view(input, ABSL_ARRAYSIZE(input) - 1));
633
634 SpdyHeaderBlock expected_header_set;
635 expected_header_set[":path"] = "/sample/path";
636 expected_header_set[":path2"] = "/sample/path/2";
637 EXPECT_EQ(expected_header_set, header_set);
638 }
639
TEST_P(HpackDecoderAdapterTest,LiteralHeaderWithIndexingInvalidNameIndex)640 TEST_P(HpackDecoderAdapterTest, LiteralHeaderWithIndexingInvalidNameIndex) {
641 decoder_.ApplyHeaderTableSizeSetting(0);
642 EXPECT_TRUE(EncodeAndDecodeDynamicTableSizeUpdates(0, 0));
643
644 // Name is the last static index. Works.
645 EXPECT_TRUE(DecodeHeaderBlock(absl::string_view("\x7d\x03ooo")));
646 // Name is one beyond the last static index. Fails.
647 EXPECT_FALSE(DecodeHeaderBlock(absl::string_view("\x7e\x03ooo")));
648 }
649
TEST_P(HpackDecoderAdapterTest,LiteralHeaderNoIndexingInvalidNameIndex)650 TEST_P(HpackDecoderAdapterTest, LiteralHeaderNoIndexingInvalidNameIndex) {
651 // Name is the last static index. Works.
652 EXPECT_TRUE(DecodeHeaderBlock(absl::string_view("\x0f\x2e\x03ooo")));
653 // Name is one beyond the last static index. Fails.
654 EXPECT_FALSE(DecodeHeaderBlock(absl::string_view("\x0f\x2f\x03ooo")));
655 }
656
TEST_P(HpackDecoderAdapterTest,LiteralHeaderNeverIndexedInvalidNameIndex)657 TEST_P(HpackDecoderAdapterTest, LiteralHeaderNeverIndexedInvalidNameIndex) {
658 // Name is the last static index. Works.
659 EXPECT_TRUE(DecodeHeaderBlock(absl::string_view("\x1f\x2e\x03ooo")));
660 // Name is one beyond the last static index. Fails.
661 EXPECT_FALSE(DecodeHeaderBlock(absl::string_view("\x1f\x2f\x03ooo")));
662 }
663
TEST_P(HpackDecoderAdapterTest,TruncatedIndex)664 TEST_P(HpackDecoderAdapterTest, TruncatedIndex) {
665 // Indexed Header, varint for index requires multiple bytes,
666 // but only one provided.
667 EXPECT_FALSE(DecodeHeaderBlock("\xff"));
668 }
669
TEST_P(HpackDecoderAdapterTest,TruncatedHuffmanLiteral)670 TEST_P(HpackDecoderAdapterTest, TruncatedHuffmanLiteral) {
671 // Literal value, Huffman encoded, but with the last byte missing (i.e.
672 // drop the final ff shown below).
673 //
674 // 41 | == Literal indexed ==
675 // | Indexed name (idx = 1)
676 // | :authority
677 // 8c | Literal value (len = 12)
678 // | Huffman encoded:
679 // f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k.....
680 // | Decoded:
681 // | www.example.com
682 // | -> :authority: www.example.com
683
684 std::string first = SpdyHexDecode("418cf1e3c2e5f23a6ba0ab90f4ff");
685 EXPECT_TRUE(DecodeHeaderBlock(first));
686 first.pop_back();
687 EXPECT_FALSE(DecodeHeaderBlock(first));
688 }
689
TEST_P(HpackDecoderAdapterTest,HuffmanEOSError)690 TEST_P(HpackDecoderAdapterTest, HuffmanEOSError) {
691 // Literal value, Huffman encoded, but with an additional ff byte at the end
692 // of the string, i.e. an EOS that is longer than permitted.
693 //
694 // 41 | == Literal indexed ==
695 // | Indexed name (idx = 1)
696 // | :authority
697 // 8d | Literal value (len = 13)
698 // | Huffman encoded:
699 // f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k.....
700 // | Decoded:
701 // | www.example.com
702 // | -> :authority: www.example.com
703
704 std::string first = SpdyHexDecode("418cf1e3c2e5f23a6ba0ab90f4ff");
705 EXPECT_TRUE(DecodeHeaderBlock(first));
706 first = SpdyHexDecode("418df1e3c2e5f23a6ba0ab90f4ffff");
707 EXPECT_FALSE(DecodeHeaderBlock(first));
708 }
709
710 // Round-tripping the header set from RFC 7541 C.3.1 should work.
711 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.3.1
TEST_P(HpackDecoderAdapterTest,BasicC31)712 TEST_P(HpackDecoderAdapterTest, BasicC31) {
713 HpackEncoder encoder;
714
715 SpdyHeaderBlock expected_header_set;
716 expected_header_set[":method"] = "GET";
717 expected_header_set[":scheme"] = "http";
718 expected_header_set[":path"] = "/";
719 expected_header_set[":authority"] = "www.example.com";
720
721 std::string encoded_header_set;
722 EXPECT_TRUE(
723 encoder.EncodeHeaderSet(expected_header_set, &encoded_header_set));
724
725 EXPECT_TRUE(DecodeHeaderBlock(encoded_header_set));
726 EXPECT_EQ(expected_header_set, decoded_block());
727 }
728
729 // RFC 7541, Section C.4: Request Examples with Huffman Coding
730 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.4
TEST_P(HpackDecoderAdapterTest,SectionC4RequestHuffmanExamples)731 TEST_P(HpackDecoderAdapterTest, SectionC4RequestHuffmanExamples) {
732 // TODO(jamessynge): Use http2/hpack/tools/hpack_example.h to parse the
733 // example directly, instead of having it as a comment.
734 //
735 // 82 | == Indexed - Add ==
736 // | idx = 2
737 // | -> :method: GET
738 // 86 | == Indexed - Add ==
739 // | idx = 6
740 // | -> :scheme: http
741 // 84 | == Indexed - Add ==
742 // | idx = 4
743 // | -> :path: /
744 // 41 | == Literal indexed ==
745 // | Indexed name (idx = 1)
746 // | :authority
747 // 8c | Literal value (len = 12)
748 // | Huffman encoded:
749 // f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k.....
750 // | Decoded:
751 // | www.example.com
752 // | -> :authority: www.example.com
753 std::string first = SpdyHexDecode("828684418cf1e3c2e5f23a6ba0ab90f4ff");
754 const SpdyHeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first);
755
756 EXPECT_THAT(first_header_set,
757 ElementsAre(
758 // clang-format off
759 Pair(":method", "GET"),
760 Pair(":scheme", "http"),
761 Pair(":path", "/"),
762 Pair(":authority", "www.example.com")));
763 // clang-format on
764
765 expectEntry(62, 57, ":authority", "www.example.com");
766 EXPECT_EQ(57u, decoder_peer_.current_header_table_size());
767
768 // 82 | == Indexed - Add ==
769 // | idx = 2
770 // | -> :method: GET
771 // 86 | == Indexed - Add ==
772 // | idx = 6
773 // | -> :scheme: http
774 // 84 | == Indexed - Add ==
775 // | idx = 4
776 // | -> :path: /
777 // be | == Indexed - Add ==
778 // | idx = 62
779 // | -> :authority: www.example.com
780 // 58 | == Literal indexed ==
781 // | Indexed name (idx = 24)
782 // | cache-control
783 // 86 | Literal value (len = 8)
784 // | Huffman encoded:
785 // a8eb 1064 9cbf | ...d..
786 // | Decoded:
787 // | no-cache
788 // | -> cache-control: no-cache
789
790 std::string second = SpdyHexDecode("828684be5886a8eb10649cbf");
791 const SpdyHeaderBlock& second_header_set =
792 DecodeBlockExpectingSuccess(second);
793
794 EXPECT_THAT(second_header_set,
795 ElementsAre(
796 // clang-format off
797 Pair(":method", "GET"),
798 Pair(":scheme", "http"),
799 Pair(":path", "/"),
800 Pair(":authority", "www.example.com"),
801 Pair("cache-control", "no-cache")));
802 // clang-format on
803
804 expectEntry(62, 53, "cache-control", "no-cache");
805 expectEntry(63, 57, ":authority", "www.example.com");
806 EXPECT_EQ(110u, decoder_peer_.current_header_table_size());
807
808 // 82 | == Indexed - Add ==
809 // | idx = 2
810 // | -> :method: GET
811 // 87 | == Indexed - Add ==
812 // | idx = 7
813 // | -> :scheme: https
814 // 85 | == Indexed - Add ==
815 // | idx = 5
816 // | -> :path: /index.html
817 // bf | == Indexed - Add ==
818 // | idx = 63
819 // | -> :authority: www.example.com
820 // 40 | == Literal indexed ==
821 // 88 | Literal name (len = 10)
822 // | Huffman encoded:
823 // 25a8 49e9 5ba9 7d7f | %.I.[.}.
824 // | Decoded:
825 // | custom-key
826 // 89 | Literal value (len = 12)
827 // | Huffman encoded:
828 // 25a8 49e9 5bb8 e8b4 bf | %.I.[....
829 // | Decoded:
830 // | custom-value
831 // | -> custom-key: custom-value
832 std::string third =
833 SpdyHexDecode("828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf");
834 const SpdyHeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third);
835
836 EXPECT_THAT(
837 third_header_set,
838 ElementsAre(
839 // clang-format off
840 Pair(":method", "GET"),
841 Pair(":scheme", "https"),
842 Pair(":path", "/index.html"),
843 Pair(":authority", "www.example.com"),
844 Pair("custom-key", "custom-value")));
845 // clang-format on
846
847 expectEntry(62, 54, "custom-key", "custom-value");
848 expectEntry(63, 53, "cache-control", "no-cache");
849 expectEntry(64, 57, ":authority", "www.example.com");
850 EXPECT_EQ(164u, decoder_peer_.current_header_table_size());
851 }
852
853 // RFC 7541, Section C.6: Response Examples with Huffman Coding
854 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.6
TEST_P(HpackDecoderAdapterTest,SectionC6ResponseHuffmanExamples)855 TEST_P(HpackDecoderAdapterTest, SectionC6ResponseHuffmanExamples) {
856 // The example is based on a maximum dynamic table size of 256,
857 // which allows for testing dynamic table evictions.
858 decoder_peer_.set_header_table_size_limit(256);
859
860 // 48 | == Literal indexed ==
861 // | Indexed name (idx = 8)
862 // | :status
863 // 82 | Literal value (len = 3)
864 // | Huffman encoded:
865 // 6402 | d.
866 // | Decoded:
867 // | 302
868 // | -> :status: 302
869 // 58 | == Literal indexed ==
870 // | Indexed name (idx = 24)
871 // | cache-control
872 // 85 | Literal value (len = 7)
873 // | Huffman encoded:
874 // aec3 771a 4b | ..w.K
875 // | Decoded:
876 // | private
877 // | -> cache-control: private
878 // 61 | == Literal indexed ==
879 // | Indexed name (idx = 33)
880 // | date
881 // 96 | Literal value (len = 29)
882 // | Huffman encoded:
883 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
884 // e082 a62d 1bff | ...-..
885 // | Decoded:
886 // | Mon, 21 Oct 2013 20:13:21
887 // | GMT
888 // | -> date: Mon, 21 Oct 2013
889 // | 20:13:21 GMT
890 // 6e | == Literal indexed ==
891 // | Indexed name (idx = 46)
892 // | location
893 // 91 | Literal value (len = 23)
894 // | Huffman encoded:
895 // 9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 | .)...c.........C
896 // d3 | .
897 // | Decoded:
898 // | https://www.example.com
899 // | -> location: https://www.e
900 // | xample.com
901
902 std::string first = SpdyHexDecode(
903 "488264025885aec3771a4b6196d07abe"
904 "941054d444a8200595040b8166e082a6"
905 "2d1bff6e919d29ad171863c78f0b97c8"
906 "e9ae82ae43d3");
907 const SpdyHeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first);
908
909 EXPECT_THAT(first_header_set,
910 ElementsAre(
911 // clang-format off
912 Pair(":status", "302"),
913 Pair("cache-control", "private"),
914 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
915 Pair("location", "https://www.example.com")));
916 // clang-format on
917
918 expectEntry(62, 63, "location", "https://www.example.com");
919 expectEntry(63, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT");
920 expectEntry(64, 52, "cache-control", "private");
921 expectEntry(65, 42, ":status", "302");
922 EXPECT_EQ(222u, decoder_peer_.current_header_table_size());
923
924 // 48 | == Literal indexed ==
925 // | Indexed name (idx = 8)
926 // | :status
927 // 83 | Literal value (len = 3)
928 // | Huffman encoded:
929 // 640e ff | d..
930 // | Decoded:
931 // | 307
932 // | - evict: :status: 302
933 // | -> :status: 307
934 // c1 | == Indexed - Add ==
935 // | idx = 65
936 // | -> cache-control: private
937 // c0 | == Indexed - Add ==
938 // | idx = 64
939 // | -> date: Mon, 21 Oct 2013
940 // | 20:13:21 GMT
941 // bf | == Indexed - Add ==
942 // | idx = 63
943 // | -> location:
944 // | https://www.example.com
945 std::string second = SpdyHexDecode("4883640effc1c0bf");
946 const SpdyHeaderBlock& second_header_set =
947 DecodeBlockExpectingSuccess(second);
948
949 EXPECT_THAT(second_header_set,
950 ElementsAre(
951 // clang-format off
952 Pair(":status", "307"),
953 Pair("cache-control", "private"),
954 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
955 Pair("location", "https://www.example.com")));
956 // clang-format on
957
958 expectEntry(62, 42, ":status", "307");
959 expectEntry(63, 63, "location", "https://www.example.com");
960 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT");
961 expectEntry(65, 52, "cache-control", "private");
962 EXPECT_EQ(222u, decoder_peer_.current_header_table_size());
963
964 // 88 | == Indexed - Add ==
965 // | idx = 8
966 // | -> :status: 200
967 // c1 | == Indexed - Add ==
968 // | idx = 65
969 // | -> cache-control: private
970 // 61 | == Literal indexed ==
971 // | Indexed name (idx = 33)
972 // | date
973 // 96 | Literal value (len = 22)
974 // | Huffman encoded:
975 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f
976 // e084 a62d 1bff | ...-..
977 // | Decoded:
978 // | Mon, 21 Oct 2013 20:13:22
979 // | GMT
980 // | - evict: cache-control:
981 // | private
982 // | -> date: Mon, 21 Oct 2013
983 // | 20:13:22 GMT
984 // c0 | == Indexed - Add ==
985 // | idx = 64
986 // | -> location:
987 // | https://www.example.com
988 // 5a | == Literal indexed ==
989 // | Indexed name (idx = 26)
990 // | content-encoding
991 // 83 | Literal value (len = 3)
992 // | Huffman encoded:
993 // 9bd9 ab | ...
994 // | Decoded:
995 // | gzip
996 // | - evict: date: Mon, 21 Oct
997 // | 2013 20:13:21 GMT
998 // | -> content-encoding: gzip
999 // 77 | == Literal indexed ==
1000 // | Indexed name (idx = 55)
1001 // | set-cookie
1002 // ad | Literal value (len = 45)
1003 // | Huffman encoded:
1004 // 94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 | .........5...[9`
1005 // d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 | ..'..6r..'..)...
1006 // 3160 65c0 03ed 4ee5 b106 3d50 07 | 1`e...N...=P.
1007 // | Decoded:
1008 // | foo=ASDJKHQKBZXOQWEOPIUAXQ
1009 // | WEOIU; max-age=3600; versi
1010 // | on=1
1011 // | - evict: location:
1012 // | https://www.example.com
1013 // | - evict: :status: 307
1014 // | -> set-cookie: foo=ASDJKHQ
1015 // | KBZXOQWEOPIUAXQWEOIU;
1016 // | max-age=3600; version=1
1017 std::string third = SpdyHexDecode(
1018 "88c16196d07abe941054d444a8200595"
1019 "040b8166e084a62d1bffc05a839bd9ab"
1020 "77ad94e7821dd7f2e6c7b335dfdfcd5b"
1021 "3960d5af27087f3672c1ab270fb5291f"
1022 "9587316065c003ed4ee5b1063d5007");
1023 const SpdyHeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third);
1024
1025 EXPECT_THAT(third_header_set,
1026 ElementsAre(
1027 // clang-format off
1028 Pair(":status", "200"),
1029 Pair("cache-control", "private"),
1030 Pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
1031 Pair("location", "https://www.example.com"),
1032 Pair("content-encoding", "gzip"),
1033 Pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
1034 " max-age=3600; version=1")));
1035 // clang-format on
1036
1037 expectEntry(62, 98, "set-cookie",
1038 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
1039 " max-age=3600; version=1");
1040 expectEntry(63, 52, "content-encoding", "gzip");
1041 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:22 GMT");
1042 EXPECT_EQ(215u, decoder_peer_.current_header_table_size());
1043 }
1044
1045 // Regression test: Found that entries with dynamic indexed names and literal
1046 // values caused "use after free" MSAN failures if the name was evicted as it
1047 // was being re-used.
TEST_P(HpackDecoderAdapterTest,ReuseNameOfEvictedEntry)1048 TEST_P(HpackDecoderAdapterTest, ReuseNameOfEvictedEntry) {
1049 // Each entry is measured as 32 bytes plus the sum of the lengths of the name
1050 // and the value. Set the size big enough for at most one entry, and a fairly
1051 // small one at that (31 ASCII characters).
1052 decoder_.ApplyHeaderTableSizeSetting(63);
1053
1054 HpackBlockBuilder hbb;
1055 hbb.AppendDynamicTableSizeUpdate(0);
1056 hbb.AppendDynamicTableSizeUpdate(63);
1057
1058 const absl::string_view name("some-name");
1059 const absl::string_view value1("some-value");
1060 const absl::string_view value2("another-value");
1061 const absl::string_view value3("yet-another-value");
1062
1063 // Add an entry that will become the first in the dynamic table, entry 62.
1064 hbb.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader, false,
1065 name, false, value1);
1066
1067 // Confirm that entry has been added by re-using it.
1068 hbb.AppendIndexedHeader(62);
1069
1070 // Add another entry referring to the name of the first. This will evict the
1071 // first.
1072 hbb.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 62,
1073 false, value2);
1074
1075 // Confirm that entry has been added by re-using it.
1076 hbb.AppendIndexedHeader(62);
1077
1078 // Add another entry referring to the name of the second. This will evict the
1079 // second.
1080 hbb.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 62,
1081 false, value3);
1082
1083 // Confirm that entry has been added by re-using it.
1084 hbb.AppendIndexedHeader(62);
1085
1086 // Can't have DecodeHeaderBlock do the default check for size of the decoded
1087 // data because SpdyHeaderBlock will join multiple headers with the same
1088 // name into a single entry, thus we won't see repeated occurrences of the
1089 // name, instead seeing separators between values.
1090 EXPECT_TRUE(DecodeHeaderBlock(hbb.buffer(), kNoCheckDecodedSize));
1091
1092 SpdyHeaderBlock expected_header_set;
1093 expected_header_set.AppendValueOrAddHeader(name, value1);
1094 expected_header_set.AppendValueOrAddHeader(name, value1);
1095 expected_header_set.AppendValueOrAddHeader(name, value2);
1096 expected_header_set.AppendValueOrAddHeader(name, value2);
1097 expected_header_set.AppendValueOrAddHeader(name, value3);
1098 expected_header_set.AppendValueOrAddHeader(name, value3);
1099
1100 // SpdyHeaderBlock stores these 6 strings as '\0' separated values.
1101 // Make sure that is what happened.
1102 std::string joined_values = expected_header_set[name].as_string();
1103 EXPECT_EQ(joined_values.size(),
1104 2 * value1.size() + 2 * value2.size() + 2 * value3.size() + 5);
1105
1106 EXPECT_EQ(expected_header_set, decoded_block());
1107
1108 if (start_choice_ == START_WITH_HANDLER) {
1109 EXPECT_EQ(handler_.uncompressed_header_bytes(),
1110 6 * name.size() + 2 * value1.size() + 2 * value2.size() +
1111 2 * value3.size());
1112 }
1113 }
1114
1115 // Regression test for https://crbug.com/747395.
TEST_P(HpackDecoderAdapterTest,Cookies)1116 TEST_P(HpackDecoderAdapterTest, Cookies) {
1117 SpdyHeaderBlock expected_header_set;
1118 expected_header_set["cookie"] = "foo; bar";
1119
1120 EXPECT_TRUE(DecodeHeaderBlock(SpdyHexDecode("608294e76003626172")));
1121 EXPECT_EQ(expected_header_set, decoded_block());
1122 }
1123
1124 } // namespace
1125 } // namespace test
1126 } // namespace spdy
1127