1 // Copyright 2014 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/spdy/hpack_decoder.h" 6 7 #include <map> 8 #include <string> 9 10 #include "base/basictypes.h" 11 #include "base/logging.h" 12 #include "base/strings/string_piece.h" 13 #include "net/spdy/hpack_encoder.h" 14 #include "net/spdy/hpack_input_stream.h" 15 #include "net/spdy/hpack_output_stream.h" 16 #include "net/spdy/spdy_test_utils.h" 17 #include "testing/gmock/include/gmock/gmock.h" 18 #include "testing/gtest/include/gtest/gtest.h" 19 20 namespace net { 21 22 namespace test { 23 24 using base::StringPiece; 25 using std::string; 26 27 class HpackDecoderPeer { 28 public: 29 explicit HpackDecoderPeer(HpackDecoder* decoder) 30 : decoder_(decoder) {} 31 32 void HandleHeaderRepresentation(StringPiece name, StringPiece value) { 33 decoder_->HandleHeaderRepresentation(name, value); 34 } 35 bool DecodeNextName(HpackInputStream* in, StringPiece* out) { 36 return decoder_->DecodeNextName(in, out); 37 } 38 HpackHeaderTable* header_table() { 39 return &decoder_->header_table_; 40 } 41 void set_cookie_value(string value) { 42 decoder_->cookie_value_ = value; 43 } 44 string cookie_value() { 45 return decoder_->cookie_value_; 46 } 47 const std::map<string, string>& decoded_block() const { 48 return decoder_->decoded_block_; 49 } 50 const string& headers_block_buffer() const { 51 return decoder_->headers_block_buffer_; 52 } 53 54 private: 55 HpackDecoder* decoder_; 56 }; 57 58 } // namespace test 59 60 namespace { 61 62 using base::StringPiece; 63 using std::string; 64 using test::a2b_hex; 65 66 using testing::ElementsAre; 67 using testing::Pair; 68 69 const size_t kLiteralBound = 1024; 70 71 class HpackDecoderTest : public ::testing::Test { 72 protected: 73 HpackDecoderTest() 74 : decoder_(ObtainHpackHuffmanTable()), 75 decoder_peer_(&decoder_) {} 76 77 bool DecodeHeaderBlock(StringPiece str) { 78 return decoder_.HandleControlFrameHeadersData(0, str.data(), str.size()) && 79 decoder_.HandleControlFrameHeadersComplete(0); 80 } 81 82 const std::map<string, string>& decoded_block() const { 83 // TODO(jgraettinger): HpackDecoderTest should implement 84 // SpdyHeadersHandlerInterface, and collect headers for examination. 85 return decoder_peer_.decoded_block(); 86 } 87 88 const std::map<string, string>& DecodeBlockExpectingSuccess(StringPiece str) { 89 EXPECT_TRUE(DecodeHeaderBlock(str)); 90 return decoded_block(); 91 } 92 93 void expectEntry(size_t index, size_t size, const string& name, 94 const string& value) { 95 const HpackEntry* entry = decoder_peer_.header_table()->GetByIndex(index); 96 EXPECT_EQ(name, entry->name()) << "index " << index; 97 EXPECT_EQ(value, entry->value()); 98 EXPECT_EQ(size, entry->Size()); 99 EXPECT_EQ(index, decoder_peer_.header_table()->IndexOf(entry)); 100 } 101 102 HpackDecoder decoder_; 103 test::HpackDecoderPeer decoder_peer_; 104 }; 105 106 TEST_F(HpackDecoderTest, HandleControlFrameHeadersData) { 107 // Strings under threshold are concatenated in the buffer. 108 EXPECT_TRUE(decoder_.HandleControlFrameHeadersData( 109 0, "small string one", 16)); 110 EXPECT_TRUE(decoder_.HandleControlFrameHeadersData( 111 0, "small string two", 16)); 112 // A string which would push the buffer over the threshold is refused. 113 EXPECT_FALSE(decoder_.HandleControlFrameHeadersData( 114 0, "fails", kMaxDecodeBufferSize - 32 + 1)); 115 116 EXPECT_EQ(decoder_peer_.headers_block_buffer(), 117 "small string onesmall string two"); 118 } 119 120 TEST_F(HpackDecoderTest, HandleControlFrameHeadersComplete) { 121 decoder_peer_.set_cookie_value("foobar=baz"); 122 123 // Incremental cookie buffer should be emitted and cleared. 124 decoder_.HandleControlFrameHeadersData(0, "\x82\x85", 2); 125 decoder_.HandleControlFrameHeadersComplete(0); 126 127 EXPECT_THAT(decoded_block(), ElementsAre( 128 Pair(":method", "GET"), 129 Pair(":path", "/index.html"), 130 Pair("cookie", "foobar=baz"))); 131 EXPECT_EQ(decoder_peer_.cookie_value(), ""); 132 } 133 134 TEST_F(HpackDecoderTest, HandleHeaderRepresentation) { 135 // All cookie crumbs are joined. 136 decoder_peer_.HandleHeaderRepresentation("cookie", " part 1"); 137 decoder_peer_.HandleHeaderRepresentation("cookie", "part 2 "); 138 decoder_peer_.HandleHeaderRepresentation("cookie", "part3"); 139 140 // Already-delimited headers are passed through. 141 decoder_peer_.HandleHeaderRepresentation("passed-through", 142 string("foo\0baz", 7)); 143 144 // Other headers are joined on \0. Case matters. 145 decoder_peer_.HandleHeaderRepresentation("joined", "not joined"); 146 decoder_peer_.HandleHeaderRepresentation("joineD", "value 1"); 147 decoder_peer_.HandleHeaderRepresentation("joineD", "value 2"); 148 149 // Empty headers remain empty. 150 decoder_peer_.HandleHeaderRepresentation("empty", ""); 151 152 // Joined empty headers work as expected. 153 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); 154 decoder_peer_.HandleHeaderRepresentation("empty-joined", "foo"); 155 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); 156 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); 157 158 // Non-contiguous cookie crumb. 159 decoder_peer_.HandleHeaderRepresentation("cookie", " fin!"); 160 161 // Finish and emit all headers. 162 decoder_.HandleControlFrameHeadersComplete(0); 163 164 EXPECT_THAT(decoded_block(), ElementsAre( 165 Pair("cookie", " part 1; part 2 ; part3; fin!"), 166 Pair("empty", ""), 167 Pair("empty-joined", string("\0foo\0\0", 6)), 168 Pair("joineD", string("value 1\0value 2", 15)), 169 Pair("joined", "not joined"), 170 Pair("passed-through", string("foo\0baz", 7)))); 171 } 172 173 // Decoding an encoded name with a valid string literal should work. 174 TEST_F(HpackDecoderTest, DecodeNextNameLiteral) { 175 HpackInputStream input_stream(kLiteralBound, StringPiece("\x00\x04name", 6)); 176 177 StringPiece string_piece; 178 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); 179 EXPECT_EQ("name", string_piece); 180 EXPECT_FALSE(input_stream.HasMoreData()); 181 } 182 183 TEST_F(HpackDecoderTest, DecodeNextNameLiteralWithHuffmanEncoding) { 184 string input = a2b_hex("008825a849e95ba97d7f"); 185 HpackInputStream input_stream(kLiteralBound, input); 186 187 StringPiece string_piece; 188 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); 189 EXPECT_EQ("custom-key", string_piece); 190 EXPECT_FALSE(input_stream.HasMoreData()); 191 } 192 193 // Decoding an encoded name with a valid index should work. 194 TEST_F(HpackDecoderTest, DecodeNextNameIndexed) { 195 HpackInputStream input_stream(kLiteralBound, "\x01"); 196 197 StringPiece string_piece; 198 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); 199 EXPECT_EQ(":authority", string_piece); 200 EXPECT_FALSE(input_stream.HasMoreData()); 201 } 202 203 // Decoding an encoded name with an invalid index should fail. 204 TEST_F(HpackDecoderTest, DecodeNextNameInvalidIndex) { 205 // One more than the number of static table entries. 206 HpackInputStream input_stream(kLiteralBound, "\x3e"); 207 208 StringPiece string_piece; 209 EXPECT_FALSE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); 210 } 211 212 // Decoding indexed static table field should work. 213 TEST_F(HpackDecoderTest, IndexedHeaderStatic) { 214 // Reference static table entries #2 and #5. 215 std::map<string, string> header_set1 = 216 DecodeBlockExpectingSuccess("\x82\x85"); 217 std::map<string, string> expected_header_set1; 218 expected_header_set1[":method"] = "GET"; 219 expected_header_set1[":path"] = "/index.html"; 220 EXPECT_EQ(expected_header_set1, header_set1); 221 222 // Reference static table entry #2. 223 std::map<string, string> header_set2 = 224 DecodeBlockExpectingSuccess("\x82"); 225 std::map<string, string> expected_header_set2; 226 expected_header_set2[":method"] = "GET"; 227 EXPECT_EQ(expected_header_set2, header_set2); 228 } 229 230 TEST_F(HpackDecoderTest, IndexedHeaderDynamic) { 231 // First header block: add an entry to header table. 232 std::map<string, string> header_set1 = 233 DecodeBlockExpectingSuccess("\x40\x03" "foo" "\x03" "bar"); 234 std::map<string, string> expected_header_set1; 235 expected_header_set1["foo"] = "bar"; 236 EXPECT_EQ(expected_header_set1, header_set1); 237 238 // Second header block: add another entry to header table. 239 std::map<string, string> header_set2 = 240 DecodeBlockExpectingSuccess("\xbe\x40\x04" "spam" "\x04" "eggs"); 241 std::map<string, string> expected_header_set2; 242 expected_header_set2["foo"] = "bar"; 243 expected_header_set2["spam"] = "eggs"; 244 EXPECT_EQ(expected_header_set2, header_set2); 245 246 // Third header block: refer to most recently added entry. 247 std::map<string, string> header_set3 = 248 DecodeBlockExpectingSuccess("\xbe"); 249 std::map<string, string> expected_header_set3; 250 expected_header_set3["spam"] = "eggs"; 251 EXPECT_EQ(expected_header_set3, header_set3); 252 } 253 254 // Test a too-large indexed header. 255 TEST_F(HpackDecoderTest, InvalidIndexedHeader) { 256 // High-bit set, and a prefix of one more than the number of static entries. 257 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\xbe", 1))); 258 } 259 260 // Test that a header block with a pseudo-header field following a regular one 261 // is treated as malformed. (HTTP2 draft-14 8.1.2.1., HPACK draft-09 3.1.) 262 263 TEST_F(HpackDecoderTest, InvalidPseudoHeaderPositionStatic) { 264 // Okay: ":path" (static entry 4) followed by "allow" (static entry 20). 265 EXPECT_TRUE(DecodeHeaderBlock(a2b_hex("8494"))); 266 // Malformed: "allow" (static entry 20) followed by ":path" (static entry 4). 267 EXPECT_FALSE(DecodeHeaderBlock(a2b_hex("9484"))); 268 } 269 270 TEST_F(HpackDecoderTest, InvalidPseudoHeaderPositionLiteral) { 271 // Okay: literal ":bar" followed by literal "foo". 272 EXPECT_TRUE(DecodeHeaderBlock(a2b_hex("40043a626172004003666f6f00"))); 273 // Malformed: literal "foo" followed by literal ":bar". 274 EXPECT_FALSE(DecodeHeaderBlock(a2b_hex("4003666f6f0040043a62617200"))); 275 } 276 277 TEST_F(HpackDecoderTest, ContextUpdateMaximumSize) { 278 EXPECT_EQ(kDefaultHeaderTableSizeSetting, 279 decoder_peer_.header_table()->max_size()); 280 string input; 281 { 282 // Maximum-size update with size 126. Succeeds. 283 HpackOutputStream output_stream; 284 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); 285 output_stream.AppendUint32(126); 286 287 output_stream.TakeString(&input); 288 EXPECT_TRUE(DecodeHeaderBlock(StringPiece(input))); 289 EXPECT_EQ(126u, decoder_peer_.header_table()->max_size()); 290 } 291 { 292 // Maximum-size update with kDefaultHeaderTableSizeSetting. Succeeds. 293 HpackOutputStream output_stream; 294 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); 295 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting); 296 297 output_stream.TakeString(&input); 298 EXPECT_TRUE(DecodeHeaderBlock(StringPiece(input))); 299 EXPECT_EQ(kDefaultHeaderTableSizeSetting, 300 decoder_peer_.header_table()->max_size()); 301 } 302 { 303 // Maximum-size update with kDefaultHeaderTableSizeSetting + 1. Fails. 304 HpackOutputStream output_stream; 305 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); 306 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting + 1); 307 308 output_stream.TakeString(&input); 309 EXPECT_FALSE(DecodeHeaderBlock(StringPiece(input))); 310 EXPECT_EQ(kDefaultHeaderTableSizeSetting, 311 decoder_peer_.header_table()->max_size()); 312 } 313 } 314 315 // Decoding two valid encoded literal headers with no indexing should 316 // work. 317 TEST_F(HpackDecoderTest, LiteralHeaderNoIndexing) { 318 // First header with indexed name, second header with string literal 319 // name. 320 const char input[] = "\x04\x0c/sample/path\x00\x06:path2\x0e/sample/path/2"; 321 std::map<string, string> header_set = 322 DecodeBlockExpectingSuccess(StringPiece(input, arraysize(input) - 1)); 323 324 std::map<string, string> expected_header_set; 325 expected_header_set[":path"] = "/sample/path"; 326 expected_header_set[":path2"] = "/sample/path/2"; 327 EXPECT_EQ(expected_header_set, header_set); 328 } 329 330 // Decoding two valid encoded literal headers with incremental 331 // indexing and string literal names should work. 332 TEST_F(HpackDecoderTest, LiteralHeaderIncrementalIndexing) { 333 const char input[] = "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2"; 334 std::map<string, string> header_set = 335 DecodeBlockExpectingSuccess(StringPiece(input, arraysize(input) - 1)); 336 337 std::map<string, string> expected_header_set; 338 expected_header_set[":path"] = "/sample/path"; 339 expected_header_set[":path2"] = "/sample/path/2"; 340 EXPECT_EQ(expected_header_set, header_set); 341 } 342 343 TEST_F(HpackDecoderTest, LiteralHeaderWithIndexingInvalidNameIndex) { 344 decoder_.ApplyHeaderTableSizeSetting(0); 345 346 // Name is the last static index. Works. 347 EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x7d\x03ooo"))); 348 // Name is one beyond the last static index. Fails. 349 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x7e\x03ooo"))); 350 } 351 352 TEST_F(HpackDecoderTest, LiteralHeaderNoIndexingInvalidNameIndex) { 353 // Name is the last static index. Works. 354 EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x0f\x2e\x03ooo"))); 355 // Name is one beyond the last static index. Fails. 356 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x0f\x2f\x03ooo"))); 357 } 358 359 TEST_F(HpackDecoderTest, LiteralHeaderNeverIndexedInvalidNameIndex) { 360 // Name is the last static index. Works. 361 EXPECT_TRUE(DecodeHeaderBlock(StringPiece("\x1f\x2e\x03ooo"))); 362 // Name is one beyond the last static index. Fails. 363 EXPECT_FALSE(DecodeHeaderBlock(StringPiece("\x1f\x2f\x03ooo"))); 364 } 365 366 // Round-tripping the header set from E.2.1 should work. 367 TEST_F(HpackDecoderTest, BasicE21) { 368 HpackEncoder encoder(ObtainHpackHuffmanTable()); 369 370 std::map<string, string> expected_header_set; 371 expected_header_set[":method"] = "GET"; 372 expected_header_set[":scheme"] = "http"; 373 expected_header_set[":path"] = "/"; 374 expected_header_set[":authority"] = "www.example.com"; 375 376 string encoded_header_set; 377 EXPECT_TRUE(encoder.EncodeHeaderSet( 378 expected_header_set, &encoded_header_set)); 379 380 EXPECT_TRUE(DecodeHeaderBlock(encoded_header_set)); 381 EXPECT_EQ(expected_header_set, decoded_block()); 382 } 383 384 TEST_F(HpackDecoderTest, SectionD4RequestHuffmanExamples) { 385 std::map<string, string> header_set; 386 387 // 82 | == Indexed - Add == 388 // | idx = 2 389 // | -> :method: GET 390 // 86 | == Indexed - Add == 391 // | idx = 6 392 // | -> :scheme: http 393 // 84 | == Indexed - Add == 394 // | idx = 4 395 // | -> :path: / 396 // 41 | == Literal indexed == 397 // | Indexed name (idx = 1) 398 // | :authority 399 // 8c | Literal value (len = 15) 400 // | Huffman encoded: 401 // f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k..... 402 // | Decoded: 403 // | www.example.com 404 // | -> :authority: www.example.com 405 string first = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4" 406 "ff"); 407 header_set = DecodeBlockExpectingSuccess(first); 408 409 EXPECT_THAT(header_set, ElementsAre( 410 Pair(":authority", "www.example.com"), 411 Pair(":method", "GET"), 412 Pair(":path", "/"), 413 Pair(":scheme", "http"))); 414 415 expectEntry(62, 57, ":authority", "www.example.com"); 416 EXPECT_EQ(57u, decoder_peer_.header_table()->size()); 417 418 // 82 | == Indexed - Add == 419 // | idx = 2 420 // | -> :method: GET 421 // 86 | == Indexed - Add == 422 // | idx = 6 423 // | -> :scheme: http 424 // 84 | == Indexed - Add == 425 // | idx = 4 426 // | -> :path: / 427 // be | == Indexed - Add == 428 // | idx = 62 429 // | -> :authority: www.example.com 430 // 58 | == Literal indexed == 431 // | Indexed name (idx = 24) 432 // | cache-control 433 // 86 | Literal value (len = 8) 434 // | Huffman encoded: 435 // a8eb 1064 9cbf | ...d.. 436 // | Decoded: 437 // | no-cache 438 // | -> cache-control: no-cache 439 440 string second = a2b_hex("828684be5886a8eb10649cbf"); 441 header_set = DecodeBlockExpectingSuccess(second); 442 443 EXPECT_THAT(header_set, ElementsAre( 444 Pair(":authority", "www.example.com"), 445 Pair(":method", "GET"), 446 Pair(":path", "/"), 447 Pair(":scheme", "http"), 448 Pair("cache-control", "no-cache"))); 449 450 expectEntry(62, 53, "cache-control", "no-cache"); 451 expectEntry(63, 57, ":authority", "www.example.com"); 452 EXPECT_EQ(110u, decoder_peer_.header_table()->size()); 453 454 // 82 | == Indexed - Add == 455 // | idx = 2 456 // | -> :method: GET 457 // 87 | == Indexed - Add == 458 // | idx = 7 459 // | -> :scheme: https 460 // 85 | == Indexed - Add == 461 // | idx = 5 462 // | -> :path: /index.html 463 // bf | == Indexed - Add == 464 // | idx = 63 465 // | -> :authority: www.example.com 466 // 40 | == Literal indexed == 467 // 88 | Literal name (len = 10) 468 // | Huffman encoded: 469 // 25a8 49e9 5ba9 7d7f | %.I.[.}. 470 // | Decoded: 471 // | custom-key 472 // 89 | Literal value (len = 12) 473 // | Huffman encoded: 474 // 25a8 49e9 5bb8 e8b4 bf | %.I.[.... 475 // | Decoded: 476 // | custom-value 477 // | -> custom-key: custom-value 478 string third = a2b_hex("828785bf408825a849e95ba97d7f89" 479 "25a849e95bb8e8b4bf"); 480 header_set = DecodeBlockExpectingSuccess(third); 481 482 EXPECT_THAT(header_set, ElementsAre( 483 Pair(":authority", "www.example.com"), 484 Pair(":method", "GET"), 485 Pair(":path", "/index.html"), 486 Pair(":scheme", "https"), 487 Pair("custom-key", "custom-value"))); 488 489 expectEntry(62, 54, "custom-key", "custom-value"); 490 expectEntry(63, 53, "cache-control", "no-cache"); 491 expectEntry(64, 57, ":authority", "www.example.com"); 492 EXPECT_EQ(164u, decoder_peer_.header_table()->size()); 493 } 494 495 TEST_F(HpackDecoderTest, SectionD6ResponseHuffmanExamples) { 496 std::map<string, string> header_set; 497 decoder_.ApplyHeaderTableSizeSetting(256); 498 499 // 48 | == Literal indexed == 500 // | Indexed name (idx = 8) 501 // | :status 502 // 82 | Literal value (len = 3) 503 // | Huffman encoded: 504 // 6402 | d. 505 // | Decoded: 506 // | 302 507 // | -> :status: 302 508 // 58 | == Literal indexed == 509 // | Indexed name (idx = 24) 510 // | cache-control 511 // 85 | Literal value (len = 7) 512 // | Huffman encoded: 513 // aec3 771a 4b | ..w.K 514 // | Decoded: 515 // | private 516 // | -> cache-control: private 517 // 61 | == Literal indexed == 518 // | Indexed name (idx = 33) 519 // | date 520 // 96 | Literal value (len = 29) 521 // | Huffman encoded: 522 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f 523 // e082 a62d 1bff | ...-.. 524 // | Decoded: 525 // | Mon, 21 Oct 2013 20:13:21 526 // | GMT 527 // | -> date: Mon, 21 Oct 2013 528 // | 20:13:21 GMT 529 // 6e | == Literal indexed == 530 // | Indexed name (idx = 46) 531 // | location 532 // 91 | Literal value (len = 23) 533 // | Huffman encoded: 534 // 9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 | .)...c.........C 535 // d3 | . 536 // | Decoded: 537 // | https://www.example.com 538 // | -> location: https://www.e 539 // | xample.com 540 541 string first = a2b_hex("488264025885aec3771a4b6196d07abe" 542 "941054d444a8200595040b8166e082a6" 543 "2d1bff6e919d29ad171863c78f0b97c8" 544 "e9ae82ae43d3"); 545 header_set = DecodeBlockExpectingSuccess(first); 546 547 EXPECT_THAT(header_set, ElementsAre( 548 Pair(":status", "302"), 549 Pair("cache-control", "private"), 550 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 551 Pair("location", "https://www.example.com"))); 552 553 expectEntry(62, 63, "location", "https://www.example.com"); 554 expectEntry(63, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT"); 555 expectEntry(64, 52, "cache-control", "private"); 556 expectEntry(65, 42, ":status", "302"); 557 EXPECT_EQ(222u, decoder_peer_.header_table()->size()); 558 559 // 48 | == Literal indexed == 560 // | Indexed name (idx = 8) 561 // | :status 562 // 83 | Literal value (len = 3) 563 // | Huffman encoded: 564 // 640e ff | d.. 565 // | Decoded: 566 // | 307 567 // | - evict: :status: 302 568 // | -> :status: 307 569 // c1 | == Indexed - Add == 570 // | idx = 65 571 // | -> cache-control: private 572 // c0 | == Indexed - Add == 573 // | idx = 64 574 // | -> date: Mon, 21 Oct 2013 575 // | 20:13:21 GMT 576 // bf | == Indexed - Add == 577 // | idx = 63 578 // | -> location: 579 // | https://www.example.com 580 string second = a2b_hex("4883640effc1c0bf"); 581 header_set = DecodeBlockExpectingSuccess(second); 582 583 EXPECT_THAT(header_set, ElementsAre( 584 Pair(":status", "307"), 585 Pair("cache-control", "private"), 586 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), 587 Pair("location", "https://www.example.com"))); 588 589 expectEntry(62, 42, ":status", "307"); 590 expectEntry(63, 63, "location", "https://www.example.com"); 591 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT"); 592 expectEntry(65, 52, "cache-control", "private"); 593 EXPECT_EQ(222u, decoder_peer_.header_table()->size()); 594 595 // 88 | == Indexed - Add == 596 // | idx = 8 597 // | -> :status: 200 598 // c1 | == Indexed - Add == 599 // | idx = 65 600 // | -> cache-control: private 601 // 61 | == Literal indexed == 602 // | Indexed name (idx = 33) 603 // | date 604 // 96 | Literal value (len = 22) 605 // | Huffman encoded: 606 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f 607 // e084 a62d 1bff | ...-.. 608 // | Decoded: 609 // | Mon, 21 Oct 2013 20:13:22 610 // | GMT 611 // | - evict: cache-control: 612 // | private 613 // | -> date: Mon, 21 Oct 2013 614 // | 20:13:22 GMT 615 // c0 | == Indexed - Add == 616 // | idx = 64 617 // | -> location: 618 // | https://www.example.com 619 // 5a | == Literal indexed == 620 // | Indexed name (idx = 26) 621 // | content-encoding 622 // 83 | Literal value (len = 3) 623 // | Huffman encoded: 624 // 9bd9 ab | ... 625 // | Decoded: 626 // | gzip 627 // | - evict: date: Mon, 21 Oct 628 // | 2013 20:13:21 GMT 629 // | -> content-encoding: gzip 630 // 77 | == Literal indexed == 631 // | Indexed name (idx = 55) 632 // | set-cookie 633 // ad | Literal value (len = 45) 634 // | Huffman encoded: 635 // 94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 | .........5...[9` 636 // d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 | ..'..6r..'..)... 637 // 3160 65c0 03ed 4ee5 b106 3d50 07 | 1`e...N...=P. 638 // | Decoded: 639 // | foo=ASDJKHQKBZXOQWEOPIUAXQ 640 // | WEOIU; max-age=3600; versi 641 // | on=1 642 // | - evict: location: 643 // | https://www.example.com 644 // | - evict: :status: 307 645 // | -> set-cookie: foo=ASDJKHQ 646 // | KBZXOQWEOPIUAXQWEOIU; 647 // | max-age=3600; version=1 648 string third = a2b_hex("88c16196d07abe941054d444a8200595" 649 "040b8166e084a62d1bffc05a839bd9ab" 650 "77ad94e7821dd7f2e6c7b335dfdfcd5b" 651 "3960d5af27087f3672c1ab270fb5291f" 652 "9587316065c003ed4ee5b1063d5007"); 653 header_set = DecodeBlockExpectingSuccess(third); 654 655 EXPECT_THAT(header_set, ElementsAre( 656 Pair(":status", "200"), 657 Pair("cache-control", "private"), 658 Pair("content-encoding", "gzip"), 659 Pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"), 660 Pair("location", "https://www.example.com"), 661 Pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" 662 " max-age=3600; version=1"))); 663 664 expectEntry(62, 98, "set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" 665 " max-age=3600; version=1"); 666 expectEntry(63, 52, "content-encoding", "gzip"); 667 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:22 GMT"); 668 EXPECT_EQ(215u, decoder_peer_.header_table()->size()); 669 } 670 671 } // namespace 672 673 } // namespace net 674