| // 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 "quiche/http2/hpack/decoder/hpack_string_decoder.h" |
| |
| // Tests of HpackStringDecoder. |
| |
| #include "absl/strings/string_view.h" |
| #include "quiche/http2/hpack/decoder/hpack_string_decoder_listener.h" |
| #include "quiche/http2/test_tools/hpack_block_builder.h" |
| #include "quiche/http2/test_tools/hpack_string_collector.h" |
| #include "quiche/http2/test_tools/http2_random.h" |
| #include "quiche/http2/test_tools/random_decoder_test_base.h" |
| #include "quiche/http2/test_tools/verify_macros.h" |
| #include "quiche/common/platform/api/quiche_test.h" |
| |
| 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. |
| QUICHE_VLOG(1) << decoder_.DebugString(); |
| QUICHE_VLOG(2) << collector_; |
| return decoder_.Resume(b, &listener_); |
| } |
| |
| AssertionResult Collected(absl::string_view s, bool huffman_encoded) { |
| QUICHE_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) { |
| HTTP2_VERIFY_EQ(collector_, |
| HpackStringCollector(expected_str, expected_huffman)); |
| } else { |
| HTTP2_VERIFY_NE(collector_, |
| HpackStringCollector(expected_str, expected_huffman)); |
| } |
| QUICHE_VLOG(2) << collector_.ToString(); |
| collector_.Clear(); |
| QUICHE_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 |