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

Side by Side Diff: net/spdy/hpack_encoder_test.cc

Issue 992733002: Remove //net (except for Android test stuff) and sdch (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 9 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
« no previous file with comments | « net/spdy/hpack_encoder.cc ('k') | net/spdy/hpack_entry.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 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/spdy/hpack_encoder.h"
6
7 #include <map>
8 #include <string>
9
10 #include "testing/gmock/include/gmock/gmock.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 namespace net {
14
15 using base::StringPiece;
16 using std::string;
17 using testing::ElementsAre;
18
19 namespace test {
20
21 class HpackHeaderTablePeer {
22 public:
23 explicit HpackHeaderTablePeer(HpackHeaderTable* table)
24 : table_(table) {}
25
26 HpackHeaderTable::EntryTable* dynamic_entries() {
27 return &table_->dynamic_entries_;
28 }
29
30 private:
31 HpackHeaderTable* table_;
32 };
33
34 class HpackEncoderPeer {
35 public:
36 typedef HpackEncoder::Representation Representation;
37 typedef HpackEncoder::Representations Representations;
38
39 explicit HpackEncoderPeer(HpackEncoder* encoder)
40 : encoder_(encoder) {}
41
42 HpackHeaderTable* table() {
43 return &encoder_->header_table_;
44 }
45 HpackHeaderTablePeer table_peer() {
46 return HpackHeaderTablePeer(table());
47 }
48 bool allow_huffman_compression() {
49 return encoder_->allow_huffman_compression_;
50 }
51 void set_allow_huffman_compression(bool allow) {
52 encoder_->allow_huffman_compression_ = allow;
53 }
54 void EmitString(StringPiece str) {
55 encoder_->EmitString(str);
56 }
57 void TakeString(string* out) {
58 encoder_->output_stream_.TakeString(out);
59 }
60 void UpdateCharacterCounts(StringPiece str) {
61 encoder_->UpdateCharacterCounts(str);
62 }
63 static void CookieToCrumbs(StringPiece cookie,
64 std::vector<StringPiece>* out) {
65 Representations tmp;
66 HpackEncoder::CookieToCrumbs(make_pair("", cookie), &tmp);
67
68 out->clear();
69 for (size_t i = 0; i != tmp.size(); ++i) {
70 out->push_back(tmp[i].second);
71 }
72 }
73 static void DecomposeRepresentation(StringPiece value,
74 std::vector<StringPiece>* out) {
75 Representations tmp;
76 HpackEncoder::DecomposeRepresentation(make_pair("foobar", value), &tmp);
77
78 out->clear();
79 for (size_t i = 0; i != tmp.size(); ++i) {
80 out->push_back(tmp[i].second);
81 }
82 }
83
84 private:
85 HpackEncoder* encoder_;
86 };
87
88 } // namespace test
89
90 namespace {
91
92 using std::map;
93 using testing::ElementsAre;
94
95 class HpackEncoderTest : public ::testing::Test {
96 protected:
97 typedef test::HpackEncoderPeer::Representations Representations;
98
99 HpackEncoderTest()
100 : encoder_(ObtainHpackHuffmanTable()),
101 peer_(&encoder_),
102 static_(peer_.table()->GetByIndex(1)) {}
103
104 void SetUp() override {
105 // Populate dynamic entries into the table fixture. For simplicity each
106 // entry has name.size() + value.size() == 10.
107 key_1_ = peer_.table()->TryAddEntry("key1", "value1");
108 key_2_ = peer_.table()->TryAddEntry("key2", "value2");
109 cookie_a_ = peer_.table()->TryAddEntry("cookie", "a=bb");
110 cookie_c_ = peer_.table()->TryAddEntry("cookie", "c=dd");
111
112 // No further insertions may occur without evictions.
113 peer_.table()->SetMaxSize(peer_.table()->size());
114
115 // Disable Huffman coding by default. Most tests don't care about it.
116 peer_.set_allow_huffman_compression(false);
117 }
118
119 void ExpectIndex(size_t index) {
120 expected_.AppendPrefix(kIndexedOpcode);
121 expected_.AppendUint32(index);
122 }
123 void ExpectIndexedLiteral(const HpackEntry* key_entry, StringPiece value) {
124 expected_.AppendPrefix(kLiteralIncrementalIndexOpcode);
125 expected_.AppendUint32(IndexOf(key_entry));
126 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
127 expected_.AppendUint32(value.size());
128 expected_.AppendBytes(value);
129 }
130 void ExpectIndexedLiteral(StringPiece name, StringPiece value) {
131 expected_.AppendPrefix(kLiteralIncrementalIndexOpcode);
132 expected_.AppendUint32(0);
133 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
134 expected_.AppendUint32(name.size());
135 expected_.AppendBytes(name);
136 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
137 expected_.AppendUint32(value.size());
138 expected_.AppendBytes(value);
139 }
140 void ExpectNonIndexedLiteral(StringPiece name, StringPiece value) {
141 expected_.AppendPrefix(kLiteralNoIndexOpcode);
142 expected_.AppendUint32(0);
143 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
144 expected_.AppendUint32(name.size());
145 expected_.AppendBytes(name);
146 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
147 expected_.AppendUint32(value.size());
148 expected_.AppendBytes(value);
149 }
150 void CompareWithExpectedEncoding(const map<string, string>& header_set) {
151 string expected_out, actual_out;
152 expected_.TakeString(&expected_out);
153 EXPECT_TRUE(encoder_.EncodeHeaderSet(header_set, &actual_out));
154 EXPECT_EQ(expected_out, actual_out);
155 }
156 size_t IndexOf(HpackEntry* entry) {
157 return peer_.table()->IndexOf(entry);
158 }
159 size_t IndexOf(const HpackEntry* entry) {
160 return peer_.table()->IndexOf(entry);
161 }
162
163 HpackEncoder encoder_;
164 test::HpackEncoderPeer peer_;
165
166 const HpackEntry* static_;
167 const HpackEntry* key_1_;
168 const HpackEntry* key_2_;
169 const HpackEntry* cookie_a_;
170 const HpackEntry* cookie_c_;
171
172 HpackOutputStream expected_;
173 };
174
175 TEST_F(HpackEncoderTest, SingleDynamicIndex) {
176 ExpectIndex(IndexOf(key_2_));
177
178 map<string, string> headers;
179 headers[key_2_->name()] = key_2_->value();
180 CompareWithExpectedEncoding(headers);
181 }
182
183 TEST_F(HpackEncoderTest, SingleStaticIndex) {
184 ExpectIndex(IndexOf(static_));
185
186 map<string, string> headers;
187 headers[static_->name()] = static_->value();
188 CompareWithExpectedEncoding(headers);
189 }
190
191 TEST_F(HpackEncoderTest, SingleStaticIndexTooLarge) {
192 peer_.table()->SetMaxSize(1); // Also evicts all fixtures.
193 ExpectIndex(IndexOf(static_));
194
195 map<string, string> headers;
196 headers[static_->name()] = static_->value();
197 CompareWithExpectedEncoding(headers);
198
199 EXPECT_EQ(0u, peer_.table_peer().dynamic_entries()->size());
200 }
201
202 TEST_F(HpackEncoderTest, SingleLiteralWithIndexName) {
203 ExpectIndexedLiteral(key_2_, "value3");
204
205 map<string, string> headers;
206 headers[key_2_->name()] = "value3";
207 CompareWithExpectedEncoding(headers);
208
209 // A new entry was inserted and added to the reference set.
210 HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front();
211 EXPECT_EQ(new_entry->name(), key_2_->name());
212 EXPECT_EQ(new_entry->value(), "value3");
213 }
214
215 TEST_F(HpackEncoderTest, SingleLiteralWithLiteralName) {
216 ExpectIndexedLiteral("key3", "value3");
217
218 map<string, string> headers;
219 headers["key3"] = "value3";
220 CompareWithExpectedEncoding(headers);
221
222 HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front();
223 EXPECT_EQ(new_entry->name(), "key3");
224 EXPECT_EQ(new_entry->value(), "value3");
225 }
226
227 TEST_F(HpackEncoderTest, SingleLiteralTooLarge) {
228 peer_.table()->SetMaxSize(1); // Also evicts all fixtures.
229
230 ExpectIndexedLiteral("key3", "value3");
231
232 // A header overflowing the header table is still emitted.
233 // The header table is empty.
234 map<string, string> headers;
235 headers["key3"] = "value3";
236 CompareWithExpectedEncoding(headers);
237
238 EXPECT_EQ(0u, peer_.table_peer().dynamic_entries()->size());
239 }
240
241 TEST_F(HpackEncoderTest, EmitThanEvict) {
242 // |key_1_| is toggled and placed into the reference set,
243 // and then immediately evicted by "key3".
244 ExpectIndex(IndexOf(key_1_));
245 ExpectIndexedLiteral("key3", "value3");
246
247 map<string, string> headers;
248 headers[key_1_->name()] = key_1_->value();
249 headers["key3"] = "value3";
250 CompareWithExpectedEncoding(headers);
251 }
252
253 TEST_F(HpackEncoderTest, CookieHeaderIsCrumbled) {
254 ExpectIndex(IndexOf(cookie_a_));
255 ExpectIndex(IndexOf(cookie_c_));
256 ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "e=ff");
257
258 map<string, string> headers;
259 headers["cookie"] = "e=ff; a=bb; c=dd";
260 CompareWithExpectedEncoding(headers);
261 }
262
263 TEST_F(HpackEncoderTest, StringsDynamicallySelectHuffmanCoding) {
264 peer_.set_allow_huffman_compression(true);
265
266 // Compactable string. Uses Huffman coding.
267 peer_.EmitString("feedbeef");
268 expected_.AppendPrefix(kStringLiteralHuffmanEncoded);
269 expected_.AppendUint32(6);
270 expected_.AppendBytes("\x94\xA5\x92""2\x96_");
271
272 // Non-compactable. Uses identity coding.
273 peer_.EmitString("@@@@@@");
274 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
275 expected_.AppendUint32(6);
276 expected_.AppendBytes("@@@@@@");
277
278 string expected_out, actual_out;
279 expected_.TakeString(&expected_out);
280 peer_.TakeString(&actual_out);
281 EXPECT_EQ(expected_out, actual_out);
282 }
283
284 TEST_F(HpackEncoderTest, EncodingWithoutCompression) {
285 // Implementation should internally disable.
286 peer_.set_allow_huffman_compression(true);
287
288 ExpectNonIndexedLiteral(":path", "/index.html");
289 ExpectNonIndexedLiteral("cookie", "foo=bar; baz=bing");
290 ExpectNonIndexedLiteral("hello", "goodbye");
291
292 map<string, string> headers;
293 headers[":path"] = "/index.html";
294 headers["cookie"] = "foo=bar; baz=bing";
295 headers["hello"] = "goodbye";
296
297 string expected_out, actual_out;
298 expected_.TakeString(&expected_out);
299 encoder_.EncodeHeaderSetWithoutCompression(headers, &actual_out);
300 EXPECT_EQ(expected_out, actual_out);
301 }
302
303 TEST_F(HpackEncoderTest, MultipleEncodingPasses) {
304 // Pass 1.
305 {
306 map<string, string> headers;
307 headers["key1"] = "value1";
308 headers["cookie"] = "a=bb";
309
310 ExpectIndex(IndexOf(cookie_a_));
311 ExpectIndex(IndexOf(key_1_));
312 CompareWithExpectedEncoding(headers);
313 }
314 // Header table is:
315 // 65: key1: value1
316 // 64: key2: value2
317 // 63: cookie: a=bb
318 // 62: cookie: c=dd
319 // Pass 2.
320 {
321 map<string, string> headers;
322 headers["key2"] = "value2";
323 headers["cookie"] = "c=dd; e=ff";
324
325 ExpectIndex(IndexOf(cookie_c_));
326 // This cookie evicts |key1| from the dynamic table.
327 ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "e=ff");
328 // "key2: value2"
329 ExpectIndex(65);
330
331 CompareWithExpectedEncoding(headers);
332 }
333 // Header table is:
334 // 65: key2: value2
335 // 64: cookie: a=bb
336 // 63: cookie: c=dd
337 // 62: cookie: e=ff
338 // Pass 3.
339 {
340 map<string, string> headers;
341 headers["key2"] = "value2";
342 headers["cookie"] = "a=bb; b=cc; c=dd";
343
344 // "cookie: a=bb"
345 ExpectIndex(64);
346 // This cookie evicts |key2| from the dynamic table.
347 ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "b=cc");
348 // "cookie: c=dd"
349 ExpectIndex(64);
350 // "key2: value2"
351 ExpectIndexedLiteral("key2", "value2");
352
353 CompareWithExpectedEncoding(headers);
354 }
355 }
356
357 TEST_F(HpackEncoderTest, PseudoHeadersFirst) {
358 map<string, string> headers;
359 // A pseudo-header to be indexed.
360 headers[":authority"] = "www.example.com";
361 // A pseudo-header that should not be indexed.
362 headers[":path"] = "/spam/eggs.html";
363 // A regular header which precedes ":" alphabetically, should still be encoded
364 // after pseudo-headers.
365 headers["-foo"] = "bar";
366 headers["foo"] = "bar";
367 headers["cookie"] = "c=dd";
368
369 // Pseudo-headers are encoded in alphabetical order.
370 // This entry pushes "cookie: a=bb" back to 63.
371 ExpectIndexedLiteral(peer_.table()->GetByName(":authority"),
372 "www.example.com");
373 ExpectNonIndexedLiteral(":path", "/spam/eggs.html");
374 // Regular headers are endoded in alphabetical order.
375 // This entry pushes "cookie: a=bb" back to 64.
376 ExpectIndexedLiteral("-foo", "bar");
377 ExpectIndex(64);
378 ExpectIndexedLiteral("foo", "bar");
379 CompareWithExpectedEncoding(headers);
380 }
381
382 TEST_F(HpackEncoderTest, CookieToCrumbs) {
383 test::HpackEncoderPeer peer(NULL);
384 std::vector<StringPiece> out;
385
386 // A space after ';' is consumed. All other spaces remain. ';' at beginning
387 // and end of string produce empty crumbs. Duplicate crumbs are removed.
388 // See section 8.1.3.4 "Compressing the Cookie Header Field" in the HTTP/2
389 // specification at http://tools.ietf.org/html/draft-ietf-httpbis-http2-11
390 peer.CookieToCrumbs(" foo=1;bar=2 ; bar=3; bing=4; ", &out);
391 EXPECT_THAT(out, ElementsAre("", " bing=4", " foo=1", "bar=2 ", "bar=3"));
392
393 peer.CookieToCrumbs(";;foo = bar ;; ;baz =bing", &out);
394 EXPECT_THAT(out, ElementsAre("", "baz =bing", "foo = bar "));
395
396 peer.CookieToCrumbs("baz=bing; foo=bar; baz=bing", &out);
397 EXPECT_THAT(out, ElementsAre("baz=bing", "foo=bar"));
398
399 peer.CookieToCrumbs("baz=bing", &out);
400 EXPECT_THAT(out, ElementsAre("baz=bing"));
401
402 peer.CookieToCrumbs("", &out);
403 EXPECT_THAT(out, ElementsAre(""));
404
405 peer.CookieToCrumbs("foo;bar; baz;baz;bing;", &out);
406 EXPECT_THAT(out, ElementsAre("", "bar", "baz", "bing", "foo"));
407 }
408
409 TEST_F(HpackEncoderTest, UpdateCharacterCounts) {
410 std::vector<size_t> counts(256, 0);
411 size_t total_counts = 0;
412 encoder_.SetCharCountsStorage(&counts, &total_counts);
413
414 char kTestString[] = "foo\0\1\xff""boo";
415 peer_.UpdateCharacterCounts(
416 StringPiece(kTestString, arraysize(kTestString) - 1));
417
418 std::vector<size_t> expect(256, 0);
419 expect[static_cast<uint8>('f')] = 1;
420 expect[static_cast<uint8>('o')] = 4;
421 expect[static_cast<uint8>('\0')] = 1;
422 expect[static_cast<uint8>('\1')] = 1;
423 expect[static_cast<uint8>('\xff')] = 1;
424 expect[static_cast<uint8>('b')] = 1;
425
426 EXPECT_EQ(expect, counts);
427 EXPECT_EQ(9u, total_counts);
428 }
429
430 TEST_F(HpackEncoderTest, DecomposeRepresentation) {
431 test::HpackEncoderPeer peer(NULL);
432 std::vector<StringPiece> out;
433
434 peer.DecomposeRepresentation("", &out);
435 EXPECT_THAT(out, ElementsAre(""));
436
437 peer.DecomposeRepresentation("foobar", &out);
438 EXPECT_THAT(out, ElementsAre("foobar"));
439
440 peer.DecomposeRepresentation(StringPiece("foo\0bar", 7), &out);
441 EXPECT_THAT(out, ElementsAre("foo", "bar"));
442
443 peer.DecomposeRepresentation(StringPiece("\0foo\0bar", 8), &out);
444 EXPECT_THAT(out, ElementsAre("", "foo", "bar"));
445
446 peer.DecomposeRepresentation(StringPiece("foo\0bar\0", 8), &out);
447 EXPECT_THAT(out, ElementsAre("foo", "bar", ""));
448
449 peer.DecomposeRepresentation(StringPiece("\0foo\0bar\0", 9), &out);
450 EXPECT_THAT(out, ElementsAre("", "foo", "bar", ""));
451 }
452
453 // Test that encoded headers do not have \0-delimited multiple values, as this
454 // became disallowed in HTTP/2 draft-14.
455 TEST_F(HpackEncoderTest, CrumbleNullByteDelimitedValue) {
456 map<string, string> headers;
457 // A header field to be crumbled: "spam: foo\0bar".
458 headers["spam"] = string("foo\0bar", 7);
459
460 ExpectIndexedLiteral("spam", "foo");
461 expected_.AppendPrefix(kLiteralIncrementalIndexOpcode);
462 expected_.AppendUint32(62);
463 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
464 expected_.AppendUint32(3);
465 expected_.AppendBytes("bar");
466 CompareWithExpectedEncoding(headers);
467 }
468
469 } // namespace
470
471 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/hpack_encoder.cc ('k') | net/spdy/hpack_entry.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698