1 // Copyright 2016 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 #include "quiche/http2/hpack/decoder/hpack_entry_decoder.h"
6
7 // Tests of HpackEntryDecoder.
8
9 #include <cstdint>
10
11 #include "quiche/http2/test_tools/hpack_block_builder.h"
12 #include "quiche/http2/test_tools/hpack_entry_collector.h"
13 #include "quiche/http2/test_tools/http2_random.h"
14 #include "quiche/http2/test_tools/random_decoder_test_base.h"
15 #include "quiche/common/platform/api/quiche_expect_bug.h"
16 #include "quiche/common/platform/api/quiche_test.h"
17
18 namespace http2 {
19 namespace test {
20 namespace {
21
22 class HpackEntryDecoderTest : public RandomDecoderTest {
23 protected:
HpackEntryDecoderTest()24 HpackEntryDecoderTest() : listener_(&collector_) {}
25
StartDecoding(DecodeBuffer * b)26 DecodeStatus StartDecoding(DecodeBuffer* b) override {
27 collector_.Clear();
28 return decoder_.Start(b, &listener_);
29 }
30
ResumeDecoding(DecodeBuffer * b)31 DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
32 return decoder_.Resume(b, &listener_);
33 }
34
DecodeAndValidateSeveralWays(DecodeBuffer * db,const Validator & validator)35 AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* db,
36 const Validator& validator) {
37 // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
38 // can call Start with the prefix byte.
39 bool return_non_zero_on_first = true;
40 return RandomDecoderTest::DecodeAndValidateSeveralWays(
41 db, return_non_zero_on_first, validator);
42 }
43
DecodeAndValidateSeveralWays(const HpackBlockBuilder & hbb,const Validator & validator)44 AssertionResult DecodeAndValidateSeveralWays(const HpackBlockBuilder& hbb,
45 const Validator& validator) {
46 DecodeBuffer db(hbb.buffer());
47 return DecodeAndValidateSeveralWays(&db, validator);
48 }
49
50 HpackEntryDecoder decoder_;
51 HpackEntryCollector collector_;
52 HpackEntryDecoderVLoggingListener listener_;
53 };
54
TEST_F(HpackEntryDecoderTest,IndexedHeader_Literals)55 TEST_F(HpackEntryDecoderTest, IndexedHeader_Literals) {
56 {
57 const char input[] = {'\x82'}; // == Index 2 ==
58 DecodeBuffer b(input);
59 auto do_check = [this]() { return collector_.ValidateIndexedHeader(2); };
60 EXPECT_TRUE(
61 DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
62 EXPECT_TRUE(do_check());
63 }
64 collector_.Clear();
65 {
66 const char input[] = {'\xfe'}; // == Index 126 ==
67 DecodeBuffer b(input);
68 auto do_check = [this]() { return collector_.ValidateIndexedHeader(126); };
69 EXPECT_TRUE(
70 DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
71 EXPECT_TRUE(do_check());
72 }
73 collector_.Clear();
74 {
75 const char input[] = {'\xff', '\x00'}; // == Index 127 ==
76 DecodeBuffer b(input);
77 auto do_check = [this]() { return collector_.ValidateIndexedHeader(127); };
78 EXPECT_TRUE(
79 DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
80 EXPECT_TRUE(do_check());
81 }
82 }
83
TEST_F(HpackEntryDecoderTest,IndexedHeader_Various)84 TEST_F(HpackEntryDecoderTest, IndexedHeader_Various) {
85 // Indices chosen to hit encoding and table boundaries.
86 for (const uint32_t ndx : {1, 2, 61, 62, 63, 126, 127, 254, 255, 256}) {
87 HpackBlockBuilder hbb;
88 hbb.AppendIndexedHeader(ndx);
89
90 auto do_check = [this, ndx]() {
91 return collector_.ValidateIndexedHeader(ndx);
92 };
93 EXPECT_TRUE(
94 DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
95 EXPECT_TRUE(do_check());
96 }
97 }
98
TEST_F(HpackEntryDecoderTest,IndexedLiteralValue_Literal)99 TEST_F(HpackEntryDecoderTest, IndexedLiteralValue_Literal) {
100 const char input[] =
101 "\x7f" // == Literal indexed, name index 0x40 ==
102 "\x01" // 2nd byte of name index (0x01 + 0x3f == 0x40)
103 "\x0d" // Value length (13)
104 "custom-header"; // Value
105 DecodeBuffer b(input, sizeof input - 1);
106 auto do_check = [this]() {
107 return collector_.ValidateLiteralValueHeader(
108 HpackEntryType::kIndexedLiteralHeader, 0x40, false, "custom-header");
109 };
110 EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
111 EXPECT_TRUE(do_check());
112 }
113
TEST_F(HpackEntryDecoderTest,IndexedLiteralNameValue_Literal)114 TEST_F(HpackEntryDecoderTest, IndexedLiteralNameValue_Literal) {
115 const char input[] =
116 "\x40" // == Literal indexed ==
117 "\x0a" // Name length (10)
118 "custom-key" // Name
119 "\x0d" // Value length (13)
120 "custom-header"; // Value
121
122 DecodeBuffer b(input, sizeof input - 1);
123 auto do_check = [this]() {
124 return collector_.ValidateLiteralNameValueHeader(
125 HpackEntryType::kIndexedLiteralHeader, false, "custom-key", false,
126 "custom-header");
127 };
128 EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
129 EXPECT_TRUE(do_check());
130 }
131
TEST_F(HpackEntryDecoderTest,DynamicTableSizeUpdate_Literal)132 TEST_F(HpackEntryDecoderTest, DynamicTableSizeUpdate_Literal) {
133 // Size update, length 31.
134 const char input[] = "\x3f\x00";
135 DecodeBuffer b(input, 2);
136 auto do_check = [this]() {
137 return collector_.ValidateDynamicTableSizeUpdate(31);
138 };
139 EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
140 EXPECT_TRUE(do_check());
141 }
142
143 class HpackLiteralEntryDecoderTest
144 : public HpackEntryDecoderTest,
145 public ::testing::WithParamInterface<HpackEntryType> {
146 protected:
HpackLiteralEntryDecoderTest()147 HpackLiteralEntryDecoderTest() : entry_type_(GetParam()) {}
148
149 const HpackEntryType entry_type_;
150 };
151
152 INSTANTIATE_TEST_SUITE_P(
153 AllLiteralTypes, HpackLiteralEntryDecoderTest,
154 testing::Values(HpackEntryType::kIndexedLiteralHeader,
155 HpackEntryType::kUnindexedLiteralHeader,
156 HpackEntryType::kNeverIndexedLiteralHeader));
157
TEST_P(HpackLiteralEntryDecoderTest,RandNameIndexAndLiteralValue)158 TEST_P(HpackLiteralEntryDecoderTest, RandNameIndexAndLiteralValue) {
159 for (int n = 0; n < 10; n++) {
160 const uint32_t ndx = 1 + Random().Rand8();
161 const bool value_is_huffman_encoded = (n % 2) == 0;
162 const std::string value = Random().RandString(Random().Rand8());
163 HpackBlockBuilder hbb;
164 hbb.AppendNameIndexAndLiteralValue(entry_type_, ndx,
165 value_is_huffman_encoded, value);
166 auto do_check = [this, ndx, value_is_huffman_encoded,
167 value]() -> AssertionResult {
168 return collector_.ValidateLiteralValueHeader(
169 entry_type_, ndx, value_is_huffman_encoded, value);
170 };
171 EXPECT_TRUE(
172 DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
173 EXPECT_TRUE(do_check());
174 }
175 }
176
TEST_P(HpackLiteralEntryDecoderTest,RandLiteralNameAndValue)177 TEST_P(HpackLiteralEntryDecoderTest, RandLiteralNameAndValue) {
178 for (int n = 0; n < 10; n++) {
179 const bool name_is_huffman_encoded = (n & 1) == 0;
180 const int name_len = 1 + Random().Rand8();
181 const std::string name = Random().RandString(name_len);
182 const bool value_is_huffman_encoded = (n & 2) == 0;
183 const int value_len = Random().Skewed(10);
184 const std::string value = Random().RandString(value_len);
185 HpackBlockBuilder hbb;
186 hbb.AppendLiteralNameAndValue(entry_type_, name_is_huffman_encoded, name,
187 value_is_huffman_encoded, value);
188 auto do_check = [this, name_is_huffman_encoded, name,
189 value_is_huffman_encoded, value]() -> AssertionResult {
190 return collector_.ValidateLiteralNameValueHeader(
191 entry_type_, name_is_huffman_encoded, name, value_is_huffman_encoded,
192 value);
193 };
194 EXPECT_TRUE(
195 DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
196 EXPECT_TRUE(do_check());
197 }
198 }
199
200 } // namespace
201 } // namespace test
202 } // namespace http2
203