Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(55)

Side by Side Diff: net/http2/hpack/decoder/hpack_decoder_tables_test.cc

Issue 2606733004: Add HpackDecoderTables, static and dynamic tables for decoding HPACK. (Closed)
Patch Set: Remove NET_EXPORT from files in the test build target. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "net/http2/hpack/decoder/hpack_decoder_tables.h"
6
7 #include <algorithm>
8 #include <string>
9 #include <tuple>
10 #include <vector>
11
12 #include "base/logging.h"
13 #include "net/http2/tools/failure.h"
14 #include "net/http2/tools/http2_random.h"
15 #include "net/http2/tools/random_util.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 using ::testing::AssertionResult;
20 using ::testing::AssertionSuccess;
21 using std::string;
22
23 namespace net {
24 namespace test {
25 class HpackDecoderTablesPeer {
26 public:
27 static size_t num_dynamic_entries(const HpackDecoderTables& tables) {
28 return tables.dynamic_table_.table_.size();
29 }
30 };
31
32 namespace {
33 struct StaticEntry {
34 const char* name;
35 const char* value;
36 size_t index;
37 };
38
39 std::vector<StaticEntry> MakeSpecStaticEntries() {
40 std::vector<StaticEntry> static_entries;
41
42 #define STATIC_TABLE_ENTRY(name, value, index) \
43 DCHECK_EQ(static_entries.size() + 1, index); \
44 static_entries.push_back({name, value, index});
45
46 #include "net/http2/hpack/hpack_static_table_entries.inc"
47
48 #undef STATIC_TABLE_ENTRY
49
50 return static_entries;
51 }
52
53 template <class C>
54 void ShuffleCollection(C* collection, RandomBase* r) {
55 std::shuffle(collection->begin(), collection->end(), *r);
56 }
57
58 class HpackDecoderStaticTableTest : public ::testing::Test {
59 protected:
60 HpackDecoderStaticTableTest() {}
61
62 std::vector<StaticEntry> shuffled_static_entries() {
63 std::vector<StaticEntry> entries = MakeSpecStaticEntries();
64 ShuffleCollection(&entries, &random_);
65 return entries;
66 }
67
68 // This test is in a function so that it can be applied to both the static
69 // table and the combined static+dynamic tables.
70 AssertionResult VerifyStaticTableContents() {
71 for (const auto& expected : shuffled_static_entries()) {
72 const HpackStringPair* found = Lookup(expected.index);
73 VERIFY_NE(found, nullptr);
74 VERIFY_EQ(expected.name, found->name) << expected.index;
75 VERIFY_EQ(expected.value, found->value) << expected.index;
76 }
77
78 // There should be no entry with index 0.
79 VERIFY_EQ(nullptr, Lookup(0));
80 return AssertionSuccess();
81 }
82
83 virtual const HpackStringPair* Lookup(size_t index) {
84 return static_table_.Lookup(index);
85 }
86
87 RandomBase* RandomPtr() { return &random_; }
88
89 private:
90 Http2Random random_;
91 HpackDecoderStaticTable static_table_;
92 };
93
94 TEST_F(HpackDecoderStaticTableTest, StaticTableContents) {
95 EXPECT_TRUE(VerifyStaticTableContents());
96 }
97
98 size_t Size(const string& name, const string& value) {
99 return name.size() + value.size() + 32;
100 }
101
102 // To support tests with more than a few of hand crafted changes to the dynamic
103 // table, we have another, exceedingly simple, implementation of the HPACK
104 // dynamic table containing FakeHpackEntry instances. We can thus compare the
105 // contents of the actual table with those in fake_dynamic_table_.
106
107 typedef std::tuple<string, string, size_t> FakeHpackEntry;
108 const string& Name(const FakeHpackEntry& entry) {
109 return std::get<0>(entry);
110 }
111 const string& Value(const FakeHpackEntry& entry) {
112 return std::get<1>(entry);
113 }
114 size_t Size(const FakeHpackEntry& entry) {
115 return std::get<2>(entry);
116 }
117
118 class HpackDecoderTablesTest : public HpackDecoderStaticTableTest {
119 protected:
120 const HpackStringPair* Lookup(size_t index) override {
121 return tables_.Lookup(index);
122 }
123
124 size_t dynamic_size_limit() const {
125 return tables_.header_table_size_limit();
126 }
127 size_t current_dynamic_size() const {
128 return tables_.current_header_table_size();
129 }
130 size_t num_dynamic_entries() const {
131 return HpackDecoderTablesPeer::num_dynamic_entries(tables_);
132 }
133
134 // Insert the name and value into fake_dynamic_table_.
135 void FakeInsert(const string& name, const string& value) {
136 FakeHpackEntry entry(name, value, Size(name, value));
137 fake_dynamic_table_.insert(fake_dynamic_table_.begin(), entry);
138 }
139
140 // Add up the size of all entries in fake_dynamic_table_.
141 size_t FakeSize() {
142 size_t sz = 0;
143 for (const auto& entry : fake_dynamic_table_) {
144 sz += Size(entry);
145 }
146 return sz;
147 }
148
149 // If the total size of the fake_dynamic_table_ is greater than limit,
150 // keep the first N entries such that those N entries have a size not
151 // greater than limit, and such that keeping entry N+1 would have a size
152 // greater than limit. Returns the count of removed bytes.
153 size_t FakeTrim(size_t limit) {
154 size_t original_size = FakeSize();
155 size_t total_size = 0;
156 for (size_t ndx = 0; ndx < fake_dynamic_table_.size(); ++ndx) {
157 total_size += Size(fake_dynamic_table_[ndx]);
158 if (total_size > limit) {
159 // Need to get rid of ndx and all following entries.
160 fake_dynamic_table_.erase(fake_dynamic_table_.begin() + ndx,
161 fake_dynamic_table_.end());
162 return original_size - FakeSize();
163 }
164 }
165 return 0;
166 }
167
168 // Verify that the contents of the actual dynamic table match those in
169 // fake_dynamic_table_.
170 AssertionResult VerifyDynamicTableContents() {
171 VERIFY_EQ(current_dynamic_size(), FakeSize());
172 VERIFY_EQ(num_dynamic_entries(), fake_dynamic_table_.size());
173
174 for (size_t ndx = 0; ndx < fake_dynamic_table_.size(); ++ndx) {
175 const HpackStringPair* found = Lookup(ndx + kFirstDynamicTableIndex);
176 VERIFY_NE(found, nullptr);
177
178 const auto& expected = fake_dynamic_table_[ndx];
179 VERIFY_EQ(Name(expected), found->name);
180 VERIFY_EQ(Value(expected), found->value);
181 }
182
183 // Make sure there are no more entries.
184 VERIFY_EQ(nullptr,
185 Lookup(fake_dynamic_table_.size() + kFirstDynamicTableIndex));
186 return AssertionSuccess();
187 }
188
189 // Apply an update to the limit on the maximum size of the dynamic table.
190 AssertionResult DynamicTableSizeUpdate(size_t size_limit) {
191 VERIFY_EQ(current_dynamic_size(), FakeSize());
192 if (size_limit < current_dynamic_size()) {
193 // Will need to trim the dynamic table's oldest entries.
194 tables_.DynamicTableSizeUpdate(size_limit);
195 FakeTrim(size_limit);
196 return VerifyDynamicTableContents();
197 }
198 // Shouldn't change the size.
199 tables_.DynamicTableSizeUpdate(size_limit);
200 return VerifyDynamicTableContents();
201 }
202
203 // Insert an entry into the dynamic table, confirming that trimming of entries
204 // occurs if the total size is greater than the limit, and that older entries
205 // move up by 1 index.
206 AssertionResult Insert(const string& name, const string& value) {
207 size_t old_count = num_dynamic_entries();
208 if (tables_.Insert(HpackString(name), HpackString(value))) {
209 VERIFY_GT(current_dynamic_size(), 0u);
210 VERIFY_GT(num_dynamic_entries(), 0u);
211 } else {
212 VERIFY_EQ(current_dynamic_size(), 0u);
213 VERIFY_EQ(num_dynamic_entries(), 0u);
214 }
215 FakeInsert(name, value);
216 VERIFY_EQ(old_count + 1, fake_dynamic_table_.size());
217 FakeTrim(dynamic_size_limit());
218 VERIFY_EQ(current_dynamic_size(), FakeSize());
219 VERIFY_EQ(num_dynamic_entries(), fake_dynamic_table_.size());
220 return VerifyDynamicTableContents();
221 }
222
223 private:
224 HpackDecoderTables tables_;
225
226 std::vector<FakeHpackEntry> fake_dynamic_table_;
227 };
228
229 TEST_F(HpackDecoderTablesTest, StaticTableContents) {
230 EXPECT_TRUE(VerifyStaticTableContents());
231 }
232
233 // Generate a bunch of random header entries, insert them, and confirm they
234 // present, as required by the RFC, using VerifyDynamicTableContents above on
235 // each Insert. Also apply various resizings of the dynamic table.
236 TEST_F(HpackDecoderTablesTest, RandomDynamicTable) {
237 EXPECT_EQ(0u, current_dynamic_size());
238 EXPECT_TRUE(VerifyStaticTableContents());
239 EXPECT_TRUE(VerifyDynamicTableContents());
240
241 std::vector<size_t> table_sizes;
242 table_sizes.push_back(dynamic_size_limit());
243 table_sizes.push_back(0);
244 table_sizes.push_back(dynamic_size_limit() / 2);
245 table_sizes.push_back(dynamic_size_limit());
246 table_sizes.push_back(dynamic_size_limit() / 2);
247 table_sizes.push_back(0);
248 table_sizes.push_back(dynamic_size_limit());
249
250 for (size_t limit : table_sizes) {
251 ASSERT_TRUE(DynamicTableSizeUpdate(limit));
252 for (int insert_count = 0; insert_count < 100; ++insert_count) {
253 string name = GenerateHttp2HeaderName(
254 GenerateUniformInRange(2, 40, RandomPtr()), RandomPtr());
255 string value = GenerateWebSafeString(
256 GenerateUniformInRange(2, 600, RandomPtr()), RandomPtr());
257 ASSERT_TRUE(Insert(name, value));
258 }
259 EXPECT_TRUE(VerifyStaticTableContents());
260 }
261 }
262
263 } // namespace
264 } // namespace test
265 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698