|  | // 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_string_decoder.h" | 
|  |  | 
|  | // Tests of HpackStringDecoder. | 
|  |  | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "http2/hpack/decoder/hpack_string_collector.h" | 
|  | #include "http2/hpack/decoder/hpack_string_decoder_listener.h" | 
|  | #include "http2/hpack/tools/hpack_block_builder.h" | 
|  | #include "http2/platform/api/http2_test_helpers.h" | 
|  | #include "http2/test_tools/http2_random.h" | 
|  | #include "http2/tools/random_decoder_test.h" | 
|  | #include "common/platform/api/quiche_test.h" | 
|  |  | 
|  | using ::testing::AssertionResult; | 
|  |  | 
|  | namespace http2 { | 
|  | namespace test { | 
|  | namespace { | 
|  |  | 
|  | const bool kMayReturnZeroOnFirst = false; | 
|  | const bool kCompressed = true; | 
|  | const bool kUncompressed = false; | 
|  |  | 
|  | class HpackStringDecoderTest : public RandomDecoderTest { | 
|  | protected: | 
|  | HpackStringDecoderTest() : listener_(&collector_) {} | 
|  |  | 
|  | DecodeStatus StartDecoding(DecodeBuffer* b) override { | 
|  | ++start_decoding_calls_; | 
|  | collector_.Clear(); | 
|  | return decoder_.Start(b, &listener_); | 
|  | } | 
|  |  | 
|  | DecodeStatus ResumeDecoding(DecodeBuffer* b) override { | 
|  | // Provides coverage of DebugString and StateToString. | 
|  | // Not validating output. | 
|  | HTTP2_VLOG(1) << decoder_.DebugString(); | 
|  | HTTP2_VLOG(2) << collector_; | 
|  | return decoder_.Resume(b, &listener_); | 
|  | } | 
|  |  | 
|  | AssertionResult Collected(absl::string_view s, bool huffman_encoded) { | 
|  | HTTP2_VLOG(1) << collector_; | 
|  | return collector_.Collected(s, huffman_encoded); | 
|  | } | 
|  |  | 
|  | // expected_str is a std::string rather than a const std::string& or | 
|  | // absl::string_view so that the lambda makes a copy of the string, and thus | 
|  | // the string to be passed to Collected outlives the call to MakeValidator. | 
|  | Validator MakeValidator(const std::string& expected_str, | 
|  | bool expected_huffman) { | 
|  | return | 
|  | [expected_str, expected_huffman, this]( | 
|  | const DecodeBuffer& /*input*/, | 
|  | DecodeStatus /*status*/) -> AssertionResult { | 
|  | AssertionResult result = Collected(expected_str, expected_huffman); | 
|  | if (result) { | 
|  | VERIFY_EQ(collector_, | 
|  | HpackStringCollector(expected_str, expected_huffman)); | 
|  | } else { | 
|  | VERIFY_NE(collector_, | 
|  | HpackStringCollector(expected_str, expected_huffman)); | 
|  | } | 
|  | HTTP2_VLOG(2) << collector_.ToString(); | 
|  | collector_.Clear(); | 
|  | HTTP2_VLOG(2) << collector_; | 
|  | return result; | 
|  | }; | 
|  | } | 
|  |  | 
|  | HpackStringDecoder decoder_; | 
|  | HpackStringCollector collector_; | 
|  | HpackStringDecoderVLoggingListener listener_; | 
|  | size_t start_decoding_calls_ = 0; | 
|  | }; | 
|  |  | 
|  | TEST_F(HpackStringDecoderTest, DecodeEmptyString) { | 
|  | { | 
|  | Validator validator = ValidateDoneAndEmpty(MakeValidator("", kCompressed)); | 
|  | const char kData[] = {'\x80'}; | 
|  | DecodeBuffer b(kData); | 
|  | EXPECT_TRUE( | 
|  | DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator)); | 
|  | } | 
|  | { | 
|  | // Make sure it stops after decoding the empty string. | 
|  | Validator validator = | 
|  | ValidateDoneAndOffset(1, MakeValidator("", kUncompressed)); | 
|  | const char kData[] = {'\x00', '\xff'}; | 
|  | DecodeBuffer b(kData); | 
|  | EXPECT_EQ(2u, b.Remaining()); | 
|  | EXPECT_TRUE( | 
|  | DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator)); | 
|  | EXPECT_EQ(1u, b.Remaining()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(HpackStringDecoderTest, DecodeShortString) { | 
|  | { | 
|  | // Make sure it stops after decoding the non-empty string. | 
|  | Validator validator = | 
|  | ValidateDoneAndOffset(11, MakeValidator("start end.", kCompressed)); | 
|  | const char kData[] = "\x8astart end.Don't peek at this."; | 
|  | DecodeBuffer b(kData); | 
|  | EXPECT_TRUE( | 
|  | DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator)); | 
|  | } | 
|  | { | 
|  | Validator validator = | 
|  | ValidateDoneAndOffset(11, MakeValidator("start end.", kUncompressed)); | 
|  | absl::string_view data("\x0astart end."); | 
|  | DecodeBuffer b(data); | 
|  | EXPECT_TRUE( | 
|  | DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator)); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(HpackStringDecoderTest, DecodeLongStrings) { | 
|  | std::string name = Random().RandString(1024); | 
|  | std::string value = Random().RandString(65536); | 
|  | HpackBlockBuilder hbb; | 
|  |  | 
|  | hbb.AppendString(false, name); | 
|  | uint32_t offset_after_name = hbb.size(); | 
|  | EXPECT_EQ(3 + name.size(), offset_after_name); | 
|  |  | 
|  | hbb.AppendString(true, value); | 
|  | uint32_t offset_after_value = hbb.size(); | 
|  | EXPECT_EQ(3 + name.size() + 4 + value.size(), offset_after_value); | 
|  |  | 
|  | DecodeBuffer b(hbb.buffer()); | 
|  |  | 
|  | // Decode the name... | 
|  | EXPECT_TRUE(DecodeAndValidateSeveralWays( | 
|  | &b, kMayReturnZeroOnFirst, | 
|  | ValidateDoneAndOffset(offset_after_name, | 
|  | MakeValidator(name, kUncompressed)))); | 
|  | EXPECT_EQ(offset_after_name, b.Offset()); | 
|  | EXPECT_EQ(offset_after_value - offset_after_name, b.Remaining()); | 
|  |  | 
|  | // Decode the value... | 
|  | EXPECT_TRUE(DecodeAndValidateSeveralWays( | 
|  | &b, kMayReturnZeroOnFirst, | 
|  | ValidateDoneAndOffset(offset_after_value - offset_after_name, | 
|  | MakeValidator(value, kCompressed)))); | 
|  | EXPECT_EQ(offset_after_value, b.Offset()); | 
|  | EXPECT_EQ(0u, b.Remaining()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace test | 
|  | }  // namespace http2 |