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