| // Copyright (c) 2018 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h" | |
| #include <algorithm> | |
| #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h" | |
| #include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" | |
| #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" | |
| #include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" | |
| using ::testing::Eq; | |
| using ::testing::Sequence; | |
| using ::testing::StrictMock; | |
| using ::testing::Values; | |
| namespace quic { | |
| namespace test { | |
| namespace { | |
| // Header Acknowledgement decoder stream instruction with stream_id = 1. | |
| const char* const kHeaderAcknowledgement = "\x81"; | |
| class QpackDecoderTest : public QuicTestWithParam<FragmentMode> { | |
| protected: | |
| QpackDecoderTest() | |
| : qpack_decoder_(&encoder_stream_error_delegate_, | |
| &decoder_stream_sender_delegate_), | |
| fragment_mode_(GetParam()) { | |
| qpack_decoder_.SetMaximumDynamicTableCapacity(1024); | |
| } | |
| ~QpackDecoderTest() override = default; | |
| void DecodeEncoderStreamData(QuicStringPiece data) { | |
| qpack_decoder_.DecodeEncoderStreamData(data); | |
| } | |
| void DecodeHeaderBlock(QuicStringPiece data) { | |
| auto fragment_size_generator = | |
| FragmentModeToFragmentSizeGenerator(fragment_mode_); | |
| auto progressive_decoder = | |
| qpack_decoder_.DecodeHeaderBlock(/* stream_id = */ 1, &handler_); | |
| while (!data.empty()) { | |
| size_t fragment_size = std::min(fragment_size_generator(), data.size()); | |
| progressive_decoder->Decode(data.substr(0, fragment_size)); | |
| data = data.substr(fragment_size); | |
| } | |
| progressive_decoder->EndHeaderBlock(); | |
| } | |
| StrictMock<MockEncoderStreamErrorDelegate> encoder_stream_error_delegate_; | |
| StrictMock<MockDecoderStreamSenderDelegate> decoder_stream_sender_delegate_; | |
| StrictMock<MockHeadersHandler> handler_; | |
| private: | |
| QpackDecoder qpack_decoder_; | |
| const FragmentMode fragment_mode_; | |
| }; | |
| INSTANTIATE_TEST_SUITE_P(, | |
| QpackDecoderTest, | |
| Values(FragmentMode::kSingleChunk, | |
| FragmentMode::kOctetByOctet)); | |
| TEST_P(QpackDecoderTest, NoPrefix) { | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Incomplete header data prefix."))); | |
| // Header Data Prefix is at least two bytes long. | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("00")); | |
| } | |
| TEST_P(QpackDecoderTest, EmptyHeaderBlock) { | |
| EXPECT_CALL(handler_, OnDecodingCompleted()); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("0000")); | |
| } | |
| TEST_P(QpackDecoderTest, LiteralEntryEmptyName) { | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq("foo"))); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("00002003666f6f")); | |
| } | |
| TEST_P(QpackDecoderTest, LiteralEntryEmptyValue) { | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(""))); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f00")); | |
| } | |
| TEST_P(QpackDecoderTest, LiteralEntryEmptyNameAndValue) { | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq(""))); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("00002000")); | |
| } | |
| TEST_P(QpackDecoderTest, SimpleLiteralEntry) { | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f03626172")); | |
| } | |
| TEST_P(QpackDecoderTest, MultipleLiteralEntries) { | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); | |
| std::string str(127, 'a'); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foobaar"), QuicStringPiece(str))); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0000" // prefix | |
| "23666f6f03626172" // foo: bar | |
| "2700666f6f62616172" // 7 octet long header name, the smallest number | |
| // that does not fit on a 3-bit prefix. | |
| "7f0061616161616161" // 127 octet long header value, the smallest number | |
| "616161616161616161" // that does not fit on a 7-bit prefix. | |
| "6161616161616161616161616161616161616161616161616161616161616161616161" | |
| "6161616161616161616161616161616161616161616161616161616161616161616161" | |
| "6161616161616161616161616161616161616161616161616161616161616161616161" | |
| "616161616161")); | |
| } | |
| // Name Length value is too large for varint decoder to decode. | |
| TEST_P(QpackDecoderTest, NameLenTooLargeForVarintDecoder) { | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Encoded integer too large."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffffffffffffffffffff")); | |
| } | |
| // Name Length value can be decoded by varint decoder but exceeds 1 MB limit. | |
| TEST_P(QpackDecoderTest, NameLenExceedsLimit) { | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("String literal too long."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffff7f")); | |
| } | |
| // Value Length value is too large for varint decoder to decode. | |
| TEST_P(QpackDecoderTest, ValueLenTooLargeForVarintDecoder) { | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Encoded integer too large."))); | |
| DecodeHeaderBlock( | |
| QuicTextUtils::HexDecode("000023666f6f7fffffffffffffffffffff")); | |
| } | |
| // Value Length value can be decoded by varint decoder but exceeds 1 MB limit. | |
| TEST_P(QpackDecoderTest, ValueLenExceedsLimit) { | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("String literal too long."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f7fffff7f")); | |
| } | |
| TEST_P(QpackDecoderTest, IncompleteHeaderBlock) { | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Incomplete header block."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("00002366")); | |
| } | |
| TEST_P(QpackDecoderTest, HuffmanSimple) { | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value"))); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); | |
| DecodeHeaderBlock( | |
| QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf")); | |
| } | |
| TEST_P(QpackDecoderTest, AlternatingHuffmanNonHuffman) { | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value"))) | |
| .Times(4); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0000" // Prefix. | |
| "2f0125a849e95ba97d7f" // Huffman-encoded name. | |
| "8925a849e95bb8e8b4bf" // Huffman-encoded value. | |
| "2703637573746f6d2d6b6579" // Non-Huffman encoded name. | |
| "0c637573746f6d2d76616c7565" // Non-Huffman encoded value. | |
| "2f0125a849e95ba97d7f" // Huffman-encoded name. | |
| "0c637573746f6d2d76616c7565" // Non-Huffman encoded value. | |
| "2703637573746f6d2d6b6579" // Non-Huffman encoded name. | |
| "8925a849e95bb8e8b4bf")); // Huffman-encoded value. | |
| } | |
| TEST_P(QpackDecoderTest, HuffmanNameDoesNotHaveEOSPrefix) { | |
| EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece( | |
| "Error in Huffman-encoded string."))); | |
| // 'y' ends in 0b0 on the most significant bit of the last byte. | |
| // The remaining 7 bits must be a prefix of EOS, which is all 1s. | |
| DecodeHeaderBlock( | |
| QuicTextUtils::HexDecode("00002f0125a849e95ba97d7e8925a849e95bb8e8b4bf")); | |
| } | |
| TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) { | |
| EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece( | |
| "Error in Huffman-encoded string."))); | |
| // 'e' ends in 0b101, taking up the 3 most significant bits of the last byte. | |
| // The remaining 5 bits must be a prefix of EOS, which is all 1s. | |
| DecodeHeaderBlock( | |
| QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4be")); | |
| } | |
| TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) { | |
| EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece( | |
| "Error in Huffman-encoded string."))); | |
| // The trailing EOS prefix must be at most 7 bits long. Appending one octet | |
| // with value 0xff is invalid, even though 0b111111111111111 (15 bits) is a | |
| // prefix of EOS. | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "00002f0225a849e95ba97d7fff8925a849e95bb8e8b4bf")); | |
| } | |
| TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) { | |
| EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece( | |
| "Error in Huffman-encoded string."))); | |
| // The trailing EOS prefix must be at most 7 bits long. Appending one octet | |
| // with value 0xff is invalid, even though 0b1111111111111 (13 bits) is a | |
| // prefix of EOS. | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "00002f0125a849e95ba97d7f8a25a849e95bb8e8b4bfff")); | |
| } | |
| TEST_P(QpackDecoderTest, StaticTable) { | |
| // A header name that has multiple entries with different values. | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET"))); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("POST"))); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("TRACE"))); | |
| // A header name that has a single entry with non-empty value. | |
| EXPECT_CALL(handler_, | |
| OnHeaderDecoded(Eq("accept-encoding"), Eq("gzip, deflate, br"))); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq("compress"))); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq(""))); | |
| // A header name that has a single entry with empty value. | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq(""))); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq("foo"))); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0000d1dfccd45f108621e9aec2a11f5c8294e75f000554524143455f1000")); | |
| } | |
| TEST_P(QpackDecoderTest, TooHighStaticTableIndex) { | |
| // This is the last entry in the static table with index 98. | |
| EXPECT_CALL(handler_, | |
| OnHeaderDecoded(Eq("x-frame-options"), Eq("sameorigin"))); | |
| // Addressing entry 99 should trigger an error. | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Static table entry not found."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("0000ff23ff24")); | |
| } | |
| TEST_P(QpackDecoderTest, DynamicTable) { | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode( | |
| "6294e703626172" // Add literal entry with name "foo" and value "bar". | |
| "80035a5a5a" // Add entry with name of dynamic table entry index 0 | |
| // (relative index) and value "ZZZ". | |
| "cf8294e7" // Add entry with name of static table entry index 15 | |
| // and value "foo". | |
| "01")); // Duplicate entry with relative index 1. | |
| // Now there are four entries in the dynamic table. | |
| // Entry 0: "foo", "bar" | |
| // Entry 1: "foo", "ZZZ" | |
| // Entry 2: ":method", "foo" | |
| // Entry 3: "foo", "ZZZ" | |
| // Use a Sequence to test that mock methods are called in order. | |
| Sequence s; | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo"))) | |
| .InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))) | |
| .InSequence(s); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0500" // Required Insert Count 4 and Delta Base 0. | |
| // Base is 4 + 0 = 4. | |
| "83" // Dynamic table entry with relative index 3, absolute index 0. | |
| "82" // Dynamic table entry with relative index 2, absolute index 1. | |
| "81" // Dynamic table entry with relative index 1, absolute index 2. | |
| "80" // Dynamic table entry with relative index 0, absolute index 3. | |
| "41025a5a")); // Name of entry 1 (relative index) from dynamic table, | |
| // with value "ZZ". | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo"))) | |
| .InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))) | |
| .InSequence(s); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0502" // Required Insert Count 4 and Delta Base 2. | |
| // Base is 4 + 2 = 6. | |
| "85" // Dynamic table entry with relative index 5, absolute index 0. | |
| "84" // Dynamic table entry with relative index 4, absolute index 1. | |
| "83" // Dynamic table entry with relative index 3, absolute index 2. | |
| "82" // Dynamic table entry with relative index 2, absolute index 3. | |
| "43025a5a")); // Name of entry 3 (relative index) from dynamic table, | |
| // with value "ZZ". | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo"))) | |
| .InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))) | |
| .InSequence(s); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0582" // Required Insert Count 4 and Delta Base 2 with sign bit set. | |
| // Base is 4 - 2 - 1 = 1. | |
| "80" // Dynamic table entry with relative index 0, absolute index 0. | |
| "10" // Dynamic table entry with post-base index 0, absolute index 1. | |
| "11" // Dynamic table entry with post-base index 1, absolute index 2. | |
| "12" // Dynamic table entry with post-base index 2, absolute index 3. | |
| "01025a5a")); // Name of entry 1 (post-base index) from dynamic table, | |
| // with value "ZZ". | |
| } | |
| TEST_P(QpackDecoderTest, DecreasingDynamicTableCapacityEvictsEntries) { | |
| // Add literal entry with name "foo" and value "bar". | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0200" // Required Insert Count 1 and Delta Base 0. | |
| // Base is 1 + 0 = 1. | |
| "80")); // Dynamic table entry with relative index 0, absolute index 0. | |
| // Change dynamic table capacity to 32 bytes, smaller than the entry. | |
| // This must cause the entry to be evicted. | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f01")); | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0200" // Required Insert Count 1 and Delta Base 0. | |
| // Base is 1 + 0 = 1. | |
| "80")); // Dynamic table entry with relative index 0, absolute index 0. | |
| } | |
| TEST_P(QpackDecoderTest, EncoderStreamErrorEntryTooLarge) { | |
| EXPECT_CALL(encoder_stream_error_delegate_, | |
| OnEncoderStreamError(Eq("Error inserting literal entry."))); | |
| // Set dynamic table capacity to 34. | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f03")); | |
| // Add literal entry with name "foo" and value "bar", size is 32 + 3 + 3 = 38. | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); | |
| } | |
| TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidStaticTableEntry) { | |
| EXPECT_CALL(encoder_stream_error_delegate_, | |
| OnEncoderStreamError(Eq("Invalid static table entry."))); | |
| // Address invalid static table entry index 99. | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("ff2400")); | |
| } | |
| TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidDynamicTableEntry) { | |
| EXPECT_CALL(encoder_stream_error_delegate_, | |
| OnEncoderStreamError(Eq("Dynamic table entry not found."))); | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode( | |
| "6294e703626172" // Add literal entry with name "foo" and value "bar". | |
| "8100")); // Address dynamic table entry with relative index 1. Such | |
| // entry does not exist. The most recently added and only | |
| // dynamic table entry has relative index 0. | |
| } | |
| TEST_P(QpackDecoderTest, EncoderStreamErrorDuplicateInvalidEntry) { | |
| EXPECT_CALL(encoder_stream_error_delegate_, | |
| OnEncoderStreamError(Eq("Dynamic table entry not found."))); | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode( | |
| "6294e703626172" // Add literal entry with name "foo" and value "bar". | |
| "01")); // Duplicate dynamic table entry with relative index 1. Such | |
| // entry does not exist. The most recently added and only | |
| // dynamic table entry has relative index 0. | |
| } | |
| TEST_P(QpackDecoderTest, EncoderStreamErrorTooLargeInteger) { | |
| EXPECT_CALL(encoder_stream_error_delegate_, | |
| OnEncoderStreamError(Eq("Encoded integer too large."))); | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fffffffffffffffffffff")); | |
| } | |
| TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIsZero) { | |
| EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0280" // Required Insert Count is 1. Base 1 - 1 - 0 = 0 is explicitly | |
| // permitted by the spec. | |
| "80")); // However, addressing entry with relative index 0 would point to | |
| // absolute index -1, which is invalid. | |
| } | |
| TEST_P(QpackDecoderTest, InvalidNegativeBase) { | |
| EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Error calculating Base."))); | |
| // Required Insert Count 1, Delta Base 1 with sign bit set, Base would | |
| // be 1 - 1 - 1 = -1, but it is not allowed to be negative. | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("0281")); | |
| } | |
| TEST_P(QpackDecoderTest, InvalidDynamicEntryByRelativeIndex) { | |
| // Add literal entry with name "foo" and value "bar". | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0500" // Required Insert Count 4 and Delta Base 0. | |
| // Base is 4 + 0 = 4. | |
| "82")); // Indexed Header Field instruction addressing relative index 2. | |
| // This is absolute index 1. Such entry does not exist. | |
| EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0500" // Required Insert Count 4 and Delta Base 0. | |
| // Base is 4 + 0 = 4. | |
| "84")); // Indexed Header Field instruction addressing relative index 4. | |
| // This is absolute index -1, which is invalid. | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0500" // Required Insert Count 4 and Delta Base 0. | |
| // Base is 4 + 0 = 4. | |
| "4200")); // Literal Header Field with Name Reference instruction | |
| // addressing relative index 2. This is absolute index 1. Such | |
| // entry does not exist. | |
| EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0500" // Required Insert Count 4 and Delta Base 0. | |
| // Base is 4 + 0 = 4. | |
| "4400")); // Literal Header Field with Name Reference instruction | |
| // addressing relative index 4. This is absolute index -1, | |
| // which is invalid. | |
| } | |
| TEST_P(QpackDecoderTest, InvalidDynamicEntryByPostBaseIndex) { | |
| // Add literal entry with name "foo" and value "bar". | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0380" // Required Insert Count 2 and Delta Base 0 with sign bit set. | |
| // Base is 2 - 0 - 1 = 1 | |
| "10")); // Indexed Header Field instruction addressing dynamic table | |
| // entry with post-base index 0, absolute index 1. Such entry | |
| // does not exist. | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Dynamic table entry not found."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0380" // Required Insert Count 2 and Delta Base 0 with sign bit set. | |
| // Base is 2 - 0 - 1 = 1 | |
| "0000")); // Literal Header Field With Name Reference instruction | |
| // addressing dynamic table entry with post-base index 0, | |
| // absolute index 1. Such entry does not exist. | |
| } | |
| TEST_P(QpackDecoderTest, TableCapacityMustNotExceedMaximum) { | |
| EXPECT_CALL( | |
| encoder_stream_error_delegate_, | |
| OnEncoderStreamError(Eq("Error updating dynamic table capacity."))); | |
| // Try to update dynamic table capacity to 2048, which exceeds the maximum. | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe10f")); | |
| } | |
| TEST_P(QpackDecoderTest, SetMaximumDynamicTableCapacity) { | |
| // Update dynamic table capacity to 128, which does not exceed the maximum. | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f61")); | |
| } | |
| TEST_P(QpackDecoderTest, InvalidEncodedRequiredInsertCount) { | |
| // Maximum dynamic table capacity is 1024. | |
| // MaxEntries is 1024 / 32 = 32. | |
| // Required Insert Count is decoded modulo 2 * MaxEntries, that is, modulo 64. | |
| // A value of 1 cannot be encoded as 65 even though it has the same remainder. | |
| EXPECT_CALL(handler_, OnDecodingErrorDetected( | |
| Eq("Error decoding Required Insert Count."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode("4100")); | |
| } | |
| TEST_P(QpackDecoderTest, WrappedRequiredInsertCount) { | |
| // Maximum dynamic table capacity is 1024. | |
| // MaxEntries is 1024 / 32 = 32. | |
| // Add literal entry with name "foo" and a 600 byte long value. This will fit | |
| // in the dynamic table once but not twice. | |
| DecodeEncoderStreamData( | |
| QuicTextUtils::HexDecode("6294e7" // Name "foo". | |
| "7fd903")); // Value length 600. | |
| std::string header_value(600, 'Z'); | |
| DecodeEncoderStreamData(header_value); | |
| // Duplicate most recent entry 200 times. | |
| DecodeEncoderStreamData(std::string(200, '\x00')); | |
| // Now there is only one entry in the dynamic table, with absolute index 200. | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(header_value))); | |
| EXPECT_CALL(handler_, OnDecodingCompleted()); | |
| EXPECT_CALL(decoder_stream_sender_delegate_, | |
| WriteDecoderStreamData(Eq(kHeaderAcknowledgement))); | |
| // Send header block with Required Insert Count = 201. | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0a00" // Encoded Required Insert Count 10, Required Insert Count 201, | |
| // Delta Base 0, Base 201. | |
| "80")); // Emit dynamic table entry with relative index 0. | |
| } | |
| TEST_P(QpackDecoderTest, NonZeroRequiredInsertCountButNoDynamicEntries) { | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET"))); | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Required Insert Count too large."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0200" // Required Insert Count is 1. | |
| "d1")); // But the only instruction references the static table. | |
| } | |
| TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) { | |
| EXPECT_CALL( | |
| handler_, | |
| OnDecodingErrorDetected( | |
| Eq("Absolute Index must be smaller than Required Insert Count."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0201" // Required Insert Count 1 and Delta Base 1. | |
| // Base is 1 + 1 = 2. | |
| "80")); // Indexed Header Field instruction addressing dynamic table | |
| // entry with relative index 0, absolute index 1. This is not | |
| // allowed by Required Insert Count. | |
| EXPECT_CALL( | |
| handler_, | |
| OnDecodingErrorDetected( | |
| Eq("Absolute Index must be smaller than Required Insert Count."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0201" // Required Insert Count 1 and Delta Base 1. | |
| // Base is 1 + 1 = 2. | |
| "4000")); // Literal Header Field with Name Reference instruction | |
| // addressing dynamic table entry with relative index 0, | |
| // absolute index 1. This is not allowed by Required Index | |
| // Count. | |
| EXPECT_CALL( | |
| handler_, | |
| OnDecodingErrorDetected( | |
| Eq("Absolute Index must be smaller than Required Insert Count."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0200" // Required Insert Count 1 and Delta Base 0. | |
| // Base is 1 + 0 = 1. | |
| "10")); // Indexed Header Field with Post-Base Index instruction | |
| // addressing dynamic table entry with post-base index 0, | |
| // absolute index 1. This is not allowed by Required Insert | |
| // Count. | |
| EXPECT_CALL( | |
| handler_, | |
| OnDecodingErrorDetected( | |
| Eq("Absolute Index must be smaller than Required Insert Count."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0200" // Required Insert Count 1 and Delta Base 0. | |
| // Base is 1 + 0 = 1. | |
| "0000")); // Literal Header Field with Post-Base Name Reference | |
| // instruction addressing dynamic table entry with post-base | |
| // index 0, absolute index 1. This is not allowed by Required | |
| // Index Count. | |
| } | |
| TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) { | |
| // Add literal entry with name "foo" and value "bar". | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172")); | |
| // Duplicate entry. | |
| DecodeEncoderStreamData(QuicTextUtils::HexDecode("00")); | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Required Insert Count too large."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0300" // Required Insert Count 2 and Delta Base 0. | |
| // Base is 2 + 0 = 2. | |
| "81")); // Indexed Header Field instruction addressing dynamic table | |
| // entry with relative index 1, absolute index 0. Header block | |
| // requires insert count of 1, even though Required Insert Count | |
| // is 2. | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(""))); | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Required Insert Count too large."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0300" // Required Insert Count 2 and Delta Base 0. | |
| // Base is 2 + 0 = 2. | |
| "4100")); // Literal Header Field with Name Reference instruction | |
| // addressing dynamic table entry with relative index 1, | |
| // absolute index 0. Header block requires insert count of 1, | |
| // even though Required Insert Count is 2. | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))); | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Required Insert Count too large."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set. | |
| // Base is 3 - 1 - 1 = 1. | |
| "10")); // Indexed Header Field with Post-Base Index instruction | |
| // addressing dynamic table entry with post-base index 0, | |
| // absolute index 1. Header block requires insert count of 2, | |
| // even though Required Insert Count is 3. | |
| EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(""))); | |
| EXPECT_CALL(handler_, | |
| OnDecodingErrorDetected(Eq("Required Insert Count too large."))); | |
| DecodeHeaderBlock(QuicTextUtils::HexDecode( | |
| "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set. | |
| // Base is 3 - 1 - 1 = 1. | |
| "0000")); // Literal Header Field with Post-Base Name Reference | |
| // instruction addressing dynamic table entry with post-base | |
| // index 0, absolute index 1. Header block requires insert | |
| // count of 2, even though Required Insert Count is 3. | |
| } | |
| } // namespace | |
| } // namespace test | |
| } // namespace quic |