1 // Copyright (c) 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // A test of roundtrips through the encoder and decoder.
6
7 #include <stddef.h>
8
9 #include "absl/strings/string_view.h"
10 #include "quiche/http2/decoder/decode_buffer.h"
11 #include "quiche/http2/decoder/decode_status.h"
12 #include "quiche/http2/hpack/huffman/hpack_huffman_decoder.h"
13 #include "quiche/http2/hpack/huffman/hpack_huffman_encoder.h"
14 #include "quiche/http2/test_tools/random_decoder_test_base.h"
15 #include "quiche/common/platform/api/quiche_test.h"
16 #include "quiche/common/quiche_text_utils.h"
17
18 using ::testing::AssertionSuccess;
19 using ::testing::Combine;
20 using ::testing::Range;
21 using ::testing::Values;
22
23 namespace http2 {
24 namespace test {
25 namespace {
26
GenAsciiNonControlSet()27 std::string GenAsciiNonControlSet() {
28 std::string s;
29 const char space = ' '; // First character after the control characters: 0x20
30 const char del = 127; // First character after the non-control characters.
31 for (char c = space; c < del; ++c) {
32 s.push_back(c);
33 }
34 return s;
35 }
36
37 class HpackHuffmanTranscoderTest : public RandomDecoderTest {
38 protected:
HpackHuffmanTranscoderTest()39 HpackHuffmanTranscoderTest()
40 : ascii_non_control_set_(GenAsciiNonControlSet()) {
41 // The decoder may return true, and its accumulator may be empty, at
42 // many boundaries while decoding, and yet the whole string hasn't
43 // been decoded.
44 stop_decode_on_done_ = false;
45 }
46
StartDecoding(DecodeBuffer * b)47 DecodeStatus StartDecoding(DecodeBuffer* b) override {
48 input_bytes_seen_ = 0;
49 output_buffer_.clear();
50 decoder_.Reset();
51 return ResumeDecoding(b);
52 }
53
ResumeDecoding(DecodeBuffer * b)54 DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
55 input_bytes_seen_ += b->Remaining();
56 absl::string_view sp(b->cursor(), b->Remaining());
57 if (decoder_.Decode(sp, &output_buffer_)) {
58 b->AdvanceCursor(b->Remaining());
59 // Successfully decoded (or buffered) the bytes in absl::string_view.
60 EXPECT_LE(input_bytes_seen_, input_bytes_expected_);
61 // Have we reached the end of the encoded string?
62 if (input_bytes_expected_ == input_bytes_seen_) {
63 if (decoder_.InputProperlyTerminated()) {
64 return DecodeStatus::kDecodeDone;
65 } else {
66 return DecodeStatus::kDecodeError;
67 }
68 }
69 return DecodeStatus::kDecodeInProgress;
70 }
71 return DecodeStatus::kDecodeError;
72 }
73
TranscodeAndValidateSeveralWays(absl::string_view plain,absl::string_view expected_huffman)74 AssertionResult TranscodeAndValidateSeveralWays(
75 absl::string_view plain, absl::string_view expected_huffman) {
76 size_t encoded_size = HuffmanSize(plain);
77 std::string encoded;
78 HuffmanEncode(plain, encoded_size, &encoded);
79 HTTP2_VERIFY_EQ(encoded_size, encoded.size());
80 if (!expected_huffman.empty() || plain.empty()) {
81 HTTP2_VERIFY_EQ(encoded, expected_huffman);
82 }
83 input_bytes_expected_ = encoded.size();
84 auto validator = [plain, this]() -> AssertionResult {
85 HTTP2_VERIFY_EQ(output_buffer_.size(), plain.size());
86 HTTP2_VERIFY_EQ(output_buffer_, plain);
87 return AssertionSuccess();
88 };
89 DecodeBuffer db(encoded);
90 bool return_non_zero_on_first = false;
91 return DecodeAndValidateSeveralWays(&db, return_non_zero_on_first,
92 ValidateDoneAndEmpty(validator));
93 }
94
TranscodeAndValidateSeveralWays(absl::string_view plain)95 AssertionResult TranscodeAndValidateSeveralWays(absl::string_view plain) {
96 return TranscodeAndValidateSeveralWays(plain, "");
97 }
98
RandomAsciiNonControlString(int length)99 std::string RandomAsciiNonControlString(int length) {
100 return Random().RandStringWithAlphabet(length, ascii_non_control_set_);
101 }
102
RandomBytes(int length)103 std::string RandomBytes(int length) { return Random().RandString(length); }
104
105 const std::string ascii_non_control_set_;
106 HpackHuffmanDecoder decoder_;
107 std::string output_buffer_;
108 size_t input_bytes_seen_;
109 size_t input_bytes_expected_;
110 };
111
TEST_F(HpackHuffmanTranscoderTest,RoundTripRandomAsciiNonControlString)112 TEST_F(HpackHuffmanTranscoderTest, RoundTripRandomAsciiNonControlString) {
113 for (size_t length = 0; length != 20; length++) {
114 const std::string s = RandomAsciiNonControlString(length);
115 ASSERT_TRUE(TranscodeAndValidateSeveralWays(s))
116 << "Unable to decode:\n\n"
117 << quiche::QuicheTextUtils::HexDump(s) << "\n\noutput_buffer_:\n"
118 << quiche::QuicheTextUtils::HexDump(output_buffer_);
119 }
120 }
121
TEST_F(HpackHuffmanTranscoderTest,RoundTripRandomBytes)122 TEST_F(HpackHuffmanTranscoderTest, RoundTripRandomBytes) {
123 for (size_t length = 0; length != 20; length++) {
124 const std::string s = RandomBytes(length);
125 ASSERT_TRUE(TranscodeAndValidateSeveralWays(s))
126 << "Unable to decode:\n\n"
127 << quiche::QuicheTextUtils::HexDump(s) << "\n\noutput_buffer_:\n"
128 << quiche::QuicheTextUtils::HexDump(output_buffer_);
129 }
130 }
131
132 // Two parameters: decoder choice, and the character to round-trip.
133 class HpackHuffmanTranscoderAdjacentCharTest
134 : public HpackHuffmanTranscoderTest,
135 public testing::WithParamInterface<int> {
136 protected:
HpackHuffmanTranscoderAdjacentCharTest()137 HpackHuffmanTranscoderAdjacentCharTest()
138 : c_(static_cast<char>(GetParam())) {}
139
140 const char c_;
141 };
142
143 INSTANTIATE_TEST_SUITE_P(HpackHuffmanTranscoderAdjacentCharTest,
144 HpackHuffmanTranscoderAdjacentCharTest, Range(0, 256));
145
146 // Test c_ adjacent to every other character, both before and after.
TEST_P(HpackHuffmanTranscoderAdjacentCharTest,RoundTripAdjacentChar)147 TEST_P(HpackHuffmanTranscoderAdjacentCharTest, RoundTripAdjacentChar) {
148 std::string s;
149 for (int a = 0; a < 256; ++a) {
150 s.push_back(static_cast<char>(a));
151 s.push_back(c_);
152 s.push_back(static_cast<char>(a));
153 }
154 ASSERT_TRUE(TranscodeAndValidateSeveralWays(s));
155 }
156
157 // Two parameters: character to repeat, number of repeats.
158 class HpackHuffmanTranscoderRepeatedCharTest
159 : public HpackHuffmanTranscoderTest,
160 public testing::WithParamInterface<std::tuple<int, int>> {
161 protected:
HpackHuffmanTranscoderRepeatedCharTest()162 HpackHuffmanTranscoderRepeatedCharTest()
163 : c_(static_cast<char>(std::get<0>(GetParam()))),
164 length_(std::get<1>(GetParam())) {}
MakeString()165 std::string MakeString() { return std::string(length_, c_); }
166
167 private:
168 const char c_;
169 const size_t length_;
170 };
171
172 INSTANTIATE_TEST_SUITE_P(HpackHuffmanTranscoderRepeatedCharTest,
173 HpackHuffmanTranscoderRepeatedCharTest,
174 Combine(Range(0, 256), Values(1, 2, 3, 4, 8, 16, 32)));
175
TEST_P(HpackHuffmanTranscoderRepeatedCharTest,RoundTripRepeatedChar)176 TEST_P(HpackHuffmanTranscoderRepeatedCharTest, RoundTripRepeatedChar) {
177 ASSERT_TRUE(TranscodeAndValidateSeveralWays(MakeString()));
178 }
179
180 } // namespace
181 } // namespace test
182 } // namespace http2
183