| // Copyright 2017 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "http2/hpack/decoder/hpack_decoder.h" | 
 |  | 
 | // Tests of HpackDecoder. | 
 |  | 
 | #include <string> | 
 | #include <tuple> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "http2/decoder/decode_buffer.h" | 
 | #include "http2/hpack/decoder/hpack_decoder_listener.h" | 
 | #include "http2/hpack/decoder/hpack_decoder_state.h" | 
 | #include "http2/hpack/decoder/hpack_decoder_tables.h" | 
 | #include "http2/hpack/http2_hpack_constants.h" | 
 | #include "http2/hpack/tools/hpack_block_builder.h" | 
 | #include "http2/hpack/tools/hpack_example.h" | 
 | #include "http2/http2_constants.h" | 
 | #include "http2/platform/api/http2_logging.h" | 
 | #include "http2/platform/api/http2_test_helpers.h" | 
 | #include "http2/test_tools/http2_random.h" | 
 | #include "http2/tools/random_util.h" | 
 | #include "common/platform/api/quiche_test.h" | 
 |  | 
 | using ::testing::AssertionFailure; | 
 | using ::testing::AssertionResult; | 
 | using ::testing::AssertionSuccess; | 
 | using ::testing::ElementsAreArray; | 
 | using ::testing::Eq; | 
 |  | 
 | namespace http2 { | 
 | namespace test { | 
 | class HpackDecoderStatePeer { | 
 |  public: | 
 |   static HpackDecoderTables* GetDecoderTables(HpackDecoderState* state) { | 
 |     return &state->decoder_tables_; | 
 |   } | 
 |   static void set_listener(HpackDecoderState* state, | 
 |                            HpackDecoderListener* listener) { | 
 |     state->listener_ = listener; | 
 |   } | 
 | }; | 
 | class HpackDecoderPeer { | 
 |  public: | 
 |   static HpackDecoderState* GetDecoderState(HpackDecoder* decoder) { | 
 |     return &decoder->decoder_state_; | 
 |   } | 
 |   static HpackDecoderTables* GetDecoderTables(HpackDecoder* decoder) { | 
 |     return HpackDecoderStatePeer::GetDecoderTables(GetDecoderState(decoder)); | 
 |   } | 
 | }; | 
 |  | 
 | namespace { | 
 |  | 
 | typedef std::pair<std::string, std::string> HpackHeaderEntry; | 
 | typedef std::vector<HpackHeaderEntry> HpackHeaderEntries; | 
 |  | 
 | // TODO(jamessynge): Create a ...test_utils.h file with the mock listener | 
 | // and with VerifyDynamicTableContents. | 
 | class MockHpackDecoderListener : public HpackDecoderListener { | 
 |  public: | 
 |   MOCK_METHOD(void, OnHeaderListStart, (), (override)); | 
 |   MOCK_METHOD(void, | 
 |               OnHeader, | 
 |               (const std::string& name, const std::string& value), | 
 |               (override)); | 
 |   MOCK_METHOD(void, OnHeaderListEnd, (), (override)); | 
 |   MOCK_METHOD(void, | 
 |               OnHeaderErrorDetected, | 
 |               (absl::string_view error_message), | 
 |               (override)); | 
 | }; | 
 |  | 
 | class HpackDecoderTest : public QuicheTestWithParam<bool>, | 
 |                          public HpackDecoderListener { | 
 |  protected: | 
 |   // Note that we initialize the random number generator with the same seed | 
 |   // for each individual test, therefore the order in which the tests are | 
 |   // executed does not effect the sequence produced by the RNG within any | 
 |   // one test. | 
 |   HpackDecoderTest() : decoder_(this, 4096) { | 
 |     fragment_the_hpack_block_ = GetParam(); | 
 |   } | 
 |   ~HpackDecoderTest() override = default; | 
 |  | 
 |   void OnHeaderListStart() override { | 
 |     ASSERT_FALSE(saw_start_); | 
 |     ASSERT_FALSE(saw_end_); | 
 |     saw_start_ = true; | 
 |     header_entries_.clear(); | 
 |   } | 
 |  | 
 |   // Called for each header name-value pair that is decoded, in the order they | 
 |   // appear in the HPACK block. Multiple values for a given key will be emitted | 
 |   // as multiple calls to OnHeader. | 
 |   void OnHeader(const std::string& name, const std::string& value) override { | 
 |     ASSERT_TRUE(saw_start_); | 
 |     ASSERT_FALSE(saw_end_); | 
 |     header_entries_.emplace_back(name, value); | 
 |   } | 
 |  | 
 |   // OnHeaderBlockEnd is called after successfully decoding an HPACK block. Will | 
 |   // only be called once per block, even if it extends into CONTINUATION frames. | 
 |   // A callback method which notifies when the parser finishes handling a | 
 |   // header block (i.e. the containing frame has the END_STREAM flag set). | 
 |   // Also indicates the total number of bytes in this block. | 
 |   void OnHeaderListEnd() override { | 
 |     ASSERT_TRUE(saw_start_); | 
 |     ASSERT_FALSE(saw_end_); | 
 |     ASSERT_TRUE(error_messages_.empty()); | 
 |     saw_end_ = true; | 
 |   } | 
 |  | 
 |   // OnHeaderErrorDetected is called if an error is detected while decoding. | 
 |   // error_message may be used in a GOAWAY frame as the Opaque Data. | 
 |   void OnHeaderErrorDetected(absl::string_view error_message) override { | 
 |     ASSERT_TRUE(saw_start_); | 
 |     error_messages_.push_back(std::string(error_message)); | 
 |     // No further callbacks should be made at this point, so replace 'this' as | 
 |     // the listener with mock_listener_, which is a strict mock, so will | 
 |     // generate an error for any calls. | 
 |     HpackDecoderStatePeer::set_listener( | 
 |         HpackDecoderPeer::GetDecoderState(&decoder_), &mock_listener_); | 
 |   } | 
 |  | 
 |   AssertionResult DecodeBlock(absl::string_view block) { | 
 |     HTTP2_VLOG(1) << "HpackDecoderTest::DecodeBlock"; | 
 |  | 
 |     VERIFY_FALSE(decoder_.DetectError()); | 
 |     VERIFY_TRUE(error_messages_.empty()); | 
 |     VERIFY_FALSE(saw_start_); | 
 |     VERIFY_FALSE(saw_end_); | 
 |     header_entries_.clear(); | 
 |  | 
 |     VERIFY_FALSE(decoder_.DetectError()); | 
 |     VERIFY_TRUE(decoder_.StartDecodingBlock()); | 
 |     VERIFY_FALSE(decoder_.DetectError()); | 
 |  | 
 |     if (fragment_the_hpack_block_) { | 
 |       // See note in ctor regarding RNG. | 
 |       while (!block.empty()) { | 
 |         size_t fragment_size = random_.RandomSizeSkewedLow(block.size()); | 
 |         DecodeBuffer db(block.substr(0, fragment_size)); | 
 |         VERIFY_TRUE(decoder_.DecodeFragment(&db)); | 
 |         VERIFY_EQ(0u, db.Remaining()); | 
 |         block.remove_prefix(fragment_size); | 
 |       } | 
 |     } else { | 
 |       DecodeBuffer db(block); | 
 |       VERIFY_TRUE(decoder_.DecodeFragment(&db)); | 
 |       VERIFY_EQ(0u, db.Remaining()); | 
 |     } | 
 |     VERIFY_FALSE(decoder_.DetectError()); | 
 |  | 
 |     VERIFY_TRUE(decoder_.EndDecodingBlock()); | 
 |     if (saw_end_) { | 
 |       VERIFY_FALSE(decoder_.DetectError()); | 
 |       VERIFY_TRUE(error_messages_.empty()); | 
 |     } else { | 
 |       VERIFY_TRUE(decoder_.DetectError()); | 
 |       VERIFY_FALSE(error_messages_.empty()); | 
 |     } | 
 |  | 
 |     saw_start_ = saw_end_ = false; | 
 |     return AssertionSuccess(); | 
 |   } | 
 |  | 
 |   const HpackDecoderTables& GetDecoderTables() { | 
 |     return *HpackDecoderPeer::GetDecoderTables(&decoder_); | 
 |   } | 
 |   const HpackStringPair* Lookup(size_t index) { | 
 |     return GetDecoderTables().Lookup(index); | 
 |   } | 
 |   size_t current_header_table_size() { | 
 |     return GetDecoderTables().current_header_table_size(); | 
 |   } | 
 |   size_t header_table_size_limit() { | 
 |     return GetDecoderTables().header_table_size_limit(); | 
 |   } | 
 |   void set_header_table_size_limit(size_t size) { | 
 |     HpackDecoderPeer::GetDecoderTables(&decoder_)->DynamicTableSizeUpdate(size); | 
 |   } | 
 |  | 
 |   // dynamic_index is one-based, because that is the way RFC 7541 shows it. | 
 |   AssertionResult VerifyEntry(size_t dynamic_index, | 
 |                               const char* name, | 
 |                               const char* value) { | 
 |     const HpackStringPair* entry = | 
 |         Lookup(dynamic_index + kFirstDynamicTableIndex - 1); | 
 |     VERIFY_NE(entry, nullptr); | 
 |     VERIFY_EQ(entry->name, name); | 
 |     VERIFY_EQ(entry->value, value); | 
 |     return AssertionSuccess(); | 
 |   } | 
 |   AssertionResult VerifyNoEntry(size_t dynamic_index) { | 
 |     const HpackStringPair* entry = | 
 |         Lookup(dynamic_index + kFirstDynamicTableIndex - 1); | 
 |     VERIFY_EQ(entry, nullptr); | 
 |     return AssertionSuccess(); | 
 |   } | 
 |   AssertionResult VerifyDynamicTableContents( | 
 |       const std::vector<std::pair<const char*, const char*>>& entries) { | 
 |     size_t index = 1; | 
 |     for (const auto& entry : entries) { | 
 |       VERIFY_SUCCESS(VerifyEntry(index, entry.first, entry.second)); | 
 |       ++index; | 
 |     } | 
 |     VERIFY_SUCCESS(VerifyNoEntry(index)); | 
 |     return AssertionSuccess(); | 
 |   } | 
 |  | 
 |   Http2Random random_; | 
 |   HpackDecoder decoder_; | 
 |   testing::StrictMock<MockHpackDecoderListener> mock_listener_; | 
 |   HpackHeaderEntries header_entries_; | 
 |   std::vector<std::string> error_messages_; | 
 |   bool fragment_the_hpack_block_; | 
 |   bool saw_start_ = false; | 
 |   bool saw_end_ = false; | 
 | }; | 
 | INSTANTIATE_TEST_SUITE_P(AllWays, HpackDecoderTest, ::testing::Bool()); | 
 |  | 
 | // Test based on RFC 7541, section C.3: Request Examples without Huffman Coding. | 
 | // This section shows several consecutive header lists, corresponding to HTTP | 
 | // requests, on the same connection. | 
 | // http://httpwg.org/specs/rfc7541.html#rfc.section.C.3 | 
 | TEST_P(HpackDecoderTest, C3_RequestExamples) { | 
 |   // C.3.1 First Request | 
 |   std::string hpack_block = HpackExampleToStringOrDie(R"( | 
 |       82                                      | == Indexed - Add == | 
 |                                               |   idx = 2 | 
 |                                               | -> :method: GET | 
 |       86                                      | == Indexed - Add == | 
 |                                               |   idx = 6 | 
 |                                               | -> :scheme: http | 
 |       84                                      | == Indexed - Add == | 
 |                                               |   idx = 4 | 
 |                                               | -> :path: / | 
 |       41                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 1) | 
 |                                               |     :authority | 
 |       0f                                      |   Literal value (len = 15) | 
 |       7777 772e 6578 616d 706c 652e 636f 6d   | www.example.com | 
 |                                               | -> :authority: | 
 |                                               |   www.example.com | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT(header_entries_, | 
 |               ElementsAreArray({ | 
 |                   HpackHeaderEntry{":method", "GET"}, | 
 |                   HpackHeaderEntry{":scheme", "http"}, | 
 |                   HpackHeaderEntry{":path", "/"}, | 
 |                   HpackHeaderEntry{":authority", "www.example.com"}, | 
 |               })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  57) :authority: www.example.com | 
 |   //         Table size:  57 | 
 |   ASSERT_TRUE(VerifyDynamicTableContents({{":authority", "www.example.com"}})); | 
 |   ASSERT_EQ(57u, current_header_table_size()); | 
 |  | 
 |   // C.3.2 Second Request | 
 |   hpack_block = HpackExampleToStringOrDie(R"( | 
 |       82                                      | == Indexed - Add == | 
 |                                               |   idx = 2 | 
 |                                               | -> :method: GET | 
 |       86                                      | == Indexed - Add == | 
 |                                               |   idx = 6 | 
 |                                               | -> :scheme: http | 
 |       84                                      | == Indexed - Add == | 
 |                                               |   idx = 4 | 
 |                                               | -> :path: / | 
 |       be                                      | == Indexed - Add == | 
 |                                               |   idx = 62 | 
 |                                               | -> :authority: | 
 |                                               |   www.example.com | 
 |       58                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 24) | 
 |                                               |     cache-control | 
 |       08                                      |   Literal value (len = 8) | 
 |       6e6f 2d63 6163 6865                     | no-cache | 
 |                                               | -> cache-control: no-cache | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT(header_entries_, | 
 |               ElementsAreArray({ | 
 |                   HpackHeaderEntry{":method", "GET"}, | 
 |                   HpackHeaderEntry{":scheme", "http"}, | 
 |                   HpackHeaderEntry{":path", "/"}, | 
 |                   HpackHeaderEntry{":authority", "www.example.com"}, | 
 |                   HpackHeaderEntry{"cache-control", "no-cache"}, | 
 |               })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  53) cache-control: no-cache | 
 |   //   [  2] (s =  57) :authority: www.example.com | 
 |   //         Table size: 110 | 
 |   ASSERT_TRUE(VerifyDynamicTableContents( | 
 |       {{"cache-control", "no-cache"}, {":authority", "www.example.com"}})); | 
 |   ASSERT_EQ(110u, current_header_table_size()); | 
 |  | 
 |   // C.3.2 Third Request | 
 |   hpack_block = HpackExampleToStringOrDie(R"( | 
 |       82                                      | == Indexed - Add == | 
 |                                               |   idx = 2 | 
 |                                               | -> :method: GET | 
 |       87                                      | == Indexed - Add == | 
 |                                               |   idx = 7 | 
 |                                               | -> :scheme: https | 
 |       85                                      | == Indexed - Add == | 
 |                                               |   idx = 5 | 
 |                                               | -> :path: /index.html | 
 |       bf                                      | == Indexed - Add == | 
 |                                               |   idx = 63 | 
 |                                               | -> :authority: | 
 |                                               |   www.example.com | 
 |       40                                      | == Literal indexed == | 
 |       0a                                      |   Literal name (len = 10) | 
 |       6375 7374 6f6d 2d6b 6579                | custom-key | 
 |       0c                                      |   Literal value (len = 12) | 
 |       6375 7374 6f6d 2d76 616c 7565           | custom-value | 
 |                                               | -> custom-key: | 
 |                                               |   custom-value | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT(header_entries_, | 
 |               ElementsAreArray({ | 
 |                   HpackHeaderEntry{":method", "GET"}, | 
 |                   HpackHeaderEntry{":scheme", "https"}, | 
 |                   HpackHeaderEntry{":path", "/index.html"}, | 
 |                   HpackHeaderEntry{":authority", "www.example.com"}, | 
 |                   HpackHeaderEntry{"custom-key", "custom-value"}, | 
 |               })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  54) custom-key: custom-value | 
 |   //   [  2] (s =  53) cache-control: no-cache | 
 |   //   [  3] (s =  57) :authority: www.example.com | 
 |   //         Table size: 164 | 
 |   ASSERT_TRUE(VerifyDynamicTableContents({{"custom-key", "custom-value"}, | 
 |                                           {"cache-control", "no-cache"}, | 
 |                                           {":authority", "www.example.com"}})); | 
 |   ASSERT_EQ(164u, current_header_table_size()); | 
 | } | 
 |  | 
 | // Test based on RFC 7541, section C.4 Request Examples with Huffman Coding. | 
 | // This section shows the same examples as the previous section but uses | 
 | // Huffman encoding for the literal values. | 
 | // http://httpwg.org/specs/rfc7541.html#rfc.section.C.4 | 
 | TEST_P(HpackDecoderTest, C4_RequestExamplesWithHuffmanEncoding) { | 
 |   // C.4.1 First Request | 
 |   std::string hpack_block = HpackExampleToStringOrDie(R"( | 
 |       82                                      | == Indexed - Add == | 
 |                                               |   idx = 2 | 
 |                                               | -> :method: GET | 
 |       86                                      | == Indexed - Add == | 
 |                                               |   idx = 6 | 
 |                                               | -> :scheme: http | 
 |       84                                      | == Indexed - Add == | 
 |                                               |   idx = 4 | 
 |                                               | -> :path: / | 
 |       41                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 1) | 
 |                                               |     :authority | 
 |       8c                                      |   Literal value (len = 12) | 
 |                                               |     Huffman encoded: | 
 |       f1e3 c2e5 f23a 6ba0 ab90 f4ff           | .....:k..... | 
 |                                               |     Decoded: | 
 |                                               | www.example.com | 
 |                                               | -> :authority: | 
 |                                               |   www.example.com | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT(header_entries_, | 
 |               ElementsAreArray({ | 
 |                   HpackHeaderEntry{":method", "GET"}, | 
 |                   HpackHeaderEntry{":scheme", "http"}, | 
 |                   HpackHeaderEntry{":path", "/"}, | 
 |                   HpackHeaderEntry{":authority", "www.example.com"}, | 
 |               })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  57) :authority: www.example.com | 
 |   //         Table size:  57 | 
 |   ASSERT_TRUE(VerifyDynamicTableContents({{":authority", "www.example.com"}})); | 
 |   ASSERT_EQ(57u, current_header_table_size()); | 
 |  | 
 |   // C.4.2 Second Request | 
 |   hpack_block = HpackExampleToStringOrDie(R"( | 
 |       82                                      | == Indexed - Add == | 
 |                                               |   idx = 2 | 
 |                                               | -> :method: GET | 
 |       86                                      | == Indexed - Add == | 
 |                                               |   idx = 6 | 
 |                                               | -> :scheme: http | 
 |       84                                      | == Indexed - Add == | 
 |                                               |   idx = 4 | 
 |                                               | -> :path: / | 
 |       be                                      | == Indexed - Add == | 
 |                                               |   idx = 62 | 
 |                                               | -> :authority: | 
 |                                               |   www.example.com | 
 |       58                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 24) | 
 |                                               |     cache-control | 
 |       86                                      |   Literal value (len = 6) | 
 |                                               |     Huffman encoded: | 
 |       a8eb 1064 9cbf                          | ...d.. | 
 |                                               |     Decoded: | 
 |                                               | no-cache | 
 |                                               | -> cache-control: no-cache | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT(header_entries_, | 
 |               ElementsAreArray({ | 
 |                   HpackHeaderEntry{":method", "GET"}, | 
 |                   HpackHeaderEntry{":scheme", "http"}, | 
 |                   HpackHeaderEntry{":path", "/"}, | 
 |                   HpackHeaderEntry{":authority", "www.example.com"}, | 
 |                   HpackHeaderEntry{"cache-control", "no-cache"}, | 
 |               })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  53) cache-control: no-cache | 
 |   //   [  2] (s =  57) :authority: www.example.com | 
 |   //         Table size: 110 | 
 |   ASSERT_TRUE(VerifyDynamicTableContents( | 
 |       {{"cache-control", "no-cache"}, {":authority", "www.example.com"}})); | 
 |   ASSERT_EQ(110u, current_header_table_size()); | 
 |  | 
 |   // C.4.2 Third Request | 
 |   hpack_block = HpackExampleToStringOrDie(R"( | 
 |     82                                      | == Indexed - Add == | 
 |                                             |   idx = 2 | 
 |                                             | -> :method: GET | 
 |     87                                      | == Indexed - Add == | 
 |                                             |   idx = 7 | 
 |                                             | -> :scheme: https | 
 |     85                                      | == Indexed - Add == | 
 |                                             |   idx = 5 | 
 |                                             | -> :path: /index.html | 
 |     bf                                      | == Indexed - Add == | 
 |                                             |   idx = 63 | 
 |                                             | -> :authority: | 
 |                                             |   www.example.com | 
 |     40                                      | == Literal indexed == | 
 |     88                                      |   Literal name (len = 8) | 
 |                                             |     Huffman encoded: | 
 |     25a8 49e9 5ba9 7d7f                     | %.I.[.}. | 
 |                                             |     Decoded: | 
 |                                             | custom-key | 
 |     89                                      |   Literal value (len = 9) | 
 |                                             |     Huffman encoded: | 
 |     25a8 49e9 5bb8 e8b4 bf                  | %.I.[.... | 
 |                                             |     Decoded: | 
 |                                             | custom-value | 
 |                                             | -> custom-key: | 
 |                                             |   custom-value | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT(header_entries_, | 
 |               ElementsAreArray({ | 
 |                   HpackHeaderEntry{":method", "GET"}, | 
 |                   HpackHeaderEntry{":scheme", "https"}, | 
 |                   HpackHeaderEntry{":path", "/index.html"}, | 
 |                   HpackHeaderEntry{":authority", "www.example.com"}, | 
 |                   HpackHeaderEntry{"custom-key", "custom-value"}, | 
 |               })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  54) custom-key: custom-value | 
 |   //   [  2] (s =  53) cache-control: no-cache | 
 |   //   [  3] (s =  57) :authority: www.example.com | 
 |   //         Table size: 164 | 
 |   ASSERT_TRUE(VerifyDynamicTableContents({{"custom-key", "custom-value"}, | 
 |                                           {"cache-control", "no-cache"}, | 
 |                                           {":authority", "www.example.com"}})); | 
 |   ASSERT_EQ(164u, current_header_table_size()); | 
 | } | 
 |  | 
 | // Test based on RFC 7541, section C.5: Response Examples without Huffman | 
 | // Coding. This section shows several consecutive header lists, corresponding | 
 | // to HTTP responses, on the same connection. The HTTP/2 setting parameter | 
 | // SETTINGS_HEADER_TABLE_SIZE is set to the value of 256 octets, causing | 
 | // some evictions to occur. | 
 | // http://httpwg.org/specs/rfc7541.html#rfc.section.C.5 | 
 | TEST_P(HpackDecoderTest, C5_ResponseExamples) { | 
 |   set_header_table_size_limit(256); | 
 |  | 
 |   // C.5.1 First Response | 
 |   // | 
 |   // Header list to encode: | 
 |   // | 
 |   //   :status: 302 | 
 |   //   cache-control: private | 
 |   //   date: Mon, 21 Oct 2013 20:13:21 GMT | 
 |   //   location: https://www.example.com | 
 |  | 
 |   std::string hpack_block = HpackExampleToStringOrDie(R"( | 
 |       48                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 8) | 
 |                                               |     :status | 
 |       03                                      |   Literal value (len = 3) | 
 |       3330 32                                 | 302 | 
 |                                               | -> :status: 302 | 
 |       58                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 24) | 
 |                                               |     cache-control | 
 |       07                                      |   Literal value (len = 7) | 
 |       7072 6976 6174 65                       | private | 
 |                                               | -> cache-control: private | 
 |       61                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 33) | 
 |                                               |     date | 
 |       1d                                      |   Literal value (len = 29) | 
 |       4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013 | 
 |       2032 303a 3133 3a32 3120 474d 54        |  20:13:21 GMT | 
 |                                               | -> date: Mon, 21 Oct 2013 | 
 |                                               |   20:13:21 GMT | 
 |       6e                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 46) | 
 |                                               |     location | 
 |       17                                      |   Literal value (len = 23) | 
 |       6874 7470 733a 2f2f 7777 772e 6578 616d | https://www.exam | 
 |       706c 652e 636f 6d                       | ple.com | 
 |                                               | -> location: | 
 |                                               |   https://www.example.com | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT(header_entries_, | 
 |               ElementsAreArray({ | 
 |                   HpackHeaderEntry{":status", "302"}, | 
 |                   HpackHeaderEntry{"cache-control", "private"}, | 
 |                   HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:21 GMT"}, | 
 |                   HpackHeaderEntry{"location", "https://www.example.com"}, | 
 |               })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  63) location: https://www.example.com | 
 |   //   [  2] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT | 
 |   //   [  3] (s =  52) cache-control: private | 
 |   //   [  4] (s =  42) :status: 302 | 
 |   //         Table size: 222 | 
 |   ASSERT_TRUE( | 
 |       VerifyDynamicTableContents({{"location", "https://www.example.com"}, | 
 |                                   {"date", "Mon, 21 Oct 2013 20:13:21 GMT"}, | 
 |                                   {"cache-control", "private"}, | 
 |                                   {":status", "302"}})); | 
 |   ASSERT_EQ(222u, current_header_table_size()); | 
 |  | 
 |   // C.5.2 Second Response | 
 |   // | 
 |   // The (":status", "302") header field is evicted from the dynamic table to | 
 |   // free space to allow adding the (":status", "307") header field. | 
 |   // | 
 |   // Header list to encode: | 
 |   // | 
 |   //   :status: 307 | 
 |   //   cache-control: private | 
 |   //   date: Mon, 21 Oct 2013 20:13:21 GMT | 
 |   //   location: https://www.example.com | 
 |  | 
 |   hpack_block = HpackExampleToStringOrDie(R"( | 
 |       48                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 8) | 
 |                                               |     :status | 
 |       03                                      |   Literal value (len = 3) | 
 |       3330 37                                 | 307 | 
 |                                               | - evict: :status: 302 | 
 |                                               | -> :status: 307 | 
 |       c1                                      | == Indexed - Add == | 
 |                                               |   idx = 65 | 
 |                                               | -> cache-control: private | 
 |       c0                                      | == Indexed - Add == | 
 |                                               |   idx = 64 | 
 |                                               | -> date: Mon, 21 Oct 2013 | 
 |                                               |   20:13:21 GMT | 
 |       bf                                      | == Indexed - Add == | 
 |                                               |   idx = 63 | 
 |                                               | -> location: | 
 |                                               |   https://www.example.com | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT(header_entries_, | 
 |               ElementsAreArray({ | 
 |                   HpackHeaderEntry{":status", "307"}, | 
 |                   HpackHeaderEntry{"cache-control", "private"}, | 
 |                   HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:21 GMT"}, | 
 |                   HpackHeaderEntry{"location", "https://www.example.com"}, | 
 |               })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  42) :status: 307 | 
 |   //   [  2] (s =  63) location: https://www.example.com | 
 |   //   [  3] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT | 
 |   //   [  4] (s =  52) cache-control: private | 
 |   //         Table size: 222 | 
 |  | 
 |   ASSERT_TRUE( | 
 |       VerifyDynamicTableContents({{":status", "307"}, | 
 |                                   {"location", "https://www.example.com"}, | 
 |                                   {"date", "Mon, 21 Oct 2013 20:13:21 GMT"}, | 
 |                                   {"cache-control", "private"}})); | 
 |   ASSERT_EQ(222u, current_header_table_size()); | 
 |  | 
 |   // C.5.3 Third Response | 
 |   // | 
 |   // Several header fields are evicted from the dynamic table during the | 
 |   // processing of this header list. | 
 |   // | 
 |   // Header list to encode: | 
 |   // | 
 |   //   :status: 200 | 
 |   //   cache-control: private | 
 |   //   date: Mon, 21 Oct 2013 20:13:22 GMT | 
 |   //   location: https://www.example.com | 
 |   //   content-encoding: gzip | 
 |   //   set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1 | 
 |   hpack_block = HpackExampleToStringOrDie(R"( | 
 |       88                                      | == Indexed - Add == | 
 |                                               |   idx = 8 | 
 |                                               | -> :status: 200 | 
 |       c1                                      | == Indexed - Add == | 
 |                                               |   idx = 65 | 
 |                                               | -> cache-control: private | 
 |       61                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 33) | 
 |                                               |     date | 
 |       1d                                      |   Literal value (len = 29) | 
 |       4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013 | 
 |       2032 303a 3133 3a32 3220 474d 54        |  20:13:22 GMT | 
 |                                               | - evict: cache-control: | 
 |                                               |   private | 
 |                                               | -> date: Mon, 21 Oct 2013 | 
 |                                               |   20:13:22 GMT | 
 |       c0                                      | == Indexed - Add == | 
 |                                               |   idx = 64 | 
 |                                               | -> location: | 
 |                                               |   https://www.example.com | 
 |       5a                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 26) | 
 |                                               |     content-encoding | 
 |       04                                      |   Literal value (len = 4) | 
 |       677a 6970                               | gzip | 
 |                                               | - evict: date: Mon, 21 Oct | 
 |                                               |    2013 20:13:21 GMT | 
 |                                               | -> content-encoding: gzip | 
 |       77                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 55) | 
 |                                               |     set-cookie | 
 |       38                                      |   Literal value (len = 56) | 
 |       666f 6f3d 4153 444a 4b48 514b 425a 584f | foo=ASDJKHQKBZXO | 
 |       5157 454f 5049 5541 5851 5745 4f49 553b | QWEOPIUAXQWEOIU; | 
 |       206d 6178 2d61 6765 3d33 3630 303b 2076 |  max-age=3600; v | 
 |       6572 7369 6f6e 3d31                     | ersion=1 | 
 |                                               | - evict: location: | 
 |                                               |   https://www.example.com | 
 |                                               | - evict: :status: 307 | 
 |                                               | -> set-cookie: foo=ASDJKHQ | 
 |                                               |   KBZXOQWEOPIUAXQWEOIU; ma | 
 |                                               |   x-age=3600; version=1 | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT( | 
 |       header_entries_, | 
 |       ElementsAreArray({ | 
 |           HpackHeaderEntry{":status", "200"}, | 
 |           HpackHeaderEntry{"cache-control", "private"}, | 
 |           HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:22 GMT"}, | 
 |           HpackHeaderEntry{"location", "https://www.example.com"}, | 
 |           HpackHeaderEntry{"content-encoding", "gzip"}, | 
 |           HpackHeaderEntry{ | 
 |               "set-cookie", | 
 |               "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"}, | 
 |       })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; | 
 |   //                    max-age=3600; version=1 | 
 |   //   [  2] (s =  52) content-encoding: gzip | 
 |   //   [  3] (s =  65) date: Mon, 21 Oct 2013 20:13:22 GMT | 
 |   //         Table size: 215 | 
 |   ASSERT_TRUE(VerifyDynamicTableContents( | 
 |       {{"set-cookie", | 
 |         "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"}, | 
 |        {"content-encoding", "gzip"}, | 
 |        {"date", "Mon, 21 Oct 2013 20:13:22 GMT"}})); | 
 |   ASSERT_EQ(215u, current_header_table_size()); | 
 | } | 
 |  | 
 | // Test based on RFC 7541, section C.6: Response Examples with Huffman Coding. | 
 | // This section shows the same examples as the previous section but uses Huffman | 
 | // encoding for the literal values. The HTTP/2 setting parameter | 
 | // SETTINGS_HEADER_TABLE_SIZE is set to the value of 256 octets, causing some | 
 | // evictions to occur. The eviction mechanism uses the length of the decoded | 
 | // literal values, so the same evictions occur as in the previous section. | 
 | // http://httpwg.org/specs/rfc7541.html#rfc.section.C.6 | 
 | TEST_P(HpackDecoderTest, C6_ResponseExamplesWithHuffmanEncoding) { | 
 |   set_header_table_size_limit(256); | 
 |  | 
 |   // C.5.1 First Response | 
 |   // | 
 |   // Header list to encode: | 
 |   // | 
 |   //   :status: 302 | 
 |   //   cache-control: private | 
 |   //   date: Mon, 21 Oct 2013 20:13:21 GMT | 
 |   //   location: https://www.example.com | 
 |   std::string hpack_block = HpackExampleToStringOrDie(R"( | 
 |       48                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 8) | 
 |                                               |     :status | 
 |       03                                      |   Literal value (len = 3) | 
 |       3330 32                                 | 302 | 
 |                                               | -> :status: 302 | 
 |       58                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 24) | 
 |                                               |     cache-control | 
 |       07                                      |   Literal value (len = 7) | 
 |       7072 6976 6174 65                       | private | 
 |                                               | -> cache-control: private | 
 |       61                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 33) | 
 |                                               |     date | 
 |       1d                                      |   Literal value (len = 29) | 
 |       4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013 | 
 |       2032 303a 3133 3a32 3120 474d 54        |  20:13:21 GMT | 
 |                                               | -> date: Mon, 21 Oct 2013 | 
 |                                               |   20:13:21 GMT | 
 |       6e                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 46) | 
 |                                               |     location | 
 |       17                                      |   Literal value (len = 23) | 
 |       6874 7470 733a 2f2f 7777 772e 6578 616d | https://www.exam | 
 |       706c 652e 636f 6d                       | ple.com | 
 |                                               | -> location: | 
 |                                               |   https://www.example.com | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT(header_entries_, | 
 |               ElementsAreArray({ | 
 |                   HpackHeaderEntry{":status", "302"}, | 
 |                   HpackHeaderEntry{"cache-control", "private"}, | 
 |                   HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:21 GMT"}, | 
 |                   HpackHeaderEntry{"location", "https://www.example.com"}, | 
 |               })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  63) location: https://www.example.com | 
 |   //   [  2] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT | 
 |   //   [  3] (s =  52) cache-control: private | 
 |   //   [  4] (s =  42) :status: 302 | 
 |   //         Table size: 222 | 
 |   ASSERT_TRUE( | 
 |       VerifyDynamicTableContents({{"location", "https://www.example.com"}, | 
 |                                   {"date", "Mon, 21 Oct 2013 20:13:21 GMT"}, | 
 |                                   {"cache-control", "private"}, | 
 |                                   {":status", "302"}})); | 
 |   ASSERT_EQ(222u, current_header_table_size()); | 
 |  | 
 |   // C.5.2 Second Response | 
 |   // | 
 |   // The (":status", "302") header field is evicted from the dynamic table to | 
 |   // free space to allow adding the (":status", "307") header field. | 
 |   // | 
 |   // Header list to encode: | 
 |   // | 
 |   //   :status: 307 | 
 |   //   cache-control: private | 
 |   //   date: Mon, 21 Oct 2013 20:13:21 GMT | 
 |   //   location: https://www.example.com | 
 |   hpack_block = HpackExampleToStringOrDie(R"( | 
 |       48                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 8) | 
 |                                               |     :status | 
 |       03                                      |   Literal value (len = 3) | 
 |       3330 37                                 | 307 | 
 |                                               | - evict: :status: 302 | 
 |                                               | -> :status: 307 | 
 |       c1                                      | == Indexed - Add == | 
 |                                               |   idx = 65 | 
 |                                               | -> cache-control: private | 
 |       c0                                      | == Indexed - Add == | 
 |                                               |   idx = 64 | 
 |                                               | -> date: Mon, 21 Oct 2013 | 
 |                                               |   20:13:21 GMT | 
 |       bf                                      | == Indexed - Add == | 
 |                                               |   idx = 63 | 
 |                                               | -> location: | 
 |                                               |   https://www.example.com | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT(header_entries_, | 
 |               ElementsAreArray({ | 
 |                   HpackHeaderEntry{":status", "307"}, | 
 |                   HpackHeaderEntry{"cache-control", "private"}, | 
 |                   HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:21 GMT"}, | 
 |                   HpackHeaderEntry{"location", "https://www.example.com"}, | 
 |               })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  42) :status: 307 | 
 |   //   [  2] (s =  63) location: https://www.example.com | 
 |   //   [  3] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT | 
 |   //   [  4] (s =  52) cache-control: private | 
 |   //         Table size: 222 | 
 |   ASSERT_TRUE( | 
 |       VerifyDynamicTableContents({{":status", "307"}, | 
 |                                   {"location", "https://www.example.com"}, | 
 |                                   {"date", "Mon, 21 Oct 2013 20:13:21 GMT"}, | 
 |                                   {"cache-control", "private"}})); | 
 |   ASSERT_EQ(222u, current_header_table_size()); | 
 |  | 
 |   // C.5.3 Third Response | 
 |   // | 
 |   // Several header fields are evicted from the dynamic table during the | 
 |   // processing of this header list. | 
 |   // | 
 |   // Header list to encode: | 
 |   // | 
 |   //   :status: 200 | 
 |   //   cache-control: private | 
 |   //   date: Mon, 21 Oct 2013 20:13:22 GMT | 
 |   //   location: https://www.example.com | 
 |   //   content-encoding: gzip | 
 |   //   set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1 | 
 |   hpack_block = HpackExampleToStringOrDie(R"( | 
 |       88                                      | == Indexed - Add == | 
 |                                               |   idx = 8 | 
 |                                               | -> :status: 200 | 
 |       c1                                      | == Indexed - Add == | 
 |                                               |   idx = 65 | 
 |                                               | -> cache-control: private | 
 |       61                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 33) | 
 |                                               |     date | 
 |       1d                                      |   Literal value (len = 29) | 
 |       4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013 | 
 |       2032 303a 3133 3a32 3220 474d 54        |  20:13:22 GMT | 
 |                                               | - evict: cache-control: | 
 |                                               |   private | 
 |                                               | -> date: Mon, 21 Oct 2013 | 
 |                                               |   20:13:22 GMT | 
 |       c0                                      | == Indexed - Add == | 
 |                                               |   idx = 64 | 
 |                                               | -> location: | 
 |                                               |   https://www.example.com | 
 |       5a                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 26) | 
 |                                               |     content-encoding | 
 |       04                                      |   Literal value (len = 4) | 
 |       677a 6970                               | gzip | 
 |                                               | - evict: date: Mon, 21 Oct | 
 |                                               |    2013 20:13:21 GMT | 
 |                                               | -> content-encoding: gzip | 
 |       77                                      | == Literal indexed == | 
 |                                               |   Indexed name (idx = 55) | 
 |                                               |     set-cookie | 
 |       38                                      |   Literal value (len = 56) | 
 |       666f 6f3d 4153 444a 4b48 514b 425a 584f | foo=ASDJKHQKBZXO | 
 |       5157 454f 5049 5541 5851 5745 4f49 553b | QWEOPIUAXQWEOIU; | 
 |       206d 6178 2d61 6765 3d33 3630 303b 2076 |  max-age=3600; v | 
 |       6572 7369 6f6e 3d31                     | ersion=1 | 
 |                                               | - evict: location: | 
 |                                               |   https://www.example.com | 
 |                                               | - evict: :status: 307 | 
 |                                               | -> set-cookie: foo=ASDJKHQ | 
 |                                               |   KBZXOQWEOPIUAXQWEOIU; ma | 
 |                                               |   x-age=3600; version=1 | 
 |   )"); | 
 |   EXPECT_TRUE(DecodeBlock(hpack_block)); | 
 |   ASSERT_THAT( | 
 |       header_entries_, | 
 |       ElementsAreArray({ | 
 |           HpackHeaderEntry{":status", "200"}, | 
 |           HpackHeaderEntry{"cache-control", "private"}, | 
 |           HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:22 GMT"}, | 
 |           HpackHeaderEntry{"location", "https://www.example.com"}, | 
 |           HpackHeaderEntry{"content-encoding", "gzip"}, | 
 |           HpackHeaderEntry{ | 
 |               "set-cookie", | 
 |               "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"}, | 
 |       })); | 
 |  | 
 |   // Dynamic Table (after decoding): | 
 |   // | 
 |   //   [  1] (s =  98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; | 
 |   //                    max-age=3600; version=1 | 
 |   //   [  2] (s =  52) content-encoding: gzip | 
 |   //   [  3] (s =  65) date: Mon, 21 Oct 2013 20:13:22 GMT | 
 |   //         Table size: 215 | 
 |   ASSERT_TRUE(VerifyDynamicTableContents( | 
 |       {{"set-cookie", | 
 |         "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"}, | 
 |        {"content-encoding", "gzip"}, | 
 |        {"date", "Mon, 21 Oct 2013 20:13:22 GMT"}})); | 
 |   ASSERT_EQ(215u, current_header_table_size()); | 
 | } | 
 |  | 
 | // Confirm that the table size can be changed, but at most twice. | 
 | TEST_P(HpackDecoderTest, ProcessesOptionalTableSizeUpdates) { | 
 |   EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(), | 
 |             header_table_size_limit()); | 
 |   // One update allowed. | 
 |   { | 
 |     HpackBlockBuilder hbb; | 
 |     hbb.AppendDynamicTableSizeUpdate(3000); | 
 |     EXPECT_TRUE(DecodeBlock(hbb.buffer())); | 
 |     EXPECT_EQ(3000u, header_table_size_limit()); | 
 |     EXPECT_EQ(0u, current_header_table_size()); | 
 |     EXPECT_TRUE(header_entries_.empty()); | 
 |   } | 
 |   // Two updates allowed. | 
 |   { | 
 |     HpackBlockBuilder hbb; | 
 |     hbb.AppendDynamicTableSizeUpdate(2000); | 
 |     hbb.AppendDynamicTableSizeUpdate(2500); | 
 |     EXPECT_TRUE(DecodeBlock(hbb.buffer())); | 
 |     EXPECT_EQ(2500u, header_table_size_limit()); | 
 |     EXPECT_EQ(0u, current_header_table_size()); | 
 |     EXPECT_TRUE(header_entries_.empty()); | 
 |   } | 
 |   // A third update in the same HPACK block is rejected, so the final | 
 |   // size is 1000, not 500. | 
 |   { | 
 |     HpackBlockBuilder hbb; | 
 |     hbb.AppendDynamicTableSizeUpdate(1500); | 
 |     hbb.AppendDynamicTableSizeUpdate(1000); | 
 |     hbb.AppendDynamicTableSizeUpdate(500); | 
 |     EXPECT_FALSE(DecodeBlock(hbb.buffer())); | 
 |     EXPECT_EQ(HpackDecodingError::kDynamicTableSizeUpdateNotAllowed, | 
 |               decoder_.error()); | 
 |     EXPECT_EQ(1u, error_messages_.size()); | 
 |     EXPECT_THAT(error_messages_[0], | 
 |                 Eq("Dynamic table size update not allowed")); | 
 |     EXPECT_EQ(1000u, header_table_size_limit()); | 
 |     EXPECT_EQ(0u, current_header_table_size()); | 
 |     EXPECT_TRUE(header_entries_.empty()); | 
 |   } | 
 |   // An error has been detected, so calls to HpackDecoder::DecodeFragment | 
 |   // should return immediately. | 
 |   DecodeBuffer db("\x80"); | 
 |   EXPECT_FALSE(decoder_.DecodeFragment(&db)); | 
 |   EXPECT_EQ(0u, db.Offset()); | 
 |   EXPECT_EQ(1u, error_messages_.size()); | 
 | } | 
 |  | 
 | // Confirm that the table size can be changed when required, but at most twice. | 
 | TEST_P(HpackDecoderTest, ProcessesRequiredTableSizeUpdate) { | 
 |   EXPECT_EQ(4096u, decoder_.GetCurrentHeaderTableSizeSetting()); | 
 |   // One update required, two allowed, one provided, followed by a header. | 
 |   decoder_.ApplyHeaderTableSizeSetting(1024); | 
 |   decoder_.ApplyHeaderTableSizeSetting(2048); | 
 |   EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(), | 
 |             header_table_size_limit()); | 
 |   EXPECT_EQ(2048u, decoder_.GetCurrentHeaderTableSizeSetting()); | 
 |   { | 
 |     HpackBlockBuilder hbb; | 
 |     hbb.AppendDynamicTableSizeUpdate(1024); | 
 |     hbb.AppendIndexedHeader(4);  // :path: / | 
 |     EXPECT_TRUE(DecodeBlock(hbb.buffer())); | 
 |     EXPECT_THAT(header_entries_, | 
 |                 ElementsAreArray({HpackHeaderEntry{":path", "/"}})); | 
 |     EXPECT_EQ(1024u, header_table_size_limit()); | 
 |     EXPECT_EQ(0u, current_header_table_size()); | 
 |   } | 
 |   // One update required, two allowed, two provided, followed by a header. | 
 |   decoder_.ApplyHeaderTableSizeSetting(1000); | 
 |   decoder_.ApplyHeaderTableSizeSetting(1500); | 
 |   EXPECT_EQ(1500u, decoder_.GetCurrentHeaderTableSizeSetting()); | 
 |   { | 
 |     HpackBlockBuilder hbb; | 
 |     hbb.AppendDynamicTableSizeUpdate(500); | 
 |     hbb.AppendDynamicTableSizeUpdate(1250); | 
 |     hbb.AppendIndexedHeader(5);  // :path: /index.html | 
 |     EXPECT_TRUE(DecodeBlock(hbb.buffer())); | 
 |     EXPECT_THAT(header_entries_, | 
 |                 ElementsAreArray({HpackHeaderEntry{":path", "/index.html"}})); | 
 |     EXPECT_EQ(1250u, header_table_size_limit()); | 
 |     EXPECT_EQ(0u, current_header_table_size()); | 
 |   } | 
 |   // One update required, two allowed, three provided, followed by a header. | 
 |   // The third update is rejected, so the final size is 1000, not 500. | 
 |   decoder_.ApplyHeaderTableSizeSetting(500); | 
 |   decoder_.ApplyHeaderTableSizeSetting(1000); | 
 |   EXPECT_EQ(1000u, decoder_.GetCurrentHeaderTableSizeSetting()); | 
 |   { | 
 |     HpackBlockBuilder hbb; | 
 |     hbb.AppendDynamicTableSizeUpdate(200); | 
 |     hbb.AppendDynamicTableSizeUpdate(700); | 
 |     hbb.AppendDynamicTableSizeUpdate(900); | 
 |     hbb.AppendIndexedHeader(5);  // Not decoded. | 
 |     EXPECT_FALSE(DecodeBlock(hbb.buffer())); | 
 |     EXPECT_FALSE(saw_end_); | 
 |     EXPECT_EQ(HpackDecodingError::kDynamicTableSizeUpdateNotAllowed, | 
 |               decoder_.error()); | 
 |     EXPECT_EQ(1u, error_messages_.size()); | 
 |     EXPECT_THAT(error_messages_[0], | 
 |                 Eq("Dynamic table size update not allowed")); | 
 |     EXPECT_EQ(700u, header_table_size_limit()); | 
 |     EXPECT_EQ(0u, current_header_table_size()); | 
 |     EXPECT_TRUE(header_entries_.empty()); | 
 |   } | 
 |   EXPECT_EQ(1000u, decoder_.GetCurrentHeaderTableSizeSetting()); | 
 |   // Now that an error has been detected, StartDecodingBlock should return | 
 |   // false. | 
 |   EXPECT_FALSE(decoder_.StartDecodingBlock()); | 
 | } | 
 |  | 
 | // Confirm that required size updates are validated. | 
 | TEST_P(HpackDecoderTest, InvalidRequiredSizeUpdate) { | 
 |   // Require a size update, but provide one that isn't small enough (must be | 
 |   // zero or one, in this case). | 
 |   decoder_.ApplyHeaderTableSizeSetting(1); | 
 |   decoder_.ApplyHeaderTableSizeSetting(1024); | 
 |   HpackBlockBuilder hbb; | 
 |   hbb.AppendDynamicTableSizeUpdate(2); | 
 |   EXPECT_TRUE(decoder_.StartDecodingBlock()); | 
 |   DecodeBuffer db(hbb.buffer()); | 
 |   EXPECT_FALSE(decoder_.DecodeFragment(&db)); | 
 |   EXPECT_FALSE(saw_end_); | 
 |   EXPECT_EQ( | 
 |       HpackDecodingError::kInitialDynamicTableSizeUpdateIsAboveLowWaterMark, | 
 |       decoder_.error()); | 
 |   EXPECT_EQ(1u, error_messages_.size()); | 
 |   EXPECT_THAT(error_messages_[0], | 
 |               Eq("Initial dynamic table size update is above low water mark")); | 
 |   EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(), | 
 |             header_table_size_limit()); | 
 | } | 
 |  | 
 | // Confirm that required size updates are indeed required before the end. | 
 | TEST_P(HpackDecoderTest, RequiredTableSizeChangeBeforeEnd) { | 
 |   decoder_.ApplyHeaderTableSizeSetting(1024); | 
 |   EXPECT_FALSE(DecodeBlock("")); | 
 |   EXPECT_EQ(HpackDecodingError::kMissingDynamicTableSizeUpdate, | 
 |             decoder_.error()); | 
 |   EXPECT_EQ(1u, error_messages_.size()); | 
 |   EXPECT_THAT(error_messages_[0], Eq("Missing dynamic table size update")); | 
 |   EXPECT_FALSE(saw_end_); | 
 | } | 
 |  | 
 | // Confirm that required size updates are indeed required before an | 
 | // indexed header. | 
 | TEST_P(HpackDecoderTest, RequiredTableSizeChangeBeforeIndexedHeader) { | 
 |   decoder_.ApplyHeaderTableSizeSetting(1024); | 
 |   HpackBlockBuilder hbb; | 
 |   hbb.AppendIndexedHeader(1); | 
 |   EXPECT_FALSE(DecodeBlock(hbb.buffer())); | 
 |   EXPECT_EQ(HpackDecodingError::kMissingDynamicTableSizeUpdate, | 
 |             decoder_.error()); | 
 |   EXPECT_EQ(1u, error_messages_.size()); | 
 |   EXPECT_THAT(error_messages_[0], Eq("Missing dynamic table size update")); | 
 |   EXPECT_FALSE(saw_end_); | 
 |   EXPECT_TRUE(header_entries_.empty()); | 
 | } | 
 |  | 
 | // Confirm that required size updates are indeed required before an indexed | 
 | // header name. | 
 | // TODO(jamessynge): Move some of these to hpack_decoder_state_test.cc. | 
 | TEST_P(HpackDecoderTest, RequiredTableSizeChangeBeforeIndexedHeaderName) { | 
 |   decoder_.ApplyHeaderTableSizeSetting(1024); | 
 |   HpackBlockBuilder hbb; | 
 |   hbb.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 2, | 
 |                                      false, "PUT"); | 
 |   EXPECT_FALSE(DecodeBlock(hbb.buffer())); | 
 |   EXPECT_EQ(HpackDecodingError::kMissingDynamicTableSizeUpdate, | 
 |             decoder_.error()); | 
 |   EXPECT_EQ(1u, error_messages_.size()); | 
 |   EXPECT_THAT(error_messages_[0], Eq("Missing dynamic table size update")); | 
 |   EXPECT_FALSE(saw_end_); | 
 |   EXPECT_TRUE(header_entries_.empty()); | 
 | } | 
 |  | 
 | // Confirm that required size updates are indeed required before a literal | 
 | // header name. | 
 | TEST_P(HpackDecoderTest, RequiredTableSizeChangeBeforeLiteralName) { | 
 |   decoder_.ApplyHeaderTableSizeSetting(1024); | 
 |   HpackBlockBuilder hbb; | 
 |   hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader, | 
 |                                 false, "name", false, "some data."); | 
 |   EXPECT_FALSE(DecodeBlock(hbb.buffer())); | 
 |   EXPECT_EQ(HpackDecodingError::kMissingDynamicTableSizeUpdate, | 
 |             decoder_.error()); | 
 |   EXPECT_EQ(1u, error_messages_.size()); | 
 |   EXPECT_THAT(error_messages_[0], Eq("Missing dynamic table size update")); | 
 |   EXPECT_FALSE(saw_end_); | 
 |   EXPECT_TRUE(header_entries_.empty()); | 
 | } | 
 |  | 
 | // Confirm that an excessively long varint is detected, in this case an | 
 | // index of 127, but with lots of additional high-order 0 bits provided, | 
 | // too many to be allowed. | 
 | TEST_P(HpackDecoderTest, InvalidIndexedHeaderVarint) { | 
 |   EXPECT_TRUE(decoder_.StartDecodingBlock()); | 
 |   DecodeBuffer db("\xff\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x00"); | 
 |   EXPECT_FALSE(decoder_.DecodeFragment(&db)); | 
 |   EXPECT_TRUE(decoder_.DetectError()); | 
 |   EXPECT_FALSE(saw_end_); | 
 |   EXPECT_EQ(HpackDecodingError::kIndexVarintError, decoder_.error()); | 
 |   EXPECT_EQ(1u, error_messages_.size()); | 
 |   EXPECT_THAT(error_messages_[0], | 
 |               Eq("Index varint beyond implementation limit")); | 
 |   EXPECT_TRUE(header_entries_.empty()); | 
 |   // Now that an error has been detected, EndDecodingBlock should not succeed. | 
 |   EXPECT_FALSE(decoder_.EndDecodingBlock()); | 
 | } | 
 |  | 
 | // Confirm that an invalid index into the tables is detected, in this case an | 
 | // index of 0. | 
 | TEST_P(HpackDecoderTest, InvalidIndex) { | 
 |   EXPECT_TRUE(decoder_.StartDecodingBlock()); | 
 |   DecodeBuffer db("\x80"); | 
 |   EXPECT_FALSE(decoder_.DecodeFragment(&db)); | 
 |   EXPECT_TRUE(decoder_.DetectError()); | 
 |   EXPECT_FALSE(saw_end_); | 
 |   EXPECT_EQ(HpackDecodingError::kInvalidIndex, decoder_.error()); | 
 |   EXPECT_EQ(1u, error_messages_.size()); | 
 |   EXPECT_THAT(error_messages_[0], | 
 |               Eq("Invalid index in indexed header field representation")); | 
 |   EXPECT_TRUE(header_entries_.empty()); | 
 |   // Now that an error has been detected, EndDecodingBlock should not succeed. | 
 |   EXPECT_FALSE(decoder_.EndDecodingBlock()); | 
 | } | 
 |  | 
 | // Confirm that EndDecodingBlock detects a truncated HPACK block. | 
 | TEST_P(HpackDecoderTest, TruncatedBlock) { | 
 |   HpackBlockBuilder hbb; | 
 |   hbb.AppendDynamicTableSizeUpdate(3000); | 
 |   EXPECT_EQ(3u, hbb.size()); | 
 |   hbb.AppendDynamicTableSizeUpdate(4000); | 
 |   EXPECT_EQ(6u, hbb.size()); | 
 |   // Decodes this block if the whole thing is provided. | 
 |   EXPECT_TRUE(DecodeBlock(hbb.buffer())); | 
 |   EXPECT_EQ(4000u, header_table_size_limit()); | 
 |   // Multiple times even. | 
 |   EXPECT_TRUE(DecodeBlock(hbb.buffer())); | 
 |   EXPECT_EQ(4000u, header_table_size_limit()); | 
 |   // But not if the block is truncated. | 
 |   EXPECT_FALSE(DecodeBlock(hbb.buffer().substr(0, hbb.size() - 1))); | 
 |   EXPECT_FALSE(saw_end_); | 
 |   EXPECT_EQ(HpackDecodingError::kTruncatedBlock, decoder_.error()); | 
 |   EXPECT_EQ(1u, error_messages_.size()); | 
 |   EXPECT_THAT(error_messages_[0], | 
 |               Eq("Block ends in the middle of an instruction")); | 
 |   // The first update was decoded. | 
 |   EXPECT_EQ(3000u, header_table_size_limit()); | 
 |   EXPECT_EQ(0u, current_header_table_size()); | 
 |   EXPECT_TRUE(header_entries_.empty()); | 
 | } | 
 |  | 
 | // Confirm that an oversized string is detected, ending decoding. | 
 | TEST_P(HpackDecoderTest, OversizeStringDetected) { | 
 |   HpackBlockBuilder hbb; | 
 |   hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader, | 
 |                                 false, "name", false, "some data."); | 
 |   hbb.AppendLiteralNameAndValue(HpackEntryType::kUnindexedLiteralHeader, false, | 
 |                                 "name2", false, "longer data"); | 
 |  | 
 |   // Normally able to decode this block. | 
 |   EXPECT_TRUE(DecodeBlock(hbb.buffer())); | 
 |   EXPECT_THAT(header_entries_, | 
 |               ElementsAreArray({HpackHeaderEntry{"name", "some data."}, | 
 |                                 HpackHeaderEntry{"name2", "longer data"}})); | 
 |  | 
 |   // But not if the maximum size of strings is less than the longest string. | 
 |   decoder_.set_max_string_size_bytes(10); | 
 |   EXPECT_FALSE(DecodeBlock(hbb.buffer())); | 
 |   EXPECT_THAT(header_entries_, | 
 |               ElementsAreArray({HpackHeaderEntry{"name", "some data."}})); | 
 |   EXPECT_FALSE(saw_end_); | 
 |   EXPECT_EQ(HpackDecodingError::kValueTooLong, decoder_.error()); | 
 |   EXPECT_EQ(1u, error_messages_.size()); | 
 |   EXPECT_THAT(error_messages_[0], Eq("Value length exceeds buffer limit")); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace test | 
 | }  // namespace http2 |