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