| OLD | NEW |
| (Empty) |
| 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/hpack_decoder.h" | |
| 6 | |
| 7 #include <map> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "net/spdy/hpack/hpack_encoder.h" | |
| 11 #include "net/spdy/hpack/hpack_input_stream.h" | |
| 12 #include "net/spdy/hpack/hpack_output_stream.h" | |
| 13 #include "net/spdy/spdy_flags.h" | |
| 14 #include "net/spdy/spdy_protocol.h" | |
| 15 #include "net/spdy/spdy_test_utils.h" | |
| 16 #include "testing/gmock/include/gmock/gmock.h" | |
| 17 #include "testing/gtest/include/gtest/gtest.h" | |
| 18 | |
| 19 namespace net { | |
| 20 namespace test { | |
| 21 | |
| 22 class HpackDecoderPeer { | |
| 23 public: | |
| 24 explicit HpackDecoderPeer(HpackDecoder* decoder) : decoder_(decoder) {} | |
| 25 | |
| 26 void HandleHeaderRepresentation(SpdyStringPiece name, SpdyStringPiece value) { | |
| 27 decoder_->HandleHeaderRepresentation(name, value); | |
| 28 } | |
| 29 bool DecodeNextName(HpackInputStream* in, SpdyStringPiece* out) { | |
| 30 return decoder_->DecodeNextName(in, out); | |
| 31 } | |
| 32 HpackHeaderTable* header_table() { return &decoder_->header_table_; } | |
| 33 | |
| 34 bool DecodeNextStringLiteral(HpackInputStream* in, | |
| 35 bool is_header_key, | |
| 36 SpdyStringPiece* str) { | |
| 37 return decoder_->DecodeNextStringLiteral(in, is_header_key, str); | |
| 38 } | |
| 39 | |
| 40 const SpdyString& headers_block_buffer() const { | |
| 41 return decoder_->headers_block_buffer_; | |
| 42 } | |
| 43 | |
| 44 private: | |
| 45 HpackDecoder* decoder_; | |
| 46 }; | |
| 47 | |
| 48 namespace { | |
| 49 | |
| 50 using test::a2b_hex; | |
| 51 | |
| 52 using testing::ElementsAre; | |
| 53 using testing::Pair; | |
| 54 | |
| 55 class HpackDecoderTest : public ::testing::TestWithParam<bool> { | |
| 56 protected: | |
| 57 HpackDecoderTest() : decoder_(), decoder_peer_(&decoder_) {} | |
| 58 | |
| 59 void SetUp() override { handler_exists_ = GetParam(); } | |
| 60 | |
| 61 bool DecodeHeaderBlock(SpdyStringPiece str) { | |
| 62 if (handler_exists_) { | |
| 63 decoder_.HandleControlFrameHeadersStart(&handler_); | |
| 64 } | |
| 65 return decoder_.HandleControlFrameHeadersData(str.data(), str.size()) && | |
| 66 decoder_.HandleControlFrameHeadersComplete(nullptr); | |
| 67 } | |
| 68 | |
| 69 bool HandleControlFrameHeadersData(SpdyStringPiece str) { | |
| 70 return decoder_.HandleControlFrameHeadersData(str.data(), str.size()); | |
| 71 } | |
| 72 | |
| 73 bool HandleControlFrameHeadersComplete(size_t* size) { | |
| 74 return decoder_.HandleControlFrameHeadersComplete(size); | |
| 75 } | |
| 76 | |
| 77 const SpdyHeaderBlock& decoded_block() const { | |
| 78 if (handler_exists_) { | |
| 79 return handler_.decoded_block(); | |
| 80 } else { | |
| 81 return decoder_.decoded_block(); | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 const SpdyHeaderBlock& DecodeBlockExpectingSuccess(SpdyStringPiece str) { | |
| 86 EXPECT_TRUE(DecodeHeaderBlock(str)); | |
| 87 return decoded_block(); | |
| 88 } | |
| 89 | |
| 90 void expectEntry(size_t index, | |
| 91 size_t size, | |
| 92 const SpdyString& name, | |
| 93 const SpdyString& value) { | |
| 94 const HpackEntry* entry = decoder_peer_.header_table()->GetByIndex(index); | |
| 95 EXPECT_EQ(name, entry->name()) << "index " << index; | |
| 96 EXPECT_EQ(value, entry->value()); | |
| 97 EXPECT_EQ(size, entry->Size()); | |
| 98 EXPECT_EQ(index, decoder_peer_.header_table()->IndexOf(entry)); | |
| 99 } | |
| 100 | |
| 101 HpackDecoder decoder_; | |
| 102 test::HpackDecoderPeer decoder_peer_; | |
| 103 TestHeadersHandler handler_; | |
| 104 bool handler_exists_; | |
| 105 }; | |
| 106 | |
| 107 INSTANTIATE_TEST_CASE_P(WithAndWithoutHeadersHandler, | |
| 108 HpackDecoderTest, | |
| 109 ::testing::Bool()); | |
| 110 | |
| 111 TEST_P(HpackDecoderTest, AddHeaderDataWithHandleControlFrameHeadersData) { | |
| 112 // The hpack decode buffer size is limited in size. This test verifies that | |
| 113 // adding encoded data under that limit is accepted, and data that exceeds the | |
| 114 // limit is rejected. | |
| 115 const size_t kMaxBufferSizeBytes = 50; | |
| 116 decoder_.set_max_decode_buffer_size_bytes(kMaxBufferSizeBytes); | |
| 117 | |
| 118 // Strings under threshold are concatenated in the buffer. | |
| 119 SpdyString first_input; | |
| 120 first_input.push_back(0x00); // Literal name and value, unindexed | |
| 121 first_input.push_back(0x7f); // Name length = 127 | |
| 122 ASSERT_EQ(2u, first_input.size()); | |
| 123 EXPECT_TRUE(decoder_.HandleControlFrameHeadersData(first_input.data(), | |
| 124 first_input.size())); | |
| 125 // Further 38 bytes to make 40 total buffered bytes. | |
| 126 SpdyString second_input = SpdyString(38, 'x'); | |
| 127 EXPECT_TRUE(decoder_.HandleControlFrameHeadersData(second_input.data(), | |
| 128 second_input.size())); | |
| 129 // A string which would push the buffer over the threshold is refused. | |
| 130 const int kThirdInputSize = | |
| 131 kMaxBufferSizeBytes - (first_input.size() + second_input.size()) + 1; | |
| 132 SpdyString third_input = SpdyString(kThirdInputSize, 'y'); | |
| 133 ASSERT_GT(first_input.size() + second_input.size() + third_input.size(), | |
| 134 kMaxBufferSizeBytes); | |
| 135 EXPECT_FALSE(decoder_.HandleControlFrameHeadersData(third_input.data(), | |
| 136 third_input.size())); | |
| 137 | |
| 138 SpdyString expected(first_input); | |
| 139 expected.append(second_input); | |
| 140 EXPECT_EQ(expected, decoder_peer_.headers_block_buffer()); | |
| 141 } | |
| 142 | |
| 143 // Decode with incomplete data in buffer. | |
| 144 TEST_P(HpackDecoderTest, DecodeWithIncompleteData) { | |
| 145 // No need to wait for more data. | |
| 146 EXPECT_TRUE(HandleControlFrameHeadersData("\x82\x85\x82")); | |
| 147 EXPECT_EQ("", decoder_peer_.headers_block_buffer()); | |
| 148 | |
| 149 // Need to wait for more data. | |
| 150 EXPECT_TRUE( | |
| 151 HandleControlFrameHeadersData("\x40\x03goo" | |
| 152 "\x03gar\xbe\x40\x04spam")); | |
| 153 EXPECT_EQ("\x40\x04spam", decoder_peer_.headers_block_buffer()); | |
| 154 | |
| 155 // Add the needed data. | |
| 156 EXPECT_TRUE(HandleControlFrameHeadersData("\x04gggs")); | |
| 157 EXPECT_EQ("", decoder_peer_.headers_block_buffer()); | |
| 158 | |
| 159 size_t size = 0; | |
| 160 EXPECT_TRUE(HandleControlFrameHeadersComplete(&size)); | |
| 161 EXPECT_EQ(24u, size); | |
| 162 } | |
| 163 | |
| 164 TEST_P(HpackDecoderTest, HandleHeaderRepresentation) { | |
| 165 if (handler_exists_) { | |
| 166 decoder_.HandleControlFrameHeadersStart(&handler_); | |
| 167 } | |
| 168 | |
| 169 // All cookie crumbs are joined. | |
| 170 decoder_peer_.HandleHeaderRepresentation("cookie", " part 1"); | |
| 171 decoder_peer_.HandleHeaderRepresentation("cookie", "part 2 "); | |
| 172 decoder_peer_.HandleHeaderRepresentation("cookie", "part3"); | |
| 173 | |
| 174 // Already-delimited headers are passed through. | |
| 175 decoder_peer_.HandleHeaderRepresentation("passed-through", | |
| 176 SpdyString("foo\0baz", 7)); | |
| 177 | |
| 178 // Other headers are joined on \0. Case matters. | |
| 179 decoder_peer_.HandleHeaderRepresentation("joined", "not joined"); | |
| 180 decoder_peer_.HandleHeaderRepresentation("joineD", "value 1"); | |
| 181 decoder_peer_.HandleHeaderRepresentation("joineD", "value 2"); | |
| 182 | |
| 183 // Empty headers remain empty. | |
| 184 decoder_peer_.HandleHeaderRepresentation("empty", ""); | |
| 185 | |
| 186 // Joined empty headers work as expected. | |
| 187 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); | |
| 188 decoder_peer_.HandleHeaderRepresentation("empty-joined", "foo"); | |
| 189 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); | |
| 190 decoder_peer_.HandleHeaderRepresentation("empty-joined", ""); | |
| 191 | |
| 192 // Non-contiguous cookie crumb. | |
| 193 decoder_peer_.HandleHeaderRepresentation("cookie", " fin!"); | |
| 194 | |
| 195 // Finish and emit all headers. | |
| 196 decoder_.HandleControlFrameHeadersComplete(nullptr); | |
| 197 | |
| 198 // Resulting decoded headers are in the same order as input. | |
| 199 EXPECT_THAT( | |
| 200 decoded_block(), | |
| 201 ElementsAre(Pair("cookie", " part 1; part 2 ; part3; fin!"), | |
| 202 Pair("passed-through", SpdyStringPiece("foo\0baz", 7)), | |
| 203 Pair("joined", "not joined"), | |
| 204 Pair("joineD", SpdyStringPiece("value 1\0value 2", 15)), | |
| 205 Pair("empty", ""), | |
| 206 Pair("empty-joined", SpdyStringPiece("\0foo\0\0", 6)))); | |
| 207 } | |
| 208 | |
| 209 // Decoding an encoded name with a valid string literal should work. | |
| 210 TEST_P(HpackDecoderTest, DecodeNextNameLiteral) { | |
| 211 HpackInputStream input_stream(SpdyStringPiece("\x00\x04name", 6)); | |
| 212 | |
| 213 SpdyStringPiece string_piece; | |
| 214 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
| 215 EXPECT_EQ("name", string_piece); | |
| 216 EXPECT_FALSE(input_stream.HasMoreData()); | |
| 217 EXPECT_FALSE(input_stream.NeedMoreData()); | |
| 218 input_stream.MarkCurrentPosition(); | |
| 219 EXPECT_EQ(6u, input_stream.ParsedBytes()); | |
| 220 } | |
| 221 | |
| 222 // Decoding an encoded name with an incomplete string literal. | |
| 223 TEST_P(HpackDecoderTest, DecodeNextNameLiteralWithIncompleteHeader) { | |
| 224 HpackInputStream input_stream(SpdyStringPiece("\x00\x04name\x00\x02g", 9)); | |
| 225 | |
| 226 SpdyStringPiece string_piece; | |
| 227 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
| 228 EXPECT_FALSE(input_stream.NeedMoreData()); | |
| 229 input_stream.MarkCurrentPosition(); | |
| 230 EXPECT_EQ(6u, input_stream.ParsedBytes()); | |
| 231 | |
| 232 EXPECT_FALSE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
| 233 EXPECT_TRUE(input_stream.NeedMoreData()); | |
| 234 input_stream.MarkCurrentPosition(); | |
| 235 EXPECT_EQ(8u, input_stream.ParsedBytes()); | |
| 236 } | |
| 237 | |
| 238 TEST_P(HpackDecoderTest, DecodeNextNameLiteralWithHuffmanEncoding) { | |
| 239 SpdyString input = a2b_hex("008825a849e95ba97d7f"); | |
| 240 HpackInputStream input_stream(input); | |
| 241 | |
| 242 SpdyStringPiece string_piece; | |
| 243 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
| 244 EXPECT_EQ("custom-key", string_piece); | |
| 245 EXPECT_FALSE(input_stream.HasMoreData()); | |
| 246 EXPECT_FALSE(input_stream.NeedMoreData()); | |
| 247 input_stream.MarkCurrentPosition(); | |
| 248 EXPECT_EQ(input.size(), input_stream.ParsedBytes()); | |
| 249 } | |
| 250 | |
| 251 // Decode with incomplete huffman encoding. | |
| 252 TEST_P(HpackDecoderTest, DecodeNextNameLiteralWithIncompleteHuffmanEncoding) { | |
| 253 // CHECK(huffman_table_.Initialize(kHpackHuffmanCode, | |
| 254 // arraysize(kHpackHuffmanCode))); | |
| 255 // Put two copies of the same huffman encoding into input. | |
| 256 SpdyString input = a2b_hex("008825a849e95ba97d7f008825a849e95ba97d7f"); | |
| 257 input.resize(input.size() - 1); // Remove the last byte. | |
| 258 HpackInputStream input_stream(input); | |
| 259 | |
| 260 SpdyStringPiece string_piece; | |
| 261 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
| 262 EXPECT_FALSE(input_stream.NeedMoreData()); | |
| 263 input_stream.MarkCurrentPosition(); | |
| 264 EXPECT_EQ(10u, input_stream.ParsedBytes()); | |
| 265 | |
| 266 EXPECT_FALSE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
| 267 EXPECT_TRUE(input_stream.NeedMoreData()); | |
| 268 input_stream.MarkCurrentPosition(); | |
| 269 EXPECT_EQ(12u, input_stream.ParsedBytes()); | |
| 270 } | |
| 271 | |
| 272 // Decoding an encoded name with a valid index should work. | |
| 273 TEST_P(HpackDecoderTest, DecodeNextNameIndexed) { | |
| 274 HpackInputStream input_stream("\x01"); | |
| 275 | |
| 276 SpdyStringPiece string_piece; | |
| 277 EXPECT_TRUE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
| 278 EXPECT_EQ(":authority", string_piece); | |
| 279 EXPECT_FALSE(input_stream.HasMoreData()); | |
| 280 EXPECT_FALSE(input_stream.NeedMoreData()); | |
| 281 input_stream.MarkCurrentPosition(); | |
| 282 EXPECT_EQ(1u, input_stream.ParsedBytes()); | |
| 283 } | |
| 284 | |
| 285 // Decoding an encoded name with an invalid index should fail. | |
| 286 TEST_P(HpackDecoderTest, DecodeNextNameInvalidIndex) { | |
| 287 // One more than the number of static table entries. | |
| 288 HpackInputStream input_stream("\x3e"); | |
| 289 | |
| 290 SpdyStringPiece string_piece; | |
| 291 EXPECT_FALSE(decoder_peer_.DecodeNextName(&input_stream, &string_piece)); | |
| 292 EXPECT_FALSE(input_stream.NeedMoreData()); | |
| 293 input_stream.MarkCurrentPosition(); | |
| 294 EXPECT_EQ(1u, input_stream.ParsedBytes()); | |
| 295 } | |
| 296 | |
| 297 // Decoding indexed static table field should work. | |
| 298 TEST_P(HpackDecoderTest, IndexedHeaderStatic) { | |
| 299 // Reference static table entries #2 and #5. | |
| 300 const SpdyHeaderBlock& header_set1 = DecodeBlockExpectingSuccess("\x82\x85"); | |
| 301 SpdyHeaderBlock expected_header_set1; | |
| 302 expected_header_set1[":method"] = "GET"; | |
| 303 expected_header_set1[":path"] = "/index.html"; | |
| 304 EXPECT_EQ(expected_header_set1, header_set1); | |
| 305 | |
| 306 // Reference static table entry #2. | |
| 307 const SpdyHeaderBlock& header_set2 = DecodeBlockExpectingSuccess("\x82"); | |
| 308 SpdyHeaderBlock expected_header_set2; | |
| 309 expected_header_set2[":method"] = "GET"; | |
| 310 EXPECT_EQ(expected_header_set2, header_set2); | |
| 311 } | |
| 312 | |
| 313 TEST_P(HpackDecoderTest, IndexedHeaderDynamic) { | |
| 314 // First header block: add an entry to header table. | |
| 315 const SpdyHeaderBlock& header_set1 = DecodeBlockExpectingSuccess( | |
| 316 "\x40\x03" | |
| 317 "foo" | |
| 318 "\x03" | |
| 319 "bar"); | |
| 320 SpdyHeaderBlock expected_header_set1; | |
| 321 expected_header_set1["foo"] = "bar"; | |
| 322 EXPECT_EQ(expected_header_set1, header_set1); | |
| 323 | |
| 324 // Second header block: add another entry to header table. | |
| 325 const SpdyHeaderBlock& header_set2 = DecodeBlockExpectingSuccess( | |
| 326 "\xbe\x40\x04" | |
| 327 "spam" | |
| 328 "\x04" | |
| 329 "eggs"); | |
| 330 SpdyHeaderBlock expected_header_set2; | |
| 331 expected_header_set2["foo"] = "bar"; | |
| 332 expected_header_set2["spam"] = "eggs"; | |
| 333 EXPECT_EQ(expected_header_set2, header_set2); | |
| 334 | |
| 335 // Third header block: refer to most recently added entry. | |
| 336 const SpdyHeaderBlock& header_set3 = DecodeBlockExpectingSuccess("\xbe"); | |
| 337 SpdyHeaderBlock expected_header_set3; | |
| 338 expected_header_set3["spam"] = "eggs"; | |
| 339 EXPECT_EQ(expected_header_set3, header_set3); | |
| 340 } | |
| 341 | |
| 342 // Test a too-large indexed header. | |
| 343 TEST_P(HpackDecoderTest, InvalidIndexedHeader) { | |
| 344 // High-bit set, and a prefix of one more than the number of static entries. | |
| 345 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece("\xbe", 1))); | |
| 346 } | |
| 347 | |
| 348 TEST_P(HpackDecoderTest, ContextUpdateMaximumSize) { | |
| 349 EXPECT_EQ(kDefaultHeaderTableSizeSetting, | |
| 350 decoder_peer_.header_table()->max_size()); | |
| 351 SpdyString input; | |
| 352 { | |
| 353 // Maximum-size update with size 126. Succeeds. | |
| 354 HpackOutputStream output_stream; | |
| 355 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
| 356 output_stream.AppendUint32(126); | |
| 357 | |
| 358 output_stream.TakeString(&input); | |
| 359 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
| 360 EXPECT_EQ(126u, decoder_peer_.header_table()->max_size()); | |
| 361 } | |
| 362 { | |
| 363 // Maximum-size update with kDefaultHeaderTableSizeSetting. Succeeds. | |
| 364 HpackOutputStream output_stream; | |
| 365 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
| 366 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting); | |
| 367 | |
| 368 output_stream.TakeString(&input); | |
| 369 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
| 370 EXPECT_EQ(kDefaultHeaderTableSizeSetting, | |
| 371 decoder_peer_.header_table()->max_size()); | |
| 372 } | |
| 373 { | |
| 374 // Maximum-size update with kDefaultHeaderTableSizeSetting + 1. Fails. | |
| 375 HpackOutputStream output_stream; | |
| 376 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
| 377 output_stream.AppendUint32(kDefaultHeaderTableSizeSetting + 1); | |
| 378 | |
| 379 output_stream.TakeString(&input); | |
| 380 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
| 381 EXPECT_EQ(kDefaultHeaderTableSizeSetting, | |
| 382 decoder_peer_.header_table()->max_size()); | |
| 383 } | |
| 384 } | |
| 385 | |
| 386 // Two HeaderTableSizeUpdates may appear at the beginning of the block | |
| 387 TEST_P(HpackDecoderTest, TwoTableSizeUpdates) { | |
| 388 SpdyString input; | |
| 389 { | |
| 390 // Should accept two table size updates, update to second one | |
| 391 HpackOutputStream output_stream; | |
| 392 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
| 393 output_stream.AppendUint32(0); | |
| 394 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
| 395 output_stream.AppendUint32(122); | |
| 396 | |
| 397 output_stream.TakeString(&input); | |
| 398 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
| 399 EXPECT_EQ(122u, decoder_peer_.header_table()->max_size()); | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 // Three HeaderTableSizeUpdates should result in an error | |
| 404 TEST_P(HpackDecoderTest, ThreeTableSizeUpdatesError) { | |
| 405 SpdyString input; | |
| 406 { | |
| 407 // Should reject three table size updates, update to second one | |
| 408 HpackOutputStream output_stream; | |
| 409 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
| 410 output_stream.AppendUint32(5); | |
| 411 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
| 412 output_stream.AppendUint32(10); | |
| 413 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
| 414 output_stream.AppendUint32(15); | |
| 415 | |
| 416 output_stream.TakeString(&input); | |
| 417 | |
| 418 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
| 419 EXPECT_EQ(10u, decoder_peer_.header_table()->max_size()); | |
| 420 } | |
| 421 } | |
| 422 | |
| 423 // HeaderTableSizeUpdates may only appear at the beginning of the block | |
| 424 // Any other updates should result in an error | |
| 425 TEST_P(HpackDecoderTest, TableSizeUpdateSecondError) { | |
| 426 SpdyString input; | |
| 427 { | |
| 428 // Should reject a table size update appearing after a different entry | |
| 429 // The table size should remain as the default | |
| 430 HpackOutputStream output_stream; | |
| 431 output_stream.AppendBytes("\x82\x85"); | |
| 432 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
| 433 output_stream.AppendUint32(123); | |
| 434 | |
| 435 output_stream.TakeString(&input); | |
| 436 | |
| 437 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
| 438 EXPECT_EQ(kDefaultHeaderTableSizeSetting, | |
| 439 decoder_peer_.header_table()->max_size()); | |
| 440 } | |
| 441 } | |
| 442 | |
| 443 // HeaderTableSizeUpdates may only appear at the beginning of the block | |
| 444 // Any other updates should result in an error | |
| 445 TEST_P(HpackDecoderTest, TableSizeUpdateFirstThirdError) { | |
| 446 SpdyString input; | |
| 447 { | |
| 448 // Should reject the second table size update | |
| 449 // if a different entry appears after the first update | |
| 450 // The table size should update to the first but not the second | |
| 451 HpackOutputStream output_stream; | |
| 452 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
| 453 output_stream.AppendUint32(60); | |
| 454 output_stream.AppendBytes("\x82\x85"); | |
| 455 output_stream.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
| 456 output_stream.AppendUint32(125); | |
| 457 | |
| 458 output_stream.TakeString(&input); | |
| 459 | |
| 460 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece(input))); | |
| 461 EXPECT_EQ(60u, decoder_peer_.header_table()->max_size()); | |
| 462 } | |
| 463 } | |
| 464 | |
| 465 // Decoding two valid encoded literal headers with no indexing should | |
| 466 // work. | |
| 467 TEST_P(HpackDecoderTest, LiteralHeaderNoIndexing) { | |
| 468 // First header with indexed name, second header with string literal | |
| 469 // name. | |
| 470 const char input[] = "\x04\x0c/sample/path\x00\x06:path2\x0e/sample/path/2"; | |
| 471 const SpdyHeaderBlock& header_set = | |
| 472 DecodeBlockExpectingSuccess(SpdyStringPiece(input, arraysize(input) - 1)); | |
| 473 | |
| 474 SpdyHeaderBlock expected_header_set; | |
| 475 expected_header_set[":path"] = "/sample/path"; | |
| 476 expected_header_set[":path2"] = "/sample/path/2"; | |
| 477 EXPECT_EQ(expected_header_set, header_set); | |
| 478 } | |
| 479 | |
| 480 // Decoding two valid encoded literal headers with incremental | |
| 481 // indexing and string literal names should work. | |
| 482 TEST_P(HpackDecoderTest, LiteralHeaderIncrementalIndexing) { | |
| 483 const char input[] = "\x44\x0c/sample/path\x40\x06:path2\x0e/sample/path/2"; | |
| 484 const SpdyHeaderBlock& header_set = | |
| 485 DecodeBlockExpectingSuccess(SpdyStringPiece(input, arraysize(input) - 1)); | |
| 486 | |
| 487 SpdyHeaderBlock expected_header_set; | |
| 488 expected_header_set[":path"] = "/sample/path"; | |
| 489 expected_header_set[":path2"] = "/sample/path/2"; | |
| 490 EXPECT_EQ(expected_header_set, header_set); | |
| 491 } | |
| 492 | |
| 493 TEST_P(HpackDecoderTest, LiteralHeaderWithIndexingInvalidNameIndex) { | |
| 494 decoder_.ApplyHeaderTableSizeSetting(0); | |
| 495 | |
| 496 // Name is the last static index. Works. | |
| 497 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece("\x7d\x03ooo"))); | |
| 498 // Name is one beyond the last static index. Fails. | |
| 499 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece("\x7e\x03ooo"))); | |
| 500 } | |
| 501 | |
| 502 TEST_P(HpackDecoderTest, LiteralHeaderNoIndexingInvalidNameIndex) { | |
| 503 // Name is the last static index. Works. | |
| 504 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece("\x0f\x2e\x03ooo"))); | |
| 505 // Name is one beyond the last static index. Fails. | |
| 506 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece("\x0f\x2f\x03ooo"))); | |
| 507 } | |
| 508 | |
| 509 TEST_P(HpackDecoderTest, LiteralHeaderNeverIndexedInvalidNameIndex) { | |
| 510 // Name is the last static index. Works. | |
| 511 EXPECT_TRUE(DecodeHeaderBlock(SpdyStringPiece("\x1f\x2e\x03ooo"))); | |
| 512 // Name is one beyond the last static index. Fails. | |
| 513 EXPECT_FALSE(DecodeHeaderBlock(SpdyStringPiece("\x1f\x2f\x03ooo"))); | |
| 514 } | |
| 515 | |
| 516 // Decode with incomplete string literal. | |
| 517 TEST_P(HpackDecoderTest, StringLiteralIncomplete) { | |
| 518 const char input[] = "\x0c/sample/path\x06:path2\x0e/sample/path/"; | |
| 519 HpackInputStream input_stream(input); | |
| 520 SpdyStringPiece str; | |
| 521 EXPECT_TRUE( | |
| 522 decoder_peer_.DecodeNextStringLiteral(&input_stream, false, &str)); | |
| 523 EXPECT_FALSE(input_stream.NeedMoreData()); | |
| 524 input_stream.MarkCurrentPosition(); | |
| 525 EXPECT_EQ(13u, input_stream.ParsedBytes()); | |
| 526 | |
| 527 EXPECT_TRUE( | |
| 528 decoder_peer_.DecodeNextStringLiteral(&input_stream, false, &str)); | |
| 529 EXPECT_FALSE(input_stream.NeedMoreData()); | |
| 530 input_stream.MarkCurrentPosition(); | |
| 531 EXPECT_EQ(20u, input_stream.ParsedBytes()); | |
| 532 | |
| 533 EXPECT_FALSE( | |
| 534 decoder_peer_.DecodeNextStringLiteral(&input_stream, false, &str)); | |
| 535 EXPECT_TRUE(input_stream.NeedMoreData()); | |
| 536 input_stream.MarkCurrentPosition(); | |
| 537 EXPECT_EQ(21u, input_stream.ParsedBytes()); | |
| 538 } | |
| 539 | |
| 540 // Round-tripping the header set from RFC 7541 C.3.1 should work. | |
| 541 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.3.1 | |
| 542 TEST_P(HpackDecoderTest, BasicC31) { | |
| 543 HpackEncoder encoder(ObtainHpackHuffmanTable()); | |
| 544 | |
| 545 SpdyHeaderBlock expected_header_set; | |
| 546 expected_header_set[":method"] = "GET"; | |
| 547 expected_header_set[":scheme"] = "http"; | |
| 548 expected_header_set[":path"] = "/"; | |
| 549 expected_header_set[":authority"] = "www.example.com"; | |
| 550 | |
| 551 SpdyString encoded_header_set; | |
| 552 EXPECT_TRUE( | |
| 553 encoder.EncodeHeaderSet(expected_header_set, &encoded_header_set)); | |
| 554 | |
| 555 EXPECT_TRUE(DecodeHeaderBlock(encoded_header_set)); | |
| 556 EXPECT_EQ(expected_header_set, decoded_block()); | |
| 557 } | |
| 558 | |
| 559 // RFC 7541, Section C.4: Request Examples with Huffman Coding | |
| 560 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.4 | |
| 561 TEST_P(HpackDecoderTest, SectionC4RequestHuffmanExamples) { | |
| 562 // 82 | == Indexed - Add == | |
| 563 // | idx = 2 | |
| 564 // | -> :method: GET | |
| 565 // 86 | == Indexed - Add == | |
| 566 // | idx = 6 | |
| 567 // | -> :scheme: http | |
| 568 // 84 | == Indexed - Add == | |
| 569 // | idx = 4 | |
| 570 // | -> :path: / | |
| 571 // 41 | == Literal indexed == | |
| 572 // | Indexed name (idx = 1) | |
| 573 // | :authority | |
| 574 // 8c | Literal value (len = 15) | |
| 575 // | Huffman encoded: | |
| 576 // f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k..... | |
| 577 // | Decoded: | |
| 578 // | www.example.com | |
| 579 // | -> :authority: www.example.com | |
| 580 SpdyString first = a2b_hex("828684418cf1e3c2e5f23a6ba0ab90f4ff"); | |
| 581 const SpdyHeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first); | |
| 582 | |
| 583 EXPECT_THAT( | |
| 584 first_header_set, | |
| 585 ElementsAre(Pair(":method", "GET"), Pair(":scheme", "http"), | |
| 586 Pair(":path", "/"), Pair(":authority", "www.example.com"))); | |
| 587 | |
| 588 expectEntry(62, 57, ":authority", "www.example.com"); | |
| 589 EXPECT_EQ(57u, decoder_peer_.header_table()->size()); | |
| 590 | |
| 591 // 82 | == Indexed - Add == | |
| 592 // | idx = 2 | |
| 593 // | -> :method: GET | |
| 594 // 86 | == Indexed - Add == | |
| 595 // | idx = 6 | |
| 596 // | -> :scheme: http | |
| 597 // 84 | == Indexed - Add == | |
| 598 // | idx = 4 | |
| 599 // | -> :path: / | |
| 600 // be | == Indexed - Add == | |
| 601 // | idx = 62 | |
| 602 // | -> :authority: www.example.com | |
| 603 // 58 | == Literal indexed == | |
| 604 // | Indexed name (idx = 24) | |
| 605 // | cache-control | |
| 606 // 86 | Literal value (len = 8) | |
| 607 // | Huffman encoded: | |
| 608 // a8eb 1064 9cbf | ...d.. | |
| 609 // | Decoded: | |
| 610 // | no-cache | |
| 611 // | -> cache-control: no-cache | |
| 612 | |
| 613 SpdyString second = a2b_hex("828684be5886a8eb10649cbf"); | |
| 614 const SpdyHeaderBlock& second_header_set = | |
| 615 DecodeBlockExpectingSuccess(second); | |
| 616 | |
| 617 EXPECT_THAT( | |
| 618 second_header_set, | |
| 619 ElementsAre(Pair(":method", "GET"), Pair(":scheme", "http"), | |
| 620 Pair(":path", "/"), Pair(":authority", "www.example.com"), | |
| 621 Pair("cache-control", "no-cache"))); | |
| 622 | |
| 623 expectEntry(62, 53, "cache-control", "no-cache"); | |
| 624 expectEntry(63, 57, ":authority", "www.example.com"); | |
| 625 EXPECT_EQ(110u, decoder_peer_.header_table()->size()); | |
| 626 | |
| 627 // 82 | == Indexed - Add == | |
| 628 // | idx = 2 | |
| 629 // | -> :method: GET | |
| 630 // 87 | == Indexed - Add == | |
| 631 // | idx = 7 | |
| 632 // | -> :scheme: https | |
| 633 // 85 | == Indexed - Add == | |
| 634 // | idx = 5 | |
| 635 // | -> :path: /index.html | |
| 636 // bf | == Indexed - Add == | |
| 637 // | idx = 63 | |
| 638 // | -> :authority: www.example.com | |
| 639 // 40 | == Literal indexed == | |
| 640 // 88 | Literal name (len = 10) | |
| 641 // | Huffman encoded: | |
| 642 // 25a8 49e9 5ba9 7d7f | %.I.[.}. | |
| 643 // | Decoded: | |
| 644 // | custom-key | |
| 645 // 89 | Literal value (len = 12) | |
| 646 // | Huffman encoded: | |
| 647 // 25a8 49e9 5bb8 e8b4 bf | %.I.[.... | |
| 648 // | Decoded: | |
| 649 // | custom-value | |
| 650 // | -> custom-key: custom-value | |
| 651 SpdyString third = a2b_hex( | |
| 652 "828785bf408825a849e95ba97d7f89" | |
| 653 "25a849e95bb8e8b4bf"); | |
| 654 const SpdyHeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third); | |
| 655 | |
| 656 EXPECT_THAT(third_header_set, | |
| 657 ElementsAre(Pair(":method", "GET"), Pair(":scheme", "https"), | |
| 658 Pair(":path", "/index.html"), | |
| 659 Pair(":authority", "www.example.com"), | |
| 660 Pair("custom-key", "custom-value"))); | |
| 661 | |
| 662 expectEntry(62, 54, "custom-key", "custom-value"); | |
| 663 expectEntry(63, 53, "cache-control", "no-cache"); | |
| 664 expectEntry(64, 57, ":authority", "www.example.com"); | |
| 665 EXPECT_EQ(164u, decoder_peer_.header_table()->size()); | |
| 666 } | |
| 667 | |
| 668 // RFC 7541, Section C.6: Response Examples with Huffman Coding | |
| 669 // http://httpwg.org/specs/rfc7541.html#rfc.section.C.6 | |
| 670 TEST_P(HpackDecoderTest, SectionC6ResponseHuffmanExamples) { | |
| 671 decoder_.ApplyHeaderTableSizeSetting(256); | |
| 672 | |
| 673 // 48 | == Literal indexed == | |
| 674 // | Indexed name (idx = 8) | |
| 675 // | :status | |
| 676 // 82 | Literal value (len = 3) | |
| 677 // | Huffman encoded: | |
| 678 // 6402 | d. | |
| 679 // | Decoded: | |
| 680 // | 302 | |
| 681 // | -> :status: 302 | |
| 682 // 58 | == Literal indexed == | |
| 683 // | Indexed name (idx = 24) | |
| 684 // | cache-control | |
| 685 // 85 | Literal value (len = 7) | |
| 686 // | Huffman encoded: | |
| 687 // aec3 771a 4b | ..w.K | |
| 688 // | Decoded: | |
| 689 // | private | |
| 690 // | -> cache-control: private | |
| 691 // 61 | == Literal indexed == | |
| 692 // | Indexed name (idx = 33) | |
| 693 // | date | |
| 694 // 96 | Literal value (len = 29) | |
| 695 // | Huffman encoded: | |
| 696 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f | |
| 697 // e082 a62d 1bff | ...-.. | |
| 698 // | Decoded: | |
| 699 // | Mon, 21 Oct 2013 20:13:21 | |
| 700 // | GMT | |
| 701 // | -> date: Mon, 21 Oct 2013 | |
| 702 // | 20:13:21 GMT | |
| 703 // 6e | == Literal indexed == | |
| 704 // | Indexed name (idx = 46) | |
| 705 // | location | |
| 706 // 91 | Literal value (len = 23) | |
| 707 // | Huffman encoded: | |
| 708 // 9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 | .)...c.........C | |
| 709 // d3 | . | |
| 710 // | Decoded: | |
| 711 // | https://www.example.com | |
| 712 // | -> location: https://www.e | |
| 713 // | xample.com | |
| 714 | |
| 715 SpdyString first = a2b_hex( | |
| 716 "488264025885aec3771a4b6196d07abe" | |
| 717 "941054d444a8200595040b8166e082a6" | |
| 718 "2d1bff6e919d29ad171863c78f0b97c8" | |
| 719 "e9ae82ae43d3"); | |
| 720 const SpdyHeaderBlock& first_header_set = DecodeBlockExpectingSuccess(first); | |
| 721 | |
| 722 EXPECT_THAT( | |
| 723 first_header_set, | |
| 724 ElementsAre(Pair(":status", "302"), Pair("cache-control", "private"), | |
| 725 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), | |
| 726 Pair("location", "https://www.example.com"))); | |
| 727 | |
| 728 expectEntry(62, 63, "location", "https://www.example.com"); | |
| 729 expectEntry(63, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT"); | |
| 730 expectEntry(64, 52, "cache-control", "private"); | |
| 731 expectEntry(65, 42, ":status", "302"); | |
| 732 EXPECT_EQ(222u, decoder_peer_.header_table()->size()); | |
| 733 | |
| 734 // 48 | == Literal indexed == | |
| 735 // | Indexed name (idx = 8) | |
| 736 // | :status | |
| 737 // 83 | Literal value (len = 3) | |
| 738 // | Huffman encoded: | |
| 739 // 640e ff | d.. | |
| 740 // | Decoded: | |
| 741 // | 307 | |
| 742 // | - evict: :status: 302 | |
| 743 // | -> :status: 307 | |
| 744 // c1 | == Indexed - Add == | |
| 745 // | idx = 65 | |
| 746 // | -> cache-control: private | |
| 747 // c0 | == Indexed - Add == | |
| 748 // | idx = 64 | |
| 749 // | -> date: Mon, 21 Oct 2013 | |
| 750 // | 20:13:21 GMT | |
| 751 // bf | == Indexed - Add == | |
| 752 // | idx = 63 | |
| 753 // | -> location: | |
| 754 // | https://www.example.com | |
| 755 SpdyString second = a2b_hex("4883640effc1c0bf"); | |
| 756 const SpdyHeaderBlock& second_header_set = | |
| 757 DecodeBlockExpectingSuccess(second); | |
| 758 | |
| 759 EXPECT_THAT( | |
| 760 second_header_set, | |
| 761 ElementsAre(Pair(":status", "307"), Pair("cache-control", "private"), | |
| 762 Pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"), | |
| 763 Pair("location", "https://www.example.com"))); | |
| 764 | |
| 765 expectEntry(62, 42, ":status", "307"); | |
| 766 expectEntry(63, 63, "location", "https://www.example.com"); | |
| 767 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:21 GMT"); | |
| 768 expectEntry(65, 52, "cache-control", "private"); | |
| 769 EXPECT_EQ(222u, decoder_peer_.header_table()->size()); | |
| 770 | |
| 771 // 88 | == Indexed - Add == | |
| 772 // | idx = 8 | |
| 773 // | -> :status: 200 | |
| 774 // c1 | == Indexed - Add == | |
| 775 // | idx = 65 | |
| 776 // | -> cache-control: private | |
| 777 // 61 | == Literal indexed == | |
| 778 // | Indexed name (idx = 33) | |
| 779 // | date | |
| 780 // 96 | Literal value (len = 22) | |
| 781 // | Huffman encoded: | |
| 782 // d07a be94 1054 d444 a820 0595 040b 8166 | .z...T.D. .....f | |
| 783 // e084 a62d 1bff | ...-.. | |
| 784 // | Decoded: | |
| 785 // | Mon, 21 Oct 2013 20:13:22 | |
| 786 // | GMT | |
| 787 // | - evict: cache-control: | |
| 788 // | private | |
| 789 // | -> date: Mon, 21 Oct 2013 | |
| 790 // | 20:13:22 GMT | |
| 791 // c0 | == Indexed - Add == | |
| 792 // | idx = 64 | |
| 793 // | -> location: | |
| 794 // | https://www.example.com | |
| 795 // 5a | == Literal indexed == | |
| 796 // | Indexed name (idx = 26) | |
| 797 // | content-encoding | |
| 798 // 83 | Literal value (len = 3) | |
| 799 // | Huffman encoded: | |
| 800 // 9bd9 ab | ... | |
| 801 // | Decoded: | |
| 802 // | gzip | |
| 803 // | - evict: date: Mon, 21 Oct | |
| 804 // | 2013 20:13:21 GMT | |
| 805 // | -> content-encoding: gzip | |
| 806 // 77 | == Literal indexed == | |
| 807 // | Indexed name (idx = 55) | |
| 808 // | set-cookie | |
| 809 // ad | Literal value (len = 45) | |
| 810 // | Huffman encoded: | |
| 811 // 94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 | .........5...[9` | |
| 812 // d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 | ..'..6r..'..)... | |
| 813 // 3160 65c0 03ed 4ee5 b106 3d50 07 | 1`e...N...=P. | |
| 814 // | Decoded: | |
| 815 // | foo=ASDJKHQKBZXOQWEOPIUAXQ | |
| 816 // | WEOIU; max-age=3600; versi | |
| 817 // | on=1 | |
| 818 // | - evict: location: | |
| 819 // | https://www.example.com | |
| 820 // | - evict: :status: 307 | |
| 821 // | -> set-cookie: foo=ASDJKHQ | |
| 822 // | KBZXOQWEOPIUAXQWEOIU; | |
| 823 // | max-age=3600; version=1 | |
| 824 SpdyString third = a2b_hex( | |
| 825 "88c16196d07abe941054d444a8200595" | |
| 826 "040b8166e084a62d1bffc05a839bd9ab" | |
| 827 "77ad94e7821dd7f2e6c7b335dfdfcd5b" | |
| 828 "3960d5af27087f3672c1ab270fb5291f" | |
| 829 "9587316065c003ed4ee5b1063d5007"); | |
| 830 const SpdyHeaderBlock& third_header_set = DecodeBlockExpectingSuccess(third); | |
| 831 | |
| 832 EXPECT_THAT( | |
| 833 third_header_set, | |
| 834 ElementsAre(Pair(":status", "200"), Pair("cache-control", "private"), | |
| 835 Pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"), | |
| 836 Pair("location", "https://www.example.com"), | |
| 837 Pair("content-encoding", "gzip"), | |
| 838 Pair("set-cookie", | |
| 839 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" | |
| 840 " max-age=3600; version=1"))); | |
| 841 | |
| 842 expectEntry(62, 98, "set-cookie", | |
| 843 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" | |
| 844 " max-age=3600; version=1"); | |
| 845 expectEntry(63, 52, "content-encoding", "gzip"); | |
| 846 expectEntry(64, 65, "date", "Mon, 21 Oct 2013 20:13:22 GMT"); | |
| 847 EXPECT_EQ(215u, decoder_peer_.header_table()->size()); | |
| 848 } | |
| 849 | |
| 850 } // namespace | |
| 851 } // namespace test | |
| 852 } // namespace net | |
| OLD | NEW |