| // Copyright 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_instruction_decoder.h" | |
| #include <algorithm> | |
| #include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.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" | |
| using ::testing::_; | |
| using ::testing::Eq; | |
| using ::testing::Expectation; | |
| using ::testing::Return; | |
| using ::testing::StrictMock; | |
| using ::testing::Values; | |
| namespace quic { | |
| namespace test { | |
| namespace { | |
| // This instruction has three fields: an S bit and two varints. | |
| const QpackInstruction* TestInstruction1() { | |
| static const QpackInstruction* const instruction = | |
| new QpackInstruction{QpackInstructionOpcode{0x00, 0x80}, | |
| {{QpackInstructionFieldType::kSbit, 0x40}, | |
| {QpackInstructionFieldType::kVarint, 6}, | |
| {QpackInstructionFieldType::kVarint2, 8}}}; | |
| return instruction; | |
| } | |
| // This instruction has two fields: a header name with a 6-bit prefix, and a | |
| // header value with a 7-bit prefix, both preceded by a Huffman bit. | |
| const QpackInstruction* TestInstruction2() { | |
| static const QpackInstruction* const instruction = | |
| new QpackInstruction{QpackInstructionOpcode{0x80, 0x80}, | |
| {{QpackInstructionFieldType::kName, 6}, | |
| {QpackInstructionFieldType::kValue, 7}}}; | |
| return instruction; | |
| } | |
| const QpackLanguage* TestLanguage() { | |
| static const QpackLanguage* const language = | |
| new QpackLanguage{TestInstruction1(), TestInstruction2()}; | |
| return language; | |
| } | |
| class MockDelegate : public QpackInstructionDecoder::Delegate { | |
| public: | |
| MockDelegate() { | |
| ON_CALL(*this, OnInstructionDecoded(_)).WillByDefault(Return(true)); | |
| } | |
| MockDelegate(const MockDelegate&) = delete; | |
| MockDelegate& operator=(const MockDelegate&) = delete; | |
| ~MockDelegate() override = default; | |
| MOCK_METHOD1(OnInstructionDecoded, bool(const QpackInstruction* instruction)); | |
| MOCK_METHOD1(OnError, void(QuicStringPiece error_message)); | |
| }; | |
| class QpackInstructionDecoderTest : public QuicTestWithParam<FragmentMode> { | |
| public: | |
| QpackInstructionDecoderTest() | |
| : decoder_(TestLanguage(), &delegate_), fragment_mode_(GetParam()) {} | |
| ~QpackInstructionDecoderTest() override = default; | |
| protected: | |
| // Decode one full instruction with fragment sizes dictated by | |
| // |fragment_mode_|. | |
| // Verifies that AtInstructionBoundary() returns true before and after the | |
| // instruction, and returns false while decoding is in progress. | |
| void DecodeInstruction(QuicStringPiece data) { | |
| EXPECT_TRUE(decoder_.AtInstructionBoundary()); | |
| FragmentSizeGenerator fragment_size_generator = | |
| FragmentModeToFragmentSizeGenerator(fragment_mode_); | |
| while (!data.empty()) { | |
| size_t fragment_size = std::min(fragment_size_generator(), data.size()); | |
| decoder_.Decode(data.substr(0, fragment_size)); | |
| data = data.substr(fragment_size); | |
| if (!data.empty()) { | |
| EXPECT_FALSE(decoder_.AtInstructionBoundary()); | |
| } | |
| } | |
| EXPECT_TRUE(decoder_.AtInstructionBoundary()); | |
| } | |
| StrictMock<MockDelegate> delegate_; | |
| QpackInstructionDecoder decoder_; | |
| private: | |
| const FragmentMode fragment_mode_; | |
| }; | |
| INSTANTIATE_TEST_SUITE_P(, | |
| QpackInstructionDecoderTest, | |
| Values(FragmentMode::kSingleChunk, | |
| FragmentMode::kOctetByOctet)); | |
| TEST_P(QpackInstructionDecoderTest, SBitAndVarint2) { | |
| EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())); | |
| DecodeInstruction(QuicTextUtils::HexDecode("7f01ff65")); | |
| EXPECT_TRUE(decoder_.s_bit()); | |
| EXPECT_EQ(64u, decoder_.varint()); | |
| EXPECT_EQ(356u, decoder_.varint2()); | |
| EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())); | |
| DecodeInstruction(QuicTextUtils::HexDecode("05c8")); | |
| EXPECT_FALSE(decoder_.s_bit()); | |
| EXPECT_EQ(5u, decoder_.varint()); | |
| EXPECT_EQ(200u, decoder_.varint2()); | |
| } | |
| TEST_P(QpackInstructionDecoderTest, NameAndValue) { | |
| EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2())); | |
| DecodeInstruction(QuicTextUtils::HexDecode("83666f6f03626172")); | |
| EXPECT_EQ("foo", decoder_.name()); | |
| EXPECT_EQ("bar", decoder_.value()); | |
| EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2())); | |
| DecodeInstruction(QuicTextUtils::HexDecode("8000")); | |
| EXPECT_EQ("", decoder_.name()); | |
| EXPECT_EQ("", decoder_.value()); | |
| EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2())); | |
| DecodeInstruction(QuicTextUtils::HexDecode("c294e7838c767f")); | |
| EXPECT_EQ("foo", decoder_.name()); | |
| EXPECT_EQ("bar", decoder_.value()); | |
| } | |
| TEST_P(QpackInstructionDecoderTest, InvalidHuffmanEncoding) { | |
| EXPECT_CALL(delegate_, OnError(Eq("Error in Huffman-encoded string."))); | |
| decoder_.Decode(QuicTextUtils::HexDecode("c1ff")); | |
| } | |
| TEST_P(QpackInstructionDecoderTest, InvalidVarintEncoding) { | |
| EXPECT_CALL(delegate_, OnError(Eq("Encoded integer too large."))); | |
| decoder_.Decode(QuicTextUtils::HexDecode("ffffffffffffffffffffff")); | |
| } | |
| TEST_P(QpackInstructionDecoderTest, DelegateSignalsError) { | |
| // First instruction is valid. | |
| Expectation first_call = | |
| EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())) | |
| .WillOnce(Return(true)); | |
| // Second instruction is invalid. Decoding must halt. | |
| EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1())) | |
| .After(first_call) | |
| .WillOnce(Return(false)); | |
| decoder_.Decode(QuicTextUtils::HexDecode("01000200030004000500")); | |
| EXPECT_EQ(2u, decoder_.varint()); | |
| } | |
| } // namespace | |
| } // namespace test | |
| } // namespace quic |