| // 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/quic/core/qpack/qpack_decoder.h" |
| |
| #include "base/logging.h" |
| #include "net/third_party/quic/core/qpack/qpack_decoder_test_utils.h" |
| #include "net/third_party/quic/core/qpack/qpack_test_utils.h" |
| #include "net/third_party/quic/platform/api/quic_test.h" |
| #include "net/third_party/quic/platform/api/quic_text_utils.h" |
| #include "net/third_party/spdy/core/spdy_header_block.h" |
| |
| using ::testing::StrictMock; |
| using ::testing::Values; |
| |
| namespace quic { |
| namespace test { |
| namespace { |
| |
| class MockHeadersHandler : public QpackDecoder::HeadersHandlerInterface { |
| public: |
| MockHeadersHandler() = default; |
| MockHeadersHandler(const MockHeadersHandler&) = delete; |
| MockHeadersHandler& operator=(const MockHeadersHandler&) = delete; |
| ~MockHeadersHandler() override = default; |
| |
| MOCK_METHOD2(OnHeaderDecoded, |
| void(QuicStringPiece name, QuicStringPiece value)); |
| MOCK_METHOD0(OnDecodingCompleted, void()); |
| MOCK_METHOD1(OnDecodingErrorDetected, void(QuicStringPiece error_message)); |
| }; |
| |
| class QpackDecoderTest : public QuicTestWithParam<FragmentMode> { |
| public: |
| QpackDecoderTest() : fragment_mode_(GetParam()) {} |
| |
| void Decode(QuicStringPiece data) { |
| QpackDecode(&handler_, FragmentModeToFragmentSizeGenerator(fragment_mode_), |
| data); |
| } |
| |
| protected: |
| StrictMock<MockHeadersHandler> handler_; |
| |
| private: |
| const FragmentMode fragment_mode_; |
| }; |
| |
| INSTANTIATE_TEST_CASE_P(, |
| QpackDecoderTest, |
| Values(FragmentMode::kSingleChunk, |
| FragmentMode::kOctetByOctet)); |
| |
| TEST_P(QpackDecoderTest, NotStarted) { |
| EXPECT_CALL(handler_, OnDecodingCompleted()); |
| |
| QpackDecoder decoder; |
| auto progressive_decoder = decoder.DecodeHeaderBlock(&handler_); |
| progressive_decoder->EndHeaderBlock(); |
| } |
| |
| TEST_P(QpackDecoderTest, Empty) { |
| EXPECT_CALL(handler_, OnDecodingCompleted()); |
| |
| Decode(""); |
| } |
| |
| TEST_P(QpackDecoderTest, EmptyName) { |
| EXPECT_CALL(handler_, |
| OnHeaderDecoded(QuicStringPiece(""), QuicStringPiece("foo"))); |
| EXPECT_CALL(handler_, OnDecodingCompleted()); |
| |
| Decode(QuicTextUtils::HexDecode("2003666f6f")); |
| } |
| |
| TEST_P(QpackDecoderTest, EmptyValue) { |
| EXPECT_CALL(handler_, |
| OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece(""))); |
| EXPECT_CALL(handler_, OnDecodingCompleted()); |
| |
| Decode(QuicTextUtils::HexDecode("23666f6f00")); |
| } |
| |
| TEST_P(QpackDecoderTest, EmptyNameAndValue) { |
| EXPECT_CALL(handler_, |
| OnHeaderDecoded(QuicStringPiece(""), QuicStringPiece(""))); |
| EXPECT_CALL(handler_, OnDecodingCompleted()); |
| |
| Decode(QuicTextUtils::HexDecode("2000")); |
| } |
| |
| TEST_P(QpackDecoderTest, Simple) { |
| EXPECT_CALL(handler_, |
| OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar"))); |
| EXPECT_CALL(handler_, OnDecodingCompleted()); |
| |
| Decode(QuicTextUtils::HexDecode("23666f6f03626172")); |
| } |
| |
| TEST_P(QpackDecoderTest, Multiple) { |
| EXPECT_CALL(handler_, |
| OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar"))); |
| QuicString str(127, 'a'); |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("foobaar"), |
| QuicStringPiece(str))); |
| EXPECT_CALL(handler_, OnDecodingCompleted()); |
| |
| Decode(QuicTextUtils::HexDecode( |
| "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")); |
| } |
| |
| TEST_P(QpackDecoderTest, NameLenTooLarge) { |
| EXPECT_CALL(handler_, OnDecodingErrorDetected( |
| QuicStringPiece("Encoded integer too large."))); |
| |
| Decode(QuicTextUtils::HexDecode("27ffffffffff")); |
| } |
| |
| TEST_P(QpackDecoderTest, ValueLenTooLarge) { |
| EXPECT_CALL(handler_, |
| OnDecodingErrorDetected(QuicStringPiece("ValueLen too large."))); |
| |
| Decode(QuicTextUtils::HexDecode("23666f6f7fffffffffff")); |
| } |
| |
| TEST_P(QpackDecoderTest, IncompleteHeaderBlock) { |
| EXPECT_CALL(handler_, OnDecodingErrorDetected( |
| QuicStringPiece("Incomplete header block."))); |
| |
| Decode(QuicTextUtils::HexDecode("2366")); |
| } |
| |
| TEST_P(QpackDecoderTest, HuffmanSimple) { |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("custom-key"), |
| QuicStringPiece("custom-value"))); |
| EXPECT_CALL(handler_, OnDecodingCompleted()); |
| |
| Decode(QuicTextUtils::HexDecode( |
| QuicStringPiece("2f0125a849e95ba97d7f8925a849e95bb8e8b4bf"))); |
| } |
| |
| TEST_P(QpackDecoderTest, AlternatingHuffmanNonHuffman) { |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("custom-key"), |
| QuicStringPiece("custom-value"))) |
| .Times(4); |
| EXPECT_CALL(handler_, OnDecodingCompleted()); |
| |
| Decode(QuicTextUtils::HexDecode( |
| "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 name."))); |
| |
| // '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. |
| Decode(QuicTextUtils::HexDecode("2f0125a849e95ba97d7e8925a849e95bb8e8b4bf")); |
| } |
| |
| TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) { |
| EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece( |
| "Error in Huffman-encoded value."))); |
| |
| // '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. |
| Decode(QuicTextUtils::HexDecode("2f0125a849e95ba97d7f8925a849e95bb8e8b4be")); |
| } |
| |
| TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) { |
| EXPECT_CALL(handler_, OnDecodingErrorDetected( |
| QuicStringPiece("Error in Huffman-encoded name."))); |
| |
| // 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. |
| Decode( |
| QuicTextUtils::HexDecode("2f0225a849e95ba97d7fff8925a849e95bb8e8b4bf")); |
| } |
| |
| TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) { |
| EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece( |
| "Error in Huffman-encoded value."))); |
| |
| // 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. |
| Decode( |
| QuicTextUtils::HexDecode("2f0125a849e95ba97d7f8a25a849e95bb8e8b4bfff")); |
| } |
| |
| TEST_P(QpackDecoderTest, StaticTable) { |
| // A header name that has multiple entries with different values. |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":method"), |
| QuicStringPiece("GET"))); |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":method"), |
| QuicStringPiece("POST"))); |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":method"), |
| QuicStringPiece("CONNECT"))); |
| |
| // A header name that has a single entry with non-empty value. |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("accept-encoding"), |
| QuicStringPiece("gzip, deflate"))); |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("accept-encoding"), |
| QuicStringPiece("brotli"))); |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("accept-encoding"), |
| QuicStringPiece(""))); |
| |
| // A header name that has a single entry with empty value. |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("cache-control"), |
| QuicStringPiece(""))); |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("cache-control"), |
| QuicStringPiece("foo"))); |
| |
| EXPECT_CALL(handler_, OnDecodingCompleted()); |
| |
| Decode(QuicTextUtils::HexDecode( |
| "c2c35207434f4e4e454354d05f010662726f746c695f0100d85f0903666f6f")); |
| } |
| |
| TEST_P(QpackDecoderTest, TooLowStaticTableIndex) { |
| // This is the first entry in the static table with index 1. |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":authority"), |
| QuicStringPiece(""))); |
| |
| // Addressing entry 0 should trigger an error. |
| EXPECT_CALL(handler_, OnDecodingErrorDetected( |
| QuicStringPiece("Invalid static table index."))); |
| |
| Decode(QuicTextUtils::HexDecode("c1c0")); |
| } |
| |
| TEST_P(QpackDecoderTest, TooHighStaticTableIndex) { |
| // This is the last entry in the static table with index 61. |
| EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("www-authenticate"), |
| QuicStringPiece(""))); |
| |
| // Addressing entry 62 should trigger an error. |
| EXPECT_CALL(handler_, OnDecodingErrorDetected( |
| QuicStringPiece("Invalid static table index."))); |
| |
| Decode(QuicTextUtils::HexDecode("fdfe")); |
| } |
| } // namespace |
| } // namespace test |
| } // namespace quic |