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