blob: aefadd1d2eaa4eda48c6085374aa38c3eea673e1 [file] [log] [blame]
// Copyright 2016 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 "quiche/http2/hpack/decoder/hpack_entry_decoder.h"
// Tests of HpackEntryDecoder.
#include <cstdint>
#include "quiche/http2/test_tools/hpack_block_builder.h"
#include "quiche/http2/test_tools/hpack_entry_collector.h"
#include "quiche/http2/test_tools/http2_random.h"
#include "quiche/http2/test_tools/random_decoder_test_base.h"
#include "quiche/common/platform/api/quiche_expect_bug.h"
#include "quiche/common/platform/api/quiche_test.h"
namespace http2 {
namespace test {
namespace {
class HpackEntryDecoderTest : public RandomDecoderTest {
protected:
HpackEntryDecoderTest() : listener_(&collector_) {}
DecodeStatus StartDecoding(DecodeBuffer* b) override {
collector_.Clear();
return decoder_.Start(b, &listener_);
}
DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
return decoder_.Resume(b, &listener_);
}
AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* db,
const Validator& validator) {
// StartDecoding, above, requires the DecodeBuffer be non-empty so that it
// can call Start with the prefix byte.
bool return_non_zero_on_first = true;
return RandomDecoderTest::DecodeAndValidateSeveralWays(
db, return_non_zero_on_first, validator);
}
AssertionResult DecodeAndValidateSeveralWays(const HpackBlockBuilder& hbb,
const Validator& validator) {
DecodeBuffer db(hbb.buffer());
return DecodeAndValidateSeveralWays(&db, validator);
}
HpackEntryDecoder decoder_;
HpackEntryCollector collector_;
HpackEntryDecoderVLoggingListener listener_;
};
TEST_F(HpackEntryDecoderTest, IndexedHeader_Literals) {
{
const char input[] = {'\x82'}; // == Index 2 ==
DecodeBuffer b(input);
auto do_check = [this]() { return collector_.ValidateIndexedHeader(2); };
EXPECT_TRUE(
DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
collector_.Clear();
{
const char input[] = {'\xfe'}; // == Index 126 ==
DecodeBuffer b(input);
auto do_check = [this]() { return collector_.ValidateIndexedHeader(126); };
EXPECT_TRUE(
DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
collector_.Clear();
{
const char input[] = {'\xff', '\x00'}; // == Index 127 ==
DecodeBuffer b(input);
auto do_check = [this]() { return collector_.ValidateIndexedHeader(127); };
EXPECT_TRUE(
DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
}
TEST_F(HpackEntryDecoderTest, IndexedHeader_Various) {
// Indices chosen to hit encoding and table boundaries.
for (const uint32_t ndx : {1, 2, 61, 62, 63, 126, 127, 254, 255, 256}) {
HpackBlockBuilder hbb;
hbb.AppendIndexedHeader(ndx);
auto do_check = [this, ndx]() {
return collector_.ValidateIndexedHeader(ndx);
};
EXPECT_TRUE(
DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
}
TEST_F(HpackEntryDecoderTest, IndexedLiteralValue_Literal) {
const char input[] =
"\x7f" // == Literal indexed, name index 0x40 ==
"\x01" // 2nd byte of name index (0x01 + 0x3f == 0x40)
"\x0d" // Value length (13)
"custom-header"; // Value
DecodeBuffer b(input, sizeof input - 1);
auto do_check = [this]() {
return collector_.ValidateLiteralValueHeader(
HpackEntryType::kIndexedLiteralHeader, 0x40, false, "custom-header");
};
EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
TEST_F(HpackEntryDecoderTest, IndexedLiteralNameValue_Literal) {
const char input[] =
"\x40" // == Literal indexed ==
"\x0a" // Name length (10)
"custom-key" // Name
"\x0d" // Value length (13)
"custom-header"; // Value
DecodeBuffer b(input, sizeof input - 1);
auto do_check = [this]() {
return collector_.ValidateLiteralNameValueHeader(
HpackEntryType::kIndexedLiteralHeader, false, "custom-key", false,
"custom-header");
};
EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
TEST_F(HpackEntryDecoderTest, DynamicTableSizeUpdate_Literal) {
// Size update, length 31.
const char input[] = "\x3f\x00";
DecodeBuffer b(input, 2);
auto do_check = [this]() {
return collector_.ValidateDynamicTableSizeUpdate(31);
};
EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
class HpackLiteralEntryDecoderTest
: public HpackEntryDecoderTest,
public ::testing::WithParamInterface<HpackEntryType> {
protected:
HpackLiteralEntryDecoderTest() : entry_type_(GetParam()) {}
const HpackEntryType entry_type_;
};
INSTANTIATE_TEST_SUITE_P(
AllLiteralTypes, HpackLiteralEntryDecoderTest,
testing::Values(HpackEntryType::kIndexedLiteralHeader,
HpackEntryType::kUnindexedLiteralHeader,
HpackEntryType::kNeverIndexedLiteralHeader));
TEST_P(HpackLiteralEntryDecoderTest, RandNameIndexAndLiteralValue) {
for (int n = 0; n < 10; n++) {
const uint32_t ndx = 1 + Random().Rand8();
const bool value_is_huffman_encoded = (n % 2) == 0;
const std::string value = Random().RandString(Random().Rand8());
HpackBlockBuilder hbb;
hbb.AppendNameIndexAndLiteralValue(entry_type_, ndx,
value_is_huffman_encoded, value);
auto do_check = [this, ndx, value_is_huffman_encoded,
value]() -> AssertionResult {
return collector_.ValidateLiteralValueHeader(
entry_type_, ndx, value_is_huffman_encoded, value);
};
EXPECT_TRUE(
DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
}
TEST_P(HpackLiteralEntryDecoderTest, RandLiteralNameAndValue) {
for (int n = 0; n < 10; n++) {
const bool name_is_huffman_encoded = (n & 1) == 0;
const int name_len = 1 + Random().Rand8();
const std::string name = Random().RandString(name_len);
const bool value_is_huffman_encoded = (n & 2) == 0;
const int value_len = Random().Skewed(10);
const std::string value = Random().RandString(value_len);
HpackBlockBuilder hbb;
hbb.AppendLiteralNameAndValue(entry_type_, name_is_huffman_encoded, name,
value_is_huffman_encoded, value);
auto do_check = [this, name_is_huffman_encoded, name,
value_is_huffman_encoded, value]() -> AssertionResult {
return collector_.ValidateLiteralNameValueHeader(
entry_type_, name_is_huffman_encoded, name, value_is_huffman_encoded,
value);
};
EXPECT_TRUE(
DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
EXPECT_TRUE(do_check());
}
}
} // namespace
} // namespace test
} // namespace http2