proxygen
HeaderTableTests.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree. An additional grant
7  * of patent rights can be found in the PATENTS file in the same directory.
8  *
9  */
11 #include <memory>
15 #include <sstream>
16 
17 using namespace std;
18 using namespace testing;
19 
20 namespace proxygen {
21 
23  protected:
24  void xcheck(uint32_t internal, uint32_t external) {
25  EXPECT_EQ(HeaderTable::toExternal(head_, length_, internal), external);
26  EXPECT_EQ(HeaderTable::toInternal(head_, length_, external), internal);
27  }
28 
29  void resizeTable(HeaderTable& table, uint32_t newCapacity, uint32_t newMax) {
30  table.setCapacity(newCapacity);
31  // On resizing the table size (count of headers) remains the same or sizes
32  // down; can not size up
33  EXPECT_LE(table.size(), newMax);
34  }
35 
37  HeaderTable& table, HPACKHeader& header, uint32_t newMax,
38  uint32_t fillCount) {
39  uint32_t newCapacity = header.bytes() * newMax;
40  resizeTable(table, newCapacity, newMax);
41  // Fill the table (with one extra) and make sure we haven't violated our
42  // size (bytes) limits (expected one entry to be evicted)
43  for (size_t i = 0; i <= fillCount; ++i) {
44  EXPECT_EQ(table.add(header.copy()), true);
45  }
46  EXPECT_EQ(table.size(), newMax);
47  EXPECT_EQ(table.bytes(), newCapacity);
48  }
49 
50  uint32_t head_{0};
52 };
53 
54 TEST_F(HeaderTableTests, IndexTranslation) {
55  // simple cases
56  length_ = 10;
57  head_ = 5;
58  xcheck(0, 6);
59  xcheck(3, 3);
60  xcheck(5, 1);
61 
62  // wrap
63  head_ = 1;
64  xcheck(0, 2);
65  xcheck(8, 4);
66  xcheck(5, 7);
67 }
68 
70  HeaderTable table(4096);
71  HPACKHeader header("accept-encoding", "gzip");
72  table.add(header.copy());
73  table.add(header.copy());
74  table.add(header.copy());
75  EXPECT_EQ(table.names().size(), 1);
76  EXPECT_EQ(table.hasName(header.name), true);
77  auto it = table.names().find(header.name);
78  EXPECT_EQ(it->second.size(), 3);
79  EXPECT_EQ(table.nameIndex(header.name), 1);
80 }
81 
83  HPACKHeaderName name("accept-encoding");
84  HPACKHeader accept("accept-encoding", "gzip");
85  HPACKHeader accept2("accept-encoding", "----"); // same size, different header
86  HPACKHeader accept3("accept-encoding", "third"); // size is larger with 1 byte
87  uint32_t max = 10;
88  uint32_t capacity = accept.bytes() * max;
89  HeaderTable table(capacity);
90  // fill the table
91  for (size_t i = 0; i < max; i++) {
92  EXPECT_EQ(table.add(accept.copy()), true);
93  }
94  EXPECT_EQ(table.size(), max);
95  EXPECT_EQ(table.add(accept2.copy()), true);
96  // evict the first one
97  EXPECT_EQ(table.getHeader(1), accept2);
98  auto ilist = table.names().find(name)->second;
99  EXPECT_EQ(ilist.size(), max);
100  // evict all the 'accept' headers
101  for (size_t i = 0; i < max - 1; i++) {
102  EXPECT_EQ(table.add(accept2.copy()), true);
103  }
104  EXPECT_EQ(table.size(), max);
105  EXPECT_EQ(table.getHeader(max), accept2);
106  EXPECT_EQ(table.names().size(), 1);
107  // add an entry that will cause 2 evictions
108  EXPECT_EQ(table.add(accept3.copy()), true);
109  EXPECT_EQ(table.getHeader(1), accept3);
110  EXPECT_EQ(table.size(), max - 1);
111 
112  // add a super huge header
113  string bigvalue;
114  bigvalue.append(capacity, 'x');
115  HPACKHeader bigheader("user-agent", bigvalue);
116  EXPECT_EQ(table.add(bigheader.copy()), false);
117  EXPECT_EQ(table.size(), 0);
118  EXPECT_EQ(table.names().size(), 0);
119 }
120 
121 TEST_F(HeaderTableTests, ReduceCapacity) {
122  HPACKHeader accept("accept-encoding", "gzip");
123  uint32_t max = 10;
124  uint32_t capacity = accept.bytes() * max;
125  HeaderTable table(capacity);
126  EXPECT_LE(table.length(), table.getMaxTableLength(capacity));
127 
128  // fill the table
129  for (size_t i = 0; i < max; i++) {
130  EXPECT_EQ(table.add(accept.copy()), true);
131  }
132  // change capacity
133  table.setCapacity(capacity / 2);
134  EXPECT_EQ(table.size(), max / 2);
135  EXPECT_EQ(table.bytes(), capacity / 2);
136 }
137 
138 TEST_F(HeaderTableTests, Comparison) {
139  uint32_t capacity = 128;
140  HeaderTable t1(capacity);
141  HeaderTable t2(capacity);
142 
143  HPACKHeader h1("Content-Encoding", "gzip");
144  HPACKHeader h2("Content-Encoding", "deflate");
145  // different in number of elements
146  t1.add(h1.copy());
147  EXPECT_FALSE(t1 == t2);
148  // different in size (bytes)
149  t2.add(h2.copy());
150  EXPECT_FALSE(t1 == t2);
151 
152  // make them the same
153  t1.add(h2.copy());
154  t2.add(h1.copy());
155  EXPECT_TRUE(t1 == t2);
156 }
157 
159  stringstream out;
160  HeaderTable t(128);
161  t.add(HPACKHeader("Accept-Encoding", "gzip"));
162  out << t;
163  EXPECT_EQ(out.str(),
164  "\n[1] (s=51) accept-encoding: gzip\ntotal size: 51\n");
165 }
166 
167 TEST_F(HeaderTableTests, IncreaseCapacity) {
168  HPACKHeader accept("accept-encoding", "gzip");
169  uint32_t max = 4;
170  uint32_t capacity = accept.bytes() * max;
171  HeaderTable table(capacity);
172  EXPECT_LE(table.length(), table.getMaxTableLength(capacity));
173 
174  // fill the table
175  uint32_t length = table.length() + 1;
176  for (size_t i = 0; i < length; i++) {
177  EXPECT_EQ(table.add(accept.copy()), true);
178  }
179  EXPECT_EQ(table.size(), max);
180  EXPECT_EQ(table.getIndex(accept), 1);
181  // head should be 0, tail should be 2
182  max = 8;
183  capacity = accept.bytes() * max;
184  table.setCapacity(capacity);
185 
186  EXPECT_LE(table.length(), table.getMaxTableLength(capacity));
187  // external index didn't change
188  EXPECT_EQ(table.getIndex(accept), 1);
189 
190 }
191 
192 TEST_F(HeaderTableTests, VaryCapacity) {
193  HPACKHeader accept("accept-encoding", "gzip");
194  uint32_t max = 6;
195  uint32_t capacity = accept.bytes() * max;
196  HeaderTable table(capacity);
197 
198  // Fill the table (extra) and make sure we haven't violated our
199  // size (bytes) limits (expected one entry to be evicted)
200  for (size_t i = 0; i <= table.length(); ++i) {
201  EXPECT_EQ(table.add(accept.copy()), true);
202  }
203  EXPECT_EQ(table.size(), max);
204 
205  // Size down the table and verify we are still honoring our size (bytes)
206  // limits
207  resizeAndFillTable(table, accept, 4, 5);
208 
209  // Size up the table (in between previous max and min within test) and verify
210  // we are still horing our size (bytes) limits
211  resizeAndFillTable(table, accept, 5, 6);
212 
213  // Finally reize up one last timestamps
214  resizeAndFillTable(table, accept, 8, 9);
215 }
216 
217 TEST_F(HeaderTableTests, VaryCapacityMalignHeadIndex) {
218  // Test checks for a previous bug/crash condition where due to resizing
219  // the underlying table to a size lower than a previous max but up from the
220  // current size and the position of the head_ index an out of bounds index
221  // would occur
222 
223  // Initialize header table
224  HPACKHeader accept("accept-encoding", "gzip");
225  uint32_t max = 6;
226  uint32_t capacity = accept.bytes() * max;
227  HeaderTable table(capacity);
228 
229  // Push head_ to last index in underlying table before potential wrap
230  // This is our max table size for the duration of the test
231  for (size_t i = 0; i < table.getMaxTableLength(capacity); ++i) {
232  EXPECT_EQ(table.add(accept.copy()), true);
233  }
234  EXPECT_EQ(table.size(), max);
235  EXPECT_EQ(table.bytes(), capacity);
236 
237  // Flush underlying table (head_ remains the same at the previous max index)
238  // Header guranteed to cause a flush as header itself requires 32 bytes plus
239  // the sizes of the name and value anyways (which themselves would cause a
240  // flush)
241  string strLargerThanTableCapacity = string(capacity + 1, 'a');
242  HPACKHeader flush("flush", strLargerThanTableCapacity);
243  EXPECT_EQ(table.add(flush.copy()), false);
244  EXPECT_EQ(table.size(), 0);
245 
246  // Now reduce capacity of table (in functional terms table.size() is lowered
247  // but currently table.length() remains the same)
248  max = 3;
249  resizeTable(table, accept.bytes() * max, max);
250 
251  // Increase capacity of table (but smaller than all time max; head_ still at
252  // previous max index). Previously (now fixed) this size up resulted in
253  // incorrect resizing semantics
254  max = 4;
255  resizeTable(table, accept.bytes() * max, max);
256 
257  // Now try and add headers; there should be no crash with current position of
258  // head_ in the underlying table. Note this is merely one possible way we
259  // could force the test to crash as a result of the resize bug this test was
260  // added for
261  for (size_t i = 0; i <= table.length(); ++i) {
262  EXPECT_EQ(table.add(accept.copy()), true);
263  }
264  EXPECT_EQ(table.size(), max);
265 }
266 
267 TEST_F(HeaderTableTests, AddLargerThanTable) {
268  // Construct a smallish table
269  uint32_t capacityBytes = 256;
270  HeaderTable table(capacityBytes);
271  HPACKHeaderName name("accept-encoding");
272  table.add(HPACKHeader("accept-encoding", "gzip")); // internal index = 0
273  table.add(HPACKHeader("accept-encoding", "gzip")); // internal index = 1
274  table.add(HPACKHeader("test-encoding", "gzip")); // internal index = 2
275  EXPECT_EQ(table.names().size(), 2);
276 
277  // Attempt to add a header that is larger than our specified table capacity
278  // bytes. This should result in a table flush.
279  table.add(HPACKHeader(std::string(capacityBytes, 'a'), "gzip"));
280  EXPECT_EQ(table.names().size(), 0);
281 
282  // Add the previous headers to the table again
283  table.add(HPACKHeader("accept-encoding", "gzip")); // internal index = 3
284  table.add(HPACKHeader("accept-encoding", "gzip")); // internal index = 4
285  table.add(HPACKHeader("test-encoding", "gzip")); // internal index = 5
286  EXPECT_EQ(table.names().size(), 2);
287 
288  EXPECT_EQ(table.hasName(name), true);
289  auto it = table.names().find(name);
290  EXPECT_EQ(it->second.size(), 2);
291  // As nameIndex takes the last index added, we have head = 5, index = 4
292  // and so yields a difference of one and as external indexing is 1 based,
293  // we expect 2 here
294  EXPECT_EQ(table.nameIndex(name), 2);
295 }
296 
297 TEST_F(HeaderTableTests, IncreaseLengthOfFullTable) {
298  HPACKHeader largeHeader("Access-Control-Allow-Credentials", "true");
299  HPACKHeader smallHeader("Accept", "All-Content");
300 
301  HeaderTable table(448);
302  CHECK_EQ(table.length(), 7);
303 
304  for (uint8_t count = 0; count < 3; count++) {
305  table.add(largeHeader.copy());
306  table.add(smallHeader.copy());
307  } // tail is at index 0
308  CHECK_EQ(table.length(), 7);
309 
310  table.add(smallHeader.copy());
311  table.add(smallHeader.copy()); // tail is at index 1
312  table.add(smallHeader.copy()); // resize on this add
313  EXPECT_EQ(table.length(), 11);
314 
315  // Check table is correct after resize
316  CHECK_EQ(table.getHeader(1), smallHeader);
317  CHECK_EQ(table.getHeader(2), smallHeader);
318  CHECK_EQ(table.getHeader(3), smallHeader);
319  CHECK_EQ(table.getHeader(4), smallHeader);
320  CHECK_EQ(table.getHeader(5), largeHeader);
321  CHECK_EQ(table.getHeader(6), smallHeader);
322  CHECK_EQ(table.getHeader(7), largeHeader);
323  CHECK_EQ(table.getHeader(8), smallHeader);
324 }
325 
326 TEST_F(HeaderTableTests, SmallTable) {
327  HeaderTable table(80);
328  HPACKHeader foo("Foo", "bar");
329  EXPECT_TRUE(table.add(foo.copy()));
330  EXPECT_TRUE(table.add(foo.copy()));
331  EXPECT_EQ(table.size(), 2);
332  EXPECT_EQ(table.length(), 2);
333 }
334 
335 }
#define EXPECT_LE(val1, val2)
Definition: gtest.h:1928
int64_t length_
Definition: JSONSchema.cpp:233
void resizeAndFillTable(HeaderTable &table, HPACKHeader &header, uint32_t newMax, uint32_t fillCount)
LogLevel max
Definition: LogLevel.cpp:31
bool hasName(const HPACKHeaderName &headerName)
Definition: HeaderTable.cpp:84
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
uint32_t getMaxTableLength(uint32_t capacityVal)
Definition: HeaderTable.cpp:98
TEST_F(HeaderTableTests, SmallTable)
STL namespace.
const HPACKHeader & getHeader(uint32_t index) const
Definition: HeaderTable.cpp:93
uint32_t bytes() const
Definition: HeaderTable.h:112
const char * name
Definition: http_parser.c:437
uint32_t bytes() const
Definition: HPACKHeader.h:49
size_t length() const
Definition: HeaderTable.h:119
HPACKHeaderName name
Definition: HPACKHeader.h:82
void resizeTable(HeaderTable &table, uint32_t newCapacity, uint32_t newMax)
uint32_t nameIndex(const HPACKHeaderName &headerName) const
Definition: HeaderTable.cpp:88
const names_map & names() const
Definition: HeaderTable.h:72
int * count
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
const char * string
Definition: Conv.cpp:212
HPACKHeader copy() const
Definition: HPACKHeader.h:71
virtual bool setCapacity(uint32_t capacity)
uint32_t size() const
Definition: HeaderTable.h:105
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
uint32_t getIndex(const HPACKHeader &header) const
Definition: HeaderTable.cpp:63
void xcheck(uint32_t internal, uint32_t external)
void Add(size_t iterations, ValueT kMin, ValueT kMax)
virtual bool add(HPACKHeader header)
Definition: HeaderTable.cpp:33
NetworkSocket accept(NetworkSocket s, sockaddr *addr, socklen_t *addrlen)
Definition: NetOps.cpp:71