blob: 3a84e9a2c5975f9e9341ac9111b0eada84ae7912 [file] [log] [blame]
// 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