| Index: net/http2/hpack/decoder/hpack_varint_decoder_test.cc
|
| diff --git a/net/http2/hpack/decoder/hpack_varint_decoder_test.cc b/net/http2/hpack/decoder/hpack_varint_decoder_test.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e63e9f03daadb5014ee81a7c00921f9000b798f4
|
| --- /dev/null
|
| +++ b/net/http2/hpack/decoder/hpack_varint_decoder_test.cc
|
| @@ -0,0 +1,395 @@
|
| +// 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 "net/http2/hpack/decoder/hpack_varint_decoder.h"
|
| +
|
| +// Tests of HpackVarintDecoder.
|
| +
|
| +#include <stddef.h>
|
| +
|
| +#include <ios>
|
| +#include <iterator>
|
| +#include <ostream>
|
| +#include <set>
|
| +#include <sstream>
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/bind_helpers.h"
|
| +#include "base/logging.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| +#include "base/strings/string_piece.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "net/http2/hpack/tools/hpack_block_builder.h"
|
| +#include "net/http2/tools/failure.h"
|
| +#include "net/http2/tools/http2_random.h"
|
| +#include "net/http2/tools/random_decoder_test.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +using ::testing::AssertionFailure;
|
| +using ::testing::AssertionSuccess;
|
| +using base::StringPiece;
|
| +using base::StringPrintf;
|
| +using std::string;
|
| +
|
| +namespace net {
|
| +namespace test {
|
| +namespace {
|
| +
|
| +class HpackVarintDecoderTest : public RandomDecoderTest {
|
| + public:
|
| + AssertionResult ValidatorForValueTooLarge(bool* validated,
|
| + uint32_t expected_offset,
|
| + const DecodeBuffer& db,
|
| + DecodeStatus status) {
|
| + *validated = true;
|
| + VERIFY_EQ(DecodeStatus::kDecodeError, status);
|
| + VERIFY_EQ(expected_offset, db.Offset());
|
| + return AssertionSuccess();
|
| + }
|
| +
|
| + protected:
|
| + DecodeStatus StartDecoding(DecodeBuffer* b) override {
|
| + CHECK_LT(0u, b->Remaining());
|
| + CHECK_NE(0, prefix_mask_);
|
| + uint8_t prefix = b->DecodeUInt8();
|
| + return decoder_.Start(prefix, prefix_mask_, b);
|
| + }
|
| +
|
| + DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
|
| + return decoder_.Resume(b);
|
| + }
|
| +
|
| + AssertionResult ValidatorForDecodeSeveralWays(uint32_t expected_value,
|
| + const DecodeBuffer& db,
|
| + DecodeStatus status) {
|
| + if (decoder_.value() != expected_value) {
|
| + return AssertionFailure()
|
| + << "Value doesn't match expected: " << decoder_.value()
|
| + << " != " << expected_value;
|
| + }
|
| + return AssertionSuccess();
|
| + }
|
| +
|
| + void DecodeSeveralWays(uint32_t expected_value, uint32_t expected_offset) {
|
| + // The validator is called after each of the several times that the input
|
| + // DecodeBuffer is decoded, each with a different segmentation of the input.
|
| + // Validate that decoder_.value() matches the expected value.
|
| + Validator validator =
|
| + base::Bind(&HpackVarintDecoderTest::ValidatorForDecodeSeveralWays,
|
| + base::Unretained(this), expected_value);
|
| +
|
| + // First validate that decoding is done and that we've advanced the cursor
|
| + // the expected amount.
|
| + validator = ValidateDoneAndOffset(expected_offset, 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;
|
| +
|
| + DecodeBuffer b(buffer_);
|
| + EXPECT_TRUE(
|
| + DecodeAndValidateSeveralWays(&b, return_non_zero_on_first, validator));
|
| +
|
| + EXPECT_EQ(expected_value, decoder_.value());
|
| + EXPECT_EQ(expected_offset, b.Offset());
|
| + }
|
| +
|
| + void EncodeNoRandom(uint32_t value, uint8_t prefix_length) {
|
| + DCHECK_LE(4, prefix_length);
|
| + DCHECK_LE(prefix_length, 7);
|
| + prefix_length_ = prefix_length;
|
| +
|
| + HpackBlockBuilder bb;
|
| + bb.AppendHighBitsAndVarint(0, prefix_length_, value);
|
| + buffer_ = bb.buffer();
|
| + ASSERT_LT(0u, buffer_.size());
|
| +
|
| + // Note: setting member variable prefix_mask_ here, which will be read
|
| + // in StartDecoding above.
|
| + prefix_mask_ = (1 << prefix_length_) - 1;
|
| + ASSERT_EQ(buffer_[0], buffer_[0] & prefix_mask_);
|
| + }
|
| +
|
| + void Encode(uint32_t value, uint8_t prefix_length) {
|
| + EncodeNoRandom(value, prefix_length);
|
| + // Add some random bits to the prefix (the first byte) above the mask.
|
| + uint8_t prefix = buffer_[0];
|
| + buffer_[0] = prefix | (Random().Rand8() << prefix_length);
|
| + ASSERT_EQ(prefix, buffer_[0] & prefix_mask_);
|
| + }
|
| +
|
| + // This is really a test of HpackBlockBuilder, making sure that the input to
|
| + // HpackVarintDecoder is as expected, which also acts as confirmation that
|
| + // my thinking about the encodings being used by the tests, i.e. cover the
|
| + // range desired.
|
| + void ValidateEncoding(uint32_t value,
|
| + uint32_t minimum,
|
| + uint32_t maximum,
|
| + size_t expected_bytes) {
|
| + ASSERT_EQ(expected_bytes, buffer_.size());
|
| + if (expected_bytes > 1) {
|
| + EXPECT_EQ(prefix_mask_, buffer_[0] & prefix_mask_);
|
| + size_t last = expected_bytes - 1;
|
| + for (size_t ndx = 1; ndx < last; ++ndx) {
|
| + // Before the last extension byte, we expect the high-bit set.
|
| + uint8_t byte = buffer_[ndx];
|
| + if (value == minimum) {
|
| + EXPECT_EQ(0x80, byte) << "ndx=" << ndx;
|
| + } else if (value == maximum) {
|
| + EXPECT_EQ(0xff, byte) << "ndx=" << ndx;
|
| + } else {
|
| + EXPECT_EQ(0x80, byte & 0x80) << "ndx=" << ndx;
|
| + }
|
| + }
|
| + // The last extension byte should not have the high-bit set.
|
| + uint8_t byte = buffer_[last];
|
| + if (value == minimum) {
|
| + if (expected_bytes == 2) {
|
| + EXPECT_EQ(0x00, byte);
|
| + } else {
|
| + EXPECT_EQ(0x01, byte);
|
| + }
|
| + } else if (value == maximum) {
|
| + EXPECT_EQ(0x7f, byte);
|
| + } else {
|
| + EXPECT_EQ(0x00, byte & 0x80);
|
| + }
|
| + } else {
|
| + EXPECT_EQ(value, static_cast<uint32_t>(buffer_[0] & prefix_mask_));
|
| + EXPECT_LT(value, static_cast<uint32_t>(prefix_mask_));
|
| + }
|
| + }
|
| +
|
| + void EncodeAndDecodeValues(const std::set<uint32_t>& values,
|
| + uint8_t prefix_length,
|
| + size_t expected_bytes) {
|
| + CHECK(!values.empty());
|
| + const uint32_t minimum = *values.begin();
|
| + const uint32_t maximum = *values.rbegin();
|
| + for (const uint32_t value : values) {
|
| + Encode(value, prefix_length); // Sets prefix_mask_ and buffer_
|
| +
|
| + std::stringstream ss;
|
| + ss << "value=" << value << " (0x" << std::hex << value
|
| + << "), prefix_length=" << std::dec << prefix_length
|
| + << ", expected_bytes=" << expected_bytes << std::endl
|
| + << HexEncode(buffer_);
|
| + string msg(ss.str());
|
| +
|
| + if (value == minimum) {
|
| + LOG(INFO) << "Checking minimum; " << msg;
|
| + } else if (value == maximum) {
|
| + LOG(INFO) << "Checking maximum; " << msg;
|
| + }
|
| +
|
| + SCOPED_TRACE(msg);
|
| + ValidateEncoding(value, minimum, maximum, expected_bytes);
|
| + DecodeSeveralWays(value, expected_bytes);
|
| +
|
| + // Append some random data to the end of buffer_ and repeat. That random
|
| + // data should be ignored.
|
| + buffer_.append(Random().RandString(1 + Random().Uniform(10)));
|
| + DecodeSeveralWays(value, expected_bytes);
|
| +
|
| + // If possible, add extension bytes that don't change the value.
|
| + if (1 < expected_bytes) {
|
| + buffer_.resize(expected_bytes);
|
| + for (uint8_t total_bytes = expected_bytes + 1; total_bytes <= 6;
|
| + ++total_bytes) {
|
| + // Mark the current last byte as not being the last one.
|
| + EXPECT_EQ(0x00, 0x80 & buffer_.back());
|
| + buffer_.back() |= 0x80;
|
| + buffer_.push_back('\0');
|
| + DecodeSeveralWays(value, total_bytes);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + void EncodeAndDecodeValuesInRange(uint32_t start,
|
| + uint32_t range,
|
| + uint8_t prefix_length,
|
| + size_t expected_bytes) {
|
| + const uint8_t prefix_mask = (1 << prefix_length) - 1;
|
| + const uint32_t beyond = start + range;
|
| +
|
| + LOG(INFO) << "############################################################";
|
| + LOG(INFO) << "prefix_length=" << static_cast<int>(prefix_length);
|
| + LOG(INFO) << "prefix_mask=" << std::hex << static_cast<int>(prefix_mask);
|
| + LOG(INFO) << "start=" << start << " (" << std::hex << start << ")";
|
| + LOG(INFO) << "range=" << range << " (" << std::hex << range << ")";
|
| + LOG(INFO) << "beyond=" << beyond << " (" << std::hex << beyond << ")";
|
| + LOG(INFO) << "expected_bytes=" << expected_bytes;
|
| +
|
| + // Confirm the claim that beyond requires more bytes.
|
| + Encode(beyond, prefix_length);
|
| + EXPECT_EQ(expected_bytes + 1, buffer_.size()) << HexEncode(buffer_);
|
| +
|
| + std::set<uint32_t> values;
|
| + if (range < 200) {
|
| + // Select all values in the range.
|
| + for (uint32_t offset = 0; offset < range; ++offset) {
|
| + values.insert(start + offset);
|
| + }
|
| + } else {
|
| + // Select some values in this range, including the minimum and maximum
|
| + // values that require exactly |expected_bytes| extension bytes.
|
| + values.insert({start, start + 1, beyond - 2, beyond - 1});
|
| + while (values.size() < 100) {
|
| + values.insert(start + Random().Rand32() % range);
|
| + }
|
| + }
|
| +
|
| + EncodeAndDecodeValues(values, prefix_length, expected_bytes);
|
| + }
|
| +
|
| + HpackVarintDecoder decoder_;
|
| + string buffer_;
|
| + uint8_t prefix_mask_ = 0;
|
| + uint8_t prefix_length_ = 0;
|
| +};
|
| +
|
| +// To help me and future debuggers of varint encodings, this LOGs out the
|
| +// transition points where a new extension byte is added.
|
| +TEST_F(HpackVarintDecoderTest, Encode) {
|
| + for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
|
| + const uint32_t a = (1 << prefix_length) - 1;
|
| + const uint32_t b = a + 128;
|
| + const uint32_t c = b + (127 << 7);
|
| + const uint32_t d = c + (127 << 14);
|
| + const uint32_t e = d + (127 << 21);
|
| +
|
| + LOG(INFO) << "############################################################";
|
| + LOG(INFO) << "prefix_length=" << prefix_length << " a=" << a
|
| + << " b=" << b << " c=" << c;
|
| +
|
| + EXPECT_EQ(a - 1,
|
| + HpackVarintDecoder::HiValueOfExtensionBytes(0, prefix_length));
|
| + EXPECT_EQ(b - 1,
|
| + HpackVarintDecoder::HiValueOfExtensionBytes(1, prefix_length));
|
| + EXPECT_EQ(c - 1,
|
| + HpackVarintDecoder::HiValueOfExtensionBytes(2, prefix_length));
|
| + EXPECT_EQ(d - 1,
|
| + HpackVarintDecoder::HiValueOfExtensionBytes(3, prefix_length));
|
| + EXPECT_EQ(e - 1,
|
| + HpackVarintDecoder::HiValueOfExtensionBytes(4, prefix_length));
|
| +
|
| + std::vector<uint32_t> values = {
|
| + 0, 1, // Force line break.
|
| + a - 2, a - 1, a, a + 1, a + 2, // Force line break.
|
| + b - 2, b - 1, b, b + 1, b + 2, // Force line break.
|
| + c - 2, c - 1, c, c + 1, c + 2, // Force line break.
|
| + d - 2, d - 1, d, d + 1, d + 2, // Force line break.
|
| + e - 2, e - 1, e, e + 1, e + 2 // Force line break.
|
| + };
|
| +
|
| + for (uint32_t value : values) {
|
| + EncodeNoRandom(value, prefix_length);
|
| + string dump = HexEncode(buffer_);
|
| + LOG(INFO) << StringPrintf("%10u %0#10x ", value, value)
|
| + << HexEncode(buffer_);
|
| + }
|
| + }
|
| +}
|
| +
|
| +TEST_F(HpackVarintDecoderTest, FromSpec1337) {
|
| + DecodeBuffer b(StringPiece("\x1f\x9a\x0a"));
|
| + uint32_t prefix_length = 5;
|
| + uint32_t prefix_mask = (1 << prefix_length) - 1;
|
| + uint8_t p = b.DecodeUInt8();
|
| + EXPECT_EQ(1u, b.Offset());
|
| + EXPECT_EQ(DecodeStatus::kDecodeDone, decoder_.Start(p, prefix_mask, &b));
|
| + EXPECT_EQ(3u, b.Offset());
|
| + EXPECT_EQ(1337u, decoder_.value());
|
| +
|
| + EncodeNoRandom(1337, prefix_length);
|
| + EXPECT_EQ(3u, buffer_.size());
|
| + EXPECT_EQ('\x1f', buffer_[0]);
|
| + EXPECT_EQ('\x9a', buffer_[1]);
|
| + EXPECT_EQ('\x0a', buffer_[2]);
|
| +}
|
| +
|
| +// Test all the values that fit into the prefix (one less than the mask).
|
| +TEST_F(HpackVarintDecoderTest, ValidatePrefixOnly) {
|
| + for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
|
| + const uint8_t prefix_mask = (1 << prefix_length) - 1;
|
| + EncodeAndDecodeValuesInRange(0, prefix_mask, prefix_length, 1);
|
| + }
|
| +}
|
| +
|
| +// Test all values that require exactly 1 extension byte.
|
| +TEST_F(HpackVarintDecoderTest, ValidateOneExtensionByte) {
|
| + for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
|
| + const uint32_t start = (1 << prefix_length) - 1;
|
| + EncodeAndDecodeValuesInRange(start, 128, prefix_length, 2);
|
| + }
|
| +}
|
| +
|
| +// Test *some* values that require exactly 2 extension bytes.
|
| +TEST_F(HpackVarintDecoderTest, ValidateTwoExtensionBytes) {
|
| + for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
|
| + const uint8_t prefix_mask = (1 << prefix_length) - 1;
|
| + const uint32_t start = prefix_mask + 128;
|
| + const uint32_t range = 127 << 7;
|
| +
|
| + EncodeAndDecodeValuesInRange(start, range, prefix_length, 3);
|
| + }
|
| +}
|
| +
|
| +// Test *some* values that require 3 extension bytes.
|
| +TEST_F(HpackVarintDecoderTest, ValidateThreeExtensionBytes) {
|
| + for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
|
| + const uint8_t prefix_mask = (1 << prefix_length) - 1;
|
| + const uint32_t start = prefix_mask + 128 + (127 << 7);
|
| + const uint32_t range = 127 << 14;
|
| +
|
| + EncodeAndDecodeValuesInRange(start, range, prefix_length, 4);
|
| + }
|
| +}
|
| +
|
| +// Test *some* values that require 4 extension bytes.
|
| +TEST_F(HpackVarintDecoderTest, ValidateFourExtensionBytes) {
|
| + for (int prefix_length = 4; prefix_length <= 7; ++prefix_length) {
|
| + const uint8_t prefix_mask = (1 << prefix_length) - 1;
|
| + const uint32_t start = prefix_mask + 128 + (127 << 7) + (127 << 14);
|
| + const uint32_t range = 127 << 21;
|
| +
|
| + EncodeAndDecodeValuesInRange(start, range, prefix_length, 5);
|
| + }
|
| +}
|
| +
|
| +// Test *some* values that require too many extension bytes.
|
| +TEST_F(HpackVarintDecoderTest, ValueTooLarge) {
|
| + const uint32_t expected_offset = HpackVarintDecoder::MaxExtensionBytes() + 1;
|
| + for (prefix_length_ = 4; prefix_length_ <= 7; ++prefix_length_) {
|
| + prefix_mask_ = (1 << prefix_length_) - 1;
|
| + uint64_t too_large = HpackVarintDecoder::HiValueOfExtensionBytes(
|
| + HpackVarintDecoder::MaxExtensionBytes() + 3, prefix_length_);
|
| + HpackBlockBuilder bb;
|
| + bb.AppendHighBitsAndVarint(0, prefix_length_, too_large);
|
| + buffer_ = bb.buffer();
|
| +
|
| + // The validator is called after each of the several times that the input
|
| + // DecodeBuffer is decoded, each with a different segmentation of the input.
|
| + // Validate that decoder_.value() matches the expected value.
|
| + bool validated = false;
|
| + Validator validator =
|
| + base::Bind(&HpackVarintDecoderTest::ValidatorForValueTooLarge,
|
| + base::Unretained(this), &validated, expected_offset);
|
| +
|
| + // 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;
|
| + DecodeBuffer b(buffer_);
|
| + EXPECT_TRUE(
|
| + DecodeAndValidateSeveralWays(&b, return_non_zero_on_first, validator));
|
| + EXPECT_EQ(expected_offset, b.Offset());
|
| + EXPECT_TRUE(validated);
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace test
|
| +} // namespace net
|
|
|