blob: 30b586fd2a631efd89a358e5f0dbcc014b7a0eab [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 "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h"
// Tests of HpackHuffmanDecoder and HuffmanBitBuffer.
#include <iostream>
#include "testing/gtest/include/gtest/gtest.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_arraysize.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
#include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h"
#include "net/third_party/quiche/src/http2/tools/random_decoder_test.h"
using ::testing::AssertionResult;
namespace http2 {
namespace test {
namespace {
TEST(HuffmanBitBufferTest, Reset) {
HuffmanBitBuffer bb;
EXPECT_TRUE(bb.IsEmpty());
EXPECT_TRUE(bb.InputProperlyTerminated());
EXPECT_EQ(bb.count(), 0u);
EXPECT_EQ(bb.free_count(), 64u);
EXPECT_EQ(bb.value(), 0u);
}
TEST(HuffmanBitBufferTest, AppendBytesAligned) {
Http2String s;
s.push_back('\x11');
s.push_back('\x22');
s.push_back('\x33');
Http2StringPiece sp(s);
HuffmanBitBuffer bb;
sp.remove_prefix(bb.AppendBytes(sp));
EXPECT_TRUE(sp.empty());
EXPECT_FALSE(bb.IsEmpty()) << bb;
EXPECT_FALSE(bb.InputProperlyTerminated());
EXPECT_EQ(bb.count(), 24u) << bb;
EXPECT_EQ(bb.free_count(), 40u) << bb;
EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 40) << bb;
s.clear();
s.push_back('\x44');
sp = s;
sp.remove_prefix(bb.AppendBytes(sp));
EXPECT_TRUE(sp.empty());
EXPECT_EQ(bb.count(), 32u) << bb;
EXPECT_EQ(bb.free_count(), 32u) << bb;
EXPECT_EQ(bb.value(), HuffmanAccumulator(0x11223344) << 32) << bb;
s.clear();
s.push_back('\x55');
s.push_back('\x66');
s.push_back('\x77');
s.push_back('\x88');
s.push_back('\x99');
sp = s;
sp.remove_prefix(bb.AppendBytes(sp));
EXPECT_EQ(sp.size(), 1u);
EXPECT_EQ('\x99', sp[0]);
EXPECT_EQ(bb.count(), 64u) << bb;
EXPECT_EQ(bb.free_count(), 0u) << bb;
EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb;
sp.remove_prefix(bb.AppendBytes(sp));
EXPECT_EQ(sp.size(), 1u);
EXPECT_EQ('\x99', sp[0]);
EXPECT_EQ(bb.count(), 64u) << bb;
EXPECT_EQ(bb.free_count(), 0u) << bb;
EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb;
}
TEST(HuffmanBitBufferTest, ConsumeBits) {
Http2String s;
s.push_back('\x11');
s.push_back('\x22');
s.push_back('\x33');
Http2StringPiece sp(s);
HuffmanBitBuffer bb;
sp.remove_prefix(bb.AppendBytes(sp));
EXPECT_TRUE(sp.empty());
bb.ConsumeBits(1);
EXPECT_EQ(bb.count(), 23u) << bb;
EXPECT_EQ(bb.free_count(), 41u) << bb;
EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 41) << bb;
bb.ConsumeBits(20);
EXPECT_EQ(bb.count(), 3u) << bb;
EXPECT_EQ(bb.free_count(), 61u) << bb;
EXPECT_EQ(bb.value(), HuffmanAccumulator(0x3) << 61) << bb;
}
TEST(HuffmanBitBufferTest, AppendBytesUnaligned) {
Http2String s;
s.push_back('\x11');
s.push_back('\x22');
s.push_back('\x33');
s.push_back('\x44');
s.push_back('\x55');
s.push_back('\x66');
s.push_back('\x77');
s.push_back('\x88');
s.push_back('\x99');
s.push_back('\xaa');
s.push_back('\xbb');
s.push_back('\xcc');
s.push_back('\xdd');
Http2StringPiece sp(s);
HuffmanBitBuffer bb;
sp.remove_prefix(bb.AppendBytes(sp));
EXPECT_EQ(sp.size(), 5u);
EXPECT_FALSE(bb.InputProperlyTerminated());
bb.ConsumeBits(15);
EXPECT_EQ(bb.count(), 49u) << bb;
EXPECT_EQ(bb.free_count(), 15u) << bb;
HuffmanAccumulator expected(0x1122334455667788);
expected <<= 15;
EXPECT_EQ(bb.value(), expected);
sp.remove_prefix(bb.AppendBytes(sp));
EXPECT_EQ(sp.size(), 4u);
EXPECT_EQ(bb.count(), 57u) << bb;
EXPECT_EQ(bb.free_count(), 7u) << bb;
expected |= (HuffmanAccumulator(0x99) << 7);
EXPECT_EQ(bb.value(), expected)
<< bb << std::hex << "\n actual: " << bb.value()
<< "\n expected: " << expected;
}
class HpackHuffmanDecoderTest : public RandomDecoderTest {
protected:
HpackHuffmanDecoderTest() {
// The decoder may return true, and its accumulator may be empty, at
// many boundaries while decoding, and yet the whole string hasn't
// been decoded.
stop_decode_on_done_ = false;
}
DecodeStatus StartDecoding(DecodeBuffer* b) override {
input_bytes_seen_ = 0;
output_buffer_.clear();
decoder_.Reset();
return ResumeDecoding(b);
}
DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
input_bytes_seen_ += b->Remaining();
Http2StringPiece sp(b->cursor(), b->Remaining());
if (decoder_.Decode(sp, &output_buffer_)) {
b->AdvanceCursor(b->Remaining());
// Successfully decoded (or buffered) the bytes in Http2StringPiece.
EXPECT_LE(input_bytes_seen_, input_bytes_expected_);
// Have we reached the end of the encoded string?
if (input_bytes_expected_ == input_bytes_seen_) {
if (decoder_.InputProperlyTerminated()) {
return DecodeStatus::kDecodeDone;
} else {
return DecodeStatus::kDecodeError;
}
}
return DecodeStatus::kDecodeInProgress;
}
return DecodeStatus::kDecodeError;
}
HpackHuffmanDecoder decoder_;
Http2String output_buffer_;
size_t input_bytes_seen_;
size_t input_bytes_expected_;
};
TEST_F(HpackHuffmanDecoderTest, SpecRequestExamples) {
HpackHuffmanDecoder decoder;
Http2String test_table[] = {
Http2HexDecode("f1e3c2e5f23a6ba0ab90f4ff"),
"www.example.com",
Http2HexDecode("a8eb10649cbf"),
"no-cache",
Http2HexDecode("25a849e95ba97d7f"),
"custom-key",
Http2HexDecode("25a849e95bb8e8b4bf"),
"custom-value",
};
for (size_t i = 0; i != HTTP2_ARRAYSIZE(test_table); i += 2) {
const Http2String& huffman_encoded(test_table[i]);
const Http2String& plain_string(test_table[i + 1]);
Http2String buffer;
decoder.Reset();
EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder;
EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder;
EXPECT_EQ(buffer, plain_string);
}
}
TEST_F(HpackHuffmanDecoderTest, SpecResponseExamples) {
HpackHuffmanDecoder decoder;
// clang-format off
Http2String test_table[] = {
Http2HexDecode("6402"),
"302",
Http2HexDecode("aec3771a4b"),
"private",
Http2HexDecode("d07abe941054d444a8200595040b8166"
"e082a62d1bff"),
"Mon, 21 Oct 2013 20:13:21 GMT",
Http2HexDecode("9d29ad171863c78f0b97c8e9ae82ae43"
"d3"),
"https://www.example.com",
Http2HexDecode("94e7821dd7f2e6c7b335dfdfcd5b3960"
"d5af27087f3672c1ab270fb5291f9587"
"316065c003ed4ee5b1063d5007"),
"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
};
// clang-format on
for (size_t i = 0; i != HTTP2_ARRAYSIZE(test_table); i += 2) {
const Http2String& huffman_encoded(test_table[i]);
const Http2String& plain_string(test_table[i + 1]);
Http2String buffer;
decoder.Reset();
EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder;
EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder;
EXPECT_EQ(buffer, plain_string);
}
}
} // namespace
} // namespace test
} // namespace http2