blob: d1fe9b56f19f18e391ef5eb7a94281886d3430c1 [file] [log] [blame]
// 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 "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#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_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_CASE_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