blob: b10c1b42d86b6b70e75454a293acb054ce3b228e [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 "quic/core/qpack/qpack_instruction_decoder.h"
#include <algorithm>
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "quic/core/qpack/qpack_instructions.h"
#include "quic/platform/api/quic_logging.h"
#include "quic/platform/api/quic_test.h"
#include "quic/test_tools/qpack/qpack_test_utils.h"
using ::testing::_;
using ::testing::Eq;
using ::testing::Expectation;
using ::testing::InvokeWithoutArgs;
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_METHOD(bool,
OnInstructionDecoded,
(const QpackInstruction*),
(override));
MOCK_METHOD(void,
OnInstructionDecodingError,
(QpackInstructionDecoder::ErrorCode error_code,
absl::string_view error_message),
(override));
};
class QpackInstructionDecoderTest : public QuicTestWithParam<FragmentMode> {
protected:
QpackInstructionDecoderTest()
: decoder_(std::make_unique<QpackInstructionDecoder>(TestLanguage(),
&delegate_)),
fragment_mode_(GetParam()) {}
~QpackInstructionDecoderTest() override = default;
void SetUp() override {
// Destroy QpackInstructionDecoder on error to test that it does not crash.
// See https://crbug.com/1025209.
ON_CALL(delegate_, OnInstructionDecodingError(_, _))
.WillByDefault(InvokeWithoutArgs([this]() { decoder_.reset(); }));
}
// Decode one full instruction with fragment sizes dictated by
// |fragment_mode_|.
// Assumes that |data| is a single complete instruction, and accordingly
// verifies that AtInstructionBoundary() returns true before and after the
// instruction, and returns false while decoding is in progress.
// Assumes that delegate methods destroy |decoder_| if they return false.
void DecodeInstruction(absl::string_view 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());
bool success = decoder_->Decode(data.substr(0, fragment_size));
if (!decoder_) {
EXPECT_FALSE(success);
return;
}
EXPECT_TRUE(success);
data = data.substr(fragment_size);
if (!data.empty()) {
EXPECT_FALSE(decoder_->AtInstructionBoundary());
}
}
EXPECT_TRUE(decoder_->AtInstructionBoundary());
}
StrictMock<MockDelegate> delegate_;
std::unique_ptr<QpackInstructionDecoder> decoder_;
private:
const FragmentMode fragment_mode_;
};
INSTANTIATE_TEST_SUITE_P(All,
QpackInstructionDecoderTest,
Values(FragmentMode::kSingleChunk,
FragmentMode::kOctetByOctet));
TEST_P(QpackInstructionDecoderTest, SBitAndVarint2) {
EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()));
DecodeInstruction(absl::HexStringToBytes("7f01ff65"));
EXPECT_TRUE(decoder_->s_bit());
EXPECT_EQ(64u, decoder_->varint());
EXPECT_EQ(356u, decoder_->varint2());
EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()));
DecodeInstruction(absl::HexStringToBytes("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(absl::HexStringToBytes("83666f6f03626172"));
EXPECT_EQ("foo", decoder_->name());
EXPECT_EQ("bar", decoder_->value());
EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2()));
DecodeInstruction(absl::HexStringToBytes("8000"));
EXPECT_EQ("", decoder_->name());
EXPECT_EQ("", decoder_->value());
EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2()));
DecodeInstruction(absl::HexStringToBytes("c294e7838c767f"));
EXPECT_EQ("foo", decoder_->name());
EXPECT_EQ("bar", decoder_->value());
}
TEST_P(QpackInstructionDecoderTest, InvalidHuffmanEncoding) {
EXPECT_CALL(delegate_,
OnInstructionDecodingError(
QpackInstructionDecoder::ErrorCode::HUFFMAN_ENCODING_ERROR,
Eq("Error in Huffman-encoded string.")));
DecodeInstruction(absl::HexStringToBytes("c1ff"));
}
TEST_P(QpackInstructionDecoderTest, InvalidVarintEncoding) {
EXPECT_CALL(delegate_,
OnInstructionDecodingError(
QpackInstructionDecoder::ErrorCode::INTEGER_TOO_LARGE,
Eq("Encoded integer too large.")));
DecodeInstruction(absl::HexStringToBytes("ffffffffffffffffffffff"));
}
TEST_P(QpackInstructionDecoderTest, StringLiteralTooLong) {
EXPECT_CALL(delegate_,
OnInstructionDecodingError(
QpackInstructionDecoder::ErrorCode::STRING_LITERAL_TOO_LONG,
Eq("String literal too long.")));
DecodeInstruction(absl::HexStringToBytes("bfffff7f"));
}
TEST_P(QpackInstructionDecoderTest, DelegateSignalsError) {
// First instruction is valid.
Expectation first_call =
EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
.WillOnce(InvokeWithoutArgs([this]() -> bool {
EXPECT_EQ(1u, decoder_->varint());
return true;
}));
// Second instruction is invalid. Decoding must halt.
EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
.After(first_call)
.WillOnce(InvokeWithoutArgs([this]() -> bool {
EXPECT_EQ(2u, decoder_->varint());
return false;
}));
EXPECT_FALSE(
decoder_->Decode(absl::HexStringToBytes("01000200030004000500")));
}
// QpackInstructionDecoder must not crash if it is destroyed from a
// Delegate::OnInstructionDecoded() call as long as it returns false.
TEST_P(QpackInstructionDecoderTest, DelegateSignalsErrorAndDestroysDecoder) {
EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
.WillOnce(InvokeWithoutArgs([this]() -> bool {
EXPECT_EQ(1u, decoder_->varint());
decoder_.reset();
return false;
}));
DecodeInstruction(absl::HexStringToBytes("0100"));
}
} // namespace
} // namespace test
} // namespace quic