blob: 82244efe4eb74e567e4fe0cfe8dc570d4e5210f8 [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 "http2/hpack/decoder/hpack_decoder_string_buffer.h"
// Tests of HpackDecoderStringBuffer.
#include <initializer_list>
#include "absl/strings/escaping.h"
#include "http2/platform/api/http2_logging.h"
#include "http2/platform/api/http2_test_helpers.h"
#include "common/platform/api/quiche_test.h"
using ::testing::AssertionResult;
using ::testing::AssertionSuccess;
using ::testing::HasSubstr;
namespace http2 {
namespace test {
namespace {
class HpackDecoderStringBufferTest : public QuicheTest {
protected:
typedef HpackDecoderStringBuffer::State State;
typedef HpackDecoderStringBuffer::Backing Backing;
State state() const { return buf_.state_for_testing(); }
Backing backing() const { return buf_.backing_for_testing(); }
// We want to know that HTTP2_LOG(x) << buf_ will work in production should
// that be needed, so we test that it outputs the expected values.
AssertionResult VerifyLogHasSubstrs(std::initializer_list<std::string> strs) {
HTTP2_VLOG(1) << buf_;
std::ostringstream ss;
buf_.OutputDebugStringTo(ss);
std::string dbg_str(ss.str());
for (const auto& expected : strs) {
VERIFY_THAT(dbg_str, HasSubstr(expected));
}
return AssertionSuccess();
}
HpackDecoderStringBuffer buf_;
};
TEST_F(HpackDecoderStringBufferTest, SetStatic) {
absl::string_view data("static string");
EXPECT_EQ(state(), State::RESET);
EXPECT_TRUE(VerifyLogHasSubstrs({"state=RESET"}));
buf_.Set(data, /*is_static*/ true);
HTTP2_LOG(INFO) << buf_;
EXPECT_EQ(state(), State::COMPLETE);
EXPECT_EQ(backing(), Backing::STATIC);
EXPECT_EQ(data, buf_.str());
EXPECT_EQ(data.data(), buf_.str().data());
EXPECT_TRUE(VerifyLogHasSubstrs(
{"state=COMPLETE", "backing=STATIC", "value: static string"}));
// The string is static, so BufferStringIfUnbuffered won't change anything.
buf_.BufferStringIfUnbuffered();
EXPECT_EQ(state(), State::COMPLETE);
EXPECT_EQ(backing(), Backing::STATIC);
EXPECT_EQ(data, buf_.str());
EXPECT_EQ(data.data(), buf_.str().data());
EXPECT_TRUE(VerifyLogHasSubstrs(
{"state=COMPLETE", "backing=STATIC", "value: static string"}));
}
TEST_F(HpackDecoderStringBufferTest, PlainWhole) {
absl::string_view data("some text.");
HTTP2_LOG(INFO) << buf_;
EXPECT_EQ(state(), State::RESET);
buf_.OnStart(/*huffman_encoded*/ false, data.size());
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::RESET);
HTTP2_LOG(INFO) << buf_;
EXPECT_TRUE(buf_.OnData(data.data(), data.size()));
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::UNBUFFERED);
EXPECT_TRUE(buf_.OnEnd());
EXPECT_EQ(state(), State::COMPLETE);
EXPECT_EQ(backing(), Backing::UNBUFFERED);
EXPECT_EQ(0u, buf_.BufferedLength());
EXPECT_TRUE(VerifyLogHasSubstrs(
{"state=COMPLETE", "backing=UNBUFFERED", "value: some text."}));
// We expect that the string buffer points to the passed in
// string_view's backing store.
EXPECT_EQ(data.data(), buf_.str().data());
// Now force it to buffer the string, after which it will still have the same
// string value, but the backing store will be different.
buf_.BufferStringIfUnbuffered();
HTTP2_LOG(INFO) << buf_;
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), data.size());
EXPECT_EQ(data, buf_.str());
EXPECT_NE(data.data(), buf_.str().data());
EXPECT_TRUE(VerifyLogHasSubstrs(
{"state=COMPLETE", "backing=BUFFERED", "buffer: some text."}));
}
TEST_F(HpackDecoderStringBufferTest, PlainSplit) {
absl::string_view data("some text.");
absl::string_view part1 = data.substr(0, 1);
absl::string_view part2 = data.substr(1);
EXPECT_EQ(state(), State::RESET);
buf_.OnStart(/*huffman_encoded*/ false, data.size());
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::RESET);
// OnData with only a part of the data, not the whole, so buf_ will buffer
// the data.
EXPECT_TRUE(buf_.OnData(part1.data(), part1.size()));
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), part1.size());
HTTP2_LOG(INFO) << buf_;
EXPECT_TRUE(buf_.OnData(part2.data(), part2.size()));
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), data.size());
EXPECT_TRUE(buf_.OnEnd());
EXPECT_EQ(state(), State::COMPLETE);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), data.size());
HTTP2_LOG(INFO) << buf_;
absl::string_view buffered = buf_.str();
EXPECT_EQ(data, buffered);
EXPECT_NE(data.data(), buffered.data());
// The string is already buffered, so BufferStringIfUnbuffered should not make
// any change.
buf_.BufferStringIfUnbuffered();
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), data.size());
EXPECT_EQ(buffered, buf_.str());
EXPECT_EQ(buffered.data(), buf_.str().data());
}
TEST_F(HpackDecoderStringBufferTest, HuffmanWhole) {
std::string encoded = absl::HexStringToBytes("f1e3c2e5f23a6ba0ab90f4ff");
absl::string_view decoded("www.example.com");
EXPECT_EQ(state(), State::RESET);
buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_TRUE(buf_.OnData(encoded.data(), encoded.size()));
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_TRUE(buf_.OnEnd());
EXPECT_EQ(state(), State::COMPLETE);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), decoded.size());
EXPECT_EQ(decoded, buf_.str());
EXPECT_TRUE(VerifyLogHasSubstrs(
{"{state=COMPLETE", "backing=BUFFERED", "buffer: www.example.com}"}));
std::string s = buf_.ReleaseString();
EXPECT_EQ(s, decoded);
EXPECT_EQ(state(), State::RESET);
}
TEST_F(HpackDecoderStringBufferTest, HuffmanSplit) {
std::string encoded = absl::HexStringToBytes("f1e3c2e5f23a6ba0ab90f4ff");
std::string part1 = encoded.substr(0, 5);
std::string part2 = encoded.substr(5);
absl::string_view decoded("www.example.com");
EXPECT_EQ(state(), State::RESET);
buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(0u, buf_.BufferedLength());
HTTP2_LOG(INFO) << buf_;
EXPECT_TRUE(buf_.OnData(part1.data(), part1.size()));
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_GT(buf_.BufferedLength(), 0u);
EXPECT_LT(buf_.BufferedLength(), decoded.size());
HTTP2_LOG(INFO) << buf_;
EXPECT_TRUE(buf_.OnData(part2.data(), part2.size()));
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), decoded.size());
HTTP2_LOG(INFO) << buf_;
EXPECT_TRUE(buf_.OnEnd());
EXPECT_EQ(state(), State::COMPLETE);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_EQ(buf_.BufferedLength(), decoded.size());
EXPECT_EQ(decoded, buf_.str());
HTTP2_LOG(INFO) << buf_;
buf_.Reset();
EXPECT_EQ(state(), State::RESET);
HTTP2_LOG(INFO) << buf_;
}
TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnData) {
// Explicitly encode the End-of-String symbol, a no-no.
std::string encoded = absl::HexStringToBytes("ffffffff");
buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_FALSE(buf_.OnData(encoded.data(), encoded.size()));
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
HTTP2_LOG(INFO) << buf_;
}
TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnEnd) {
// Last byte of string doesn't end with prefix of End-of-String symbol.
std::string encoded = absl::HexStringToBytes("00");
buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_TRUE(buf_.OnData(encoded.data(), encoded.size()));
EXPECT_EQ(state(), State::COLLECTING);
EXPECT_EQ(backing(), Backing::BUFFERED);
EXPECT_FALSE(buf_.OnEnd());
HTTP2_LOG(INFO) << buf_;
}
// TODO(jamessynge): Add tests for ReleaseString().
} // namespace
} // namespace test
} // namespace http2