|  | // 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 |