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

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

Issue 246073007: SPDY & HPACK: Land recent internal changes (through 65328503) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase on upstream change: Expanded FRAME_TOO_LARGE/FRAME_SIZE_ERROR comment. Created 6 years, 7 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 | Annotate | Revision Log
« no previous file with comments | « net/spdy/hpack_encoder.cc ('k') | net/spdy/hpack_encoding_context.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/spdy/hpack_encoder.h" 5 #include "net/spdy/hpack_encoder.h"
6 6
7 #include <map> 7 #include <map>
8 #include <string> 8 #include <string>
9 9
10 #include "testing/gmock/include/gmock/gmock.h" 10 #include "testing/gmock/include/gmock/gmock.h"
11 #include "testing/gtest/include/gtest/gtest.h" 11 #include "testing/gtest/include/gtest/gtest.h"
12 12
13 namespace net { 13 namespace net {
14 14
15 using base::StringPiece; 15 using base::StringPiece;
16 using std::string; 16 using std::string;
17 using testing::ElementsAre; 17 using testing::ElementsAre;
18 18
19 namespace test { 19 namespace test {
20 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
21 class HpackEncoderPeer { 34 class HpackEncoderPeer {
22 public: 35 public:
36 typedef HpackEncoder::Representation Representation;
37 typedef HpackEncoder::Representations Representations;
38
23 explicit HpackEncoderPeer(HpackEncoder* encoder) 39 explicit HpackEncoderPeer(HpackEncoder* encoder)
24 : encoder_(encoder) {} 40 : encoder_(encoder) {}
25 41
26 void set_max_string_literal_size(uint32 size) { 42 HpackHeaderTable* table() {
27 encoder_->max_string_literal_size_ = size; 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);
28 } 59 }
29 static void CookieToCrumbs(StringPiece cookie, 60 static void CookieToCrumbs(StringPiece cookie,
30 std::vector<StringPiece>* out) { 61 std::vector<StringPiece>* out) {
31 HpackEncoder::CookieToCrumbs(cookie, out); 62 Representations tmp;
63 HpackEncoder::CookieToCrumbs(make_pair("", cookie), &tmp);
64
65 out->clear();
66 for (size_t i = 0; i != tmp.size(); ++i) {
67 out->push_back(tmp[i].second);
68 }
32 } 69 }
70
33 private: 71 private:
34 HpackEncoder* encoder_; 72 HpackEncoder* encoder_;
35 }; 73 };
36 74
37 } // namespace test 75 } // namespace test
38 76
39 namespace { 77 namespace {
40 78
41 TEST(HpackEncoderTest, CookieToCrumbs) { 79 using std::map;
80 using testing::ElementsAre;
81
82 class HpackEncoderTest : public ::testing::Test {
83 protected:
84 typedef test::HpackEncoderPeer::Representations Representations;
85
86 HpackEncoderTest()
87 : encoder_(ObtainHpackHuffmanTable()),
88 peer_(&encoder_) {}
89
90 virtual void SetUp() {
91 static_ = peer_.table()->GetByIndex(1);
92 // Populate dynamic entries into the table fixture. For simplicity each
93 // entry has name.size() + value.size() == 10.
94 key_1_ = peer_.table()->TryAddEntry("key1", "value1");
95 key_2_ = peer_.table()->TryAddEntry("key2", "value2");
96 cookie_a_ = peer_.table()->TryAddEntry("cookie", "a=bb");
97 cookie_c_ = peer_.table()->TryAddEntry("cookie", "c=dd");
98
99 // No further insertions may occur without evictions.
100 peer_.table()->SetMaxSize(peer_.table()->size());
101
102 // Disable Huffman coding by default. Most tests don't care about it.
103 peer_.set_allow_huffman_compression(false);
104 }
105
106 void ExpectIndex(size_t index) {
107 expected_.AppendPrefix(kIndexedOpcode);
108 expected_.AppendUint32(index);
109 }
110 void ExpectIndexedLiteral(HpackEntry* key_entry, StringPiece value) {
111 expected_.AppendPrefix(kLiteralIncrementalIndexOpcode);
112 expected_.AppendUint32(key_entry->Index());
113 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
114 expected_.AppendUint32(value.size());
115 expected_.AppendBytes(value);
116 }
117 void ExpectIndexedLiteral(StringPiece name, StringPiece value) {
118 expected_.AppendPrefix(kLiteralIncrementalIndexOpcode);
119 expected_.AppendUint32(0);
120 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
121 expected_.AppendUint32(name.size());
122 expected_.AppendBytes(name);
123 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
124 expected_.AppendUint32(value.size());
125 expected_.AppendBytes(value);
126 }
127 void ExpectNonIndexedLiteral(StringPiece name, StringPiece value) {
128 expected_.AppendPrefix(kLiteralNoIndexOpcode);
129 expected_.AppendUint32(0);
130 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
131 expected_.AppendUint32(name.size());
132 expected_.AppendBytes(name);
133 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
134 expected_.AppendUint32(value.size());
135 expected_.AppendBytes(value);
136 }
137 void CompareWithExpectedEncoding(const map<string, string>& header_set) {
138 string expected_out, actual_out;
139 expected_.TakeString(&expected_out);
140 EXPECT_TRUE(encoder_.EncodeHeaderSet(header_set, &actual_out));
141 EXPECT_EQ(expected_out, actual_out);
142 }
143
144 HpackEncoder encoder_;
145 test::HpackEncoderPeer peer_;
146
147 HpackEntry* static_;
148 HpackEntry* key_1_;
149 HpackEntry* key_2_;
150 HpackEntry* cookie_a_;
151 HpackEntry* cookie_c_;
152
153 HpackOutputStream expected_;
154 };
155
156 TEST_F(HpackEncoderTest, SingleDynamicIndex) {
157 ExpectIndex(key_2_->Index());
158
159 map<string, string> headers;
160 headers[key_2_->name()] = key_2_->value();
161 CompareWithExpectedEncoding(headers);
162
163 // |key_2_| was added to the reference set.
164 EXPECT_THAT(peer_.table()->reference_set(), ElementsAre(key_2_));
165 }
166
167 TEST_F(HpackEncoderTest, SingleStaticIndex) {
168 ExpectIndex(static_->Index());
169
170 map<string, string> headers;
171 headers[static_->name()] = static_->value();
172 CompareWithExpectedEncoding(headers);
173
174 // A new entry copying |static_| was inserted and added to the reference set.
175 HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front();
176 EXPECT_NE(static_, new_entry);
177 EXPECT_EQ(static_->name(), new_entry->name());
178 EXPECT_EQ(static_->value(), new_entry->value());
179 EXPECT_THAT(peer_.table()->reference_set(), ElementsAre(new_entry));
180 }
181
182 TEST_F(HpackEncoderTest, SingleStaticIndexTooLarge) {
183 peer_.table()->SetMaxSize(1); // Also evicts all fixtures.
184 ExpectIndex(static_->Index());
185
186 map<string, string> headers;
187 headers[static_->name()] = static_->value();
188 CompareWithExpectedEncoding(headers);
189
190 EXPECT_EQ(0u, peer_.table_peer().dynamic_entries()->size());
191 EXPECT_EQ(0u, peer_.table()->reference_set().size());
192 }
193
194 TEST_F(HpackEncoderTest, SingleLiteralWithIndexName) {
195 ExpectIndexedLiteral(key_2_, "value3");
196
197 map<string, string> headers;
198 headers[key_2_->name()] = "value3";
199 CompareWithExpectedEncoding(headers);
200
201 // A new entry was inserted and added to the reference set.
202 HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front();
203 EXPECT_EQ(new_entry->name(), key_2_->name());
204 EXPECT_EQ(new_entry->value(), "value3");
205 EXPECT_THAT(peer_.table()->reference_set(), ElementsAre(new_entry));
206 }
207
208 TEST_F(HpackEncoderTest, SingleLiteralWithLiteralName) {
209 ExpectIndexedLiteral("key3", "value3");
210
211 map<string, string> headers;
212 headers["key3"] = "value3";
213 CompareWithExpectedEncoding(headers);
214
215 // A new entry was inserted and added to the reference set.
216 HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front();
217 EXPECT_EQ(new_entry->name(), "key3");
218 EXPECT_EQ(new_entry->value(), "value3");
219 EXPECT_THAT(peer_.table()->reference_set(), ElementsAre(new_entry));
220 }
221
222 TEST_F(HpackEncoderTest, SingleLiteralTooLarge) {
223 peer_.table()->SetMaxSize(1); // Also evicts all fixtures.
224
225 ExpectIndexedLiteral("key3", "value3");
226
227 // A header overflowing the header table is still emitted.
228 // The header table is empty.
229 map<string, string> headers;
230 headers["key3"] = "value3";
231 CompareWithExpectedEncoding(headers);
232
233 EXPECT_EQ(0u, peer_.table_peer().dynamic_entries()->size());
234 EXPECT_EQ(0u, peer_.table()->reference_set().size());
235 }
236
237 TEST_F(HpackEncoderTest, SingleInReferenceSet) {
238 peer_.table()->Toggle(key_2_);
239
240 // Nothing is emitted.
241 map<string, string> headers;
242 headers[key_2_->name()] = key_2_->value();
243 CompareWithExpectedEncoding(headers);
244 }
245
246 TEST_F(HpackEncoderTest, ExplicitToggleOff) {
247 peer_.table()->Toggle(key_1_);
248 peer_.table()->Toggle(key_2_);
249
250 // |key_1_| is explicitly toggled off.
251 ExpectIndex(key_1_->Index());
252
253 map<string, string> headers;
254 headers[key_2_->name()] = key_2_->value();
255 CompareWithExpectedEncoding(headers);
256 }
257
258 TEST_F(HpackEncoderTest, ImplicitToggleOff) {
259 peer_.table()->Toggle(key_1_);
260 peer_.table()->Toggle(key_2_);
261
262 // |key_1_| is evicted. No explicit toggle required.
263 ExpectIndexedLiteral("key3", "value3");
264
265 map<string, string> headers;
266 headers[key_2_->name()] = key_2_->value();
267 headers["key3"] = "value3";
268 CompareWithExpectedEncoding(headers);
269 }
270
271 TEST_F(HpackEncoderTest, ExplicitDoubleToggle) {
272 peer_.table()->Toggle(key_1_);
273
274 // |key_1_| is double-toggled prior to being evicted.
275 ExpectIndex(key_1_->Index());
276 ExpectIndex(key_1_->Index());
277 ExpectIndexedLiteral("key3", "value3");
278
279 map<string, string> headers;
280 headers[key_1_->name()] = key_1_->value();
281 headers["key3"] = "value3";
282 CompareWithExpectedEncoding(headers);
283 }
284
285 TEST_F(HpackEncoderTest, EmitThanEvict) {
286 // |key_1_| is toggled and placed into the reference set,
287 // and then immediately evicted by "key3".
288 ExpectIndex(key_1_->Index());
289 ExpectIndexedLiteral("key3", "value3");
290
291 map<string, string> headers;
292 headers[key_1_->name()] = key_1_->value();
293 headers["key3"] = "value3";
294 CompareWithExpectedEncoding(headers);
295 }
296
297 TEST_F(HpackEncoderTest, CookieHeaderIsCrumbled) {
298 peer_.table()->Toggle(cookie_a_);
299
300 // |cookie_a_| is already in the reference set. |cookie_c_| is
301 // toggled, and "e=ff" is emitted with an indexed name.
302 ExpectIndex(cookie_c_->Index());
303 ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "e=ff");
304
305 map<string, string> headers;
306 headers["cookie"] = "e=ff; a=bb; c=dd";
307 CompareWithExpectedEncoding(headers);
308 }
309
310 TEST_F(HpackEncoderTest, StringsDynamicallySelectHuffmanCoding) {
311 peer_.set_allow_huffman_compression(true);
312
313 // Compactable string. Uses Huffman coding.
314 peer_.EmitString("feedbeef");
315 expected_.AppendPrefix(kStringLiteralHuffmanEncoded);
316 expected_.AppendUint32(5);
317 expected_.AppendBytes("\xC4G\v\xC4q");
318
319 // Non-compactable. Uses identity coding.
320 peer_.EmitString("@@@@@@");
321 expected_.AppendPrefix(kStringLiteralIdentityEncoded);
322 expected_.AppendUint32(6);
323 expected_.AppendBytes("@@@@@@");
324
325 string expected_out, actual_out;
326 expected_.TakeString(&expected_out);
327 peer_.TakeString(&actual_out);
328 EXPECT_EQ(expected_out, actual_out);
329 }
330
331 TEST_F(HpackEncoderTest, EncodingWithoutCompression) {
332 // Implementation should internally disable.
333 peer_.set_allow_huffman_compression(true);
334
335 ExpectNonIndexedLiteral(":path", "/index.html");
336 ExpectNonIndexedLiteral("cookie", "foo=bar; baz=bing");
337 ExpectNonIndexedLiteral("hello", "goodbye");
338
339 map<string, string> headers;
340 headers[":path"] = "/index.html";
341 headers["cookie"] = "foo=bar; baz=bing";
342 headers["hello"] = "goodbye";
343
344 string expected_out, actual_out;
345 expected_.TakeString(&expected_out);
346 encoder_.EncodeHeaderSetWithoutCompression(headers, &actual_out);
347 EXPECT_EQ(expected_out, actual_out);
348 }
349
350 TEST_F(HpackEncoderTest, MultipleEncodingPasses) {
351 // Pass 1: key_1_ and cookie_a_ are toggled on.
352 {
353 map<string, string> headers;
354 headers["key1"] = "value1";
355 headers["cookie"] = "a=bb";
356
357 ExpectIndex(cookie_a_->Index());
358 ExpectIndex(key_1_->Index());
359 CompareWithExpectedEncoding(headers);
360 }
361 // Pass 2: |key_1_| is double-toggled and evicted.
362 // |key_2_| & |cookie_c_| are toggled on.
363 // |cookie_a_| is toggled off.
364 // A new cookie entry is added.
365 {
366 map<string, string> headers;
367 headers["key1"] = "value1";
368 headers["key2"] = "value2";
369 headers["cookie"] = "c=dd; e=ff";
370
371 ExpectIndex(cookie_c_->Index()); // Toggle on.
372 ExpectIndex(key_1_->Index()); // Double-toggle before eviction.
373 ExpectIndex(key_1_->Index());
374 ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "e=ff");
375 ExpectIndex(key_2_->Index() + 1); // Toggle on. Add 1 to reflect insertion.
376 ExpectIndex(cookie_a_->Index() + 1); // Toggle off.
377 CompareWithExpectedEncoding(headers);
378 }
379 // Pass 3: |key_2_| is evicted and implicitly toggled off.
380 // |cookie_c_| is explicitly toggled off.
381 // "key1" is re-inserted.
382 {
383 map<string, string> headers;
384 headers["key1"] = "value1";
385 headers["key3"] = "value3";
386 headers["cookie"] = "e=ff";
387
388 ExpectIndexedLiteral("key1", "value1");
389 ExpectIndexedLiteral("key3", "value3");
390 ExpectIndex(cookie_c_->Index() + 2); // Toggle off. Add 1 for insertion.
391
392 CompareWithExpectedEncoding(headers);
393 }
394 }
395
396 TEST_F(HpackEncoderTest, CookieToCrumbs) {
42 test::HpackEncoderPeer peer(NULL); 397 test::HpackEncoderPeer peer(NULL);
43 std::vector<StringPiece> out; 398 std::vector<StringPiece> out;
44 399
45 // A space after ';' is consumed. All other spaces remain. 400 // A space after ';' is consumed. All other spaces remain.
46 // ';' at begin and end of string produce empty crumbs. 401 // ';' at beginning and end of string produce empty crumbs.
47 peer.CookieToCrumbs(" foo=1;bar=2 ; baz=3; bing=4;", &out); 402 // See section 8.1.3.4 "Compressing the Cookie Header Field" in the HTTP/2
403 // specification at http://tools.ietf.org/html/draft-ietf-httpbis-http2-11
404 peer.CookieToCrumbs(" foo=1;bar=2 ; baz=3; bing=4; ", &out);
48 EXPECT_THAT(out, ElementsAre(" foo=1", "bar=2 ", "baz=3", " bing=4", "")); 405 EXPECT_THAT(out, ElementsAre(" foo=1", "bar=2 ", "baz=3", " bing=4", ""));
49 406
50 peer.CookieToCrumbs(";;foo = bar ;; ;baz =bing", &out); 407 peer.CookieToCrumbs(";;foo = bar ;; ;baz =bing", &out);
51 EXPECT_THAT(out, ElementsAre("", "", "foo = bar ", "", "", "baz =bing")); 408 EXPECT_THAT(out, ElementsAre("", "", "foo = bar ", "", "", "baz =bing"));
52 409
53 peer.CookieToCrumbs("foo=bar; baz=bing", &out); 410 peer.CookieToCrumbs("foo=bar; baz=bing", &out);
54 EXPECT_THAT(out, ElementsAre("foo=bar", "baz=bing")); 411 EXPECT_THAT(out, ElementsAre("foo=bar", "baz=bing"));
55 412
56 peer.CookieToCrumbs("baz=bing", &out); 413 peer.CookieToCrumbs("baz=bing", &out);
57 EXPECT_THAT(out, ElementsAre("baz=bing")); 414 EXPECT_THAT(out, ElementsAre("baz=bing"));
58 415
59 peer.CookieToCrumbs("", &out); 416 peer.CookieToCrumbs("", &out);
60 EXPECT_THAT(out, ElementsAre("")); 417 EXPECT_THAT(out, ElementsAre(""));
61 } 418
62 419 peer.CookieToCrumbs("foo;bar; baz;bing;", &out);
63 // Test that EncoderHeaderSet() simply encodes everything as literals 420 EXPECT_THAT(out, ElementsAre("foo", "bar", "baz", "bing", ""));
64 // without indexing.
65 TEST(HpackEncoderTest, Basic) {
66 HpackEncoder encoder;
67
68 std::map<string, string> header_set1;
69 header_set1["name1"] = "value1";
70 header_set1["name2"] = "value2";
71
72 string encoded_header_set1;
73 EXPECT_TRUE(encoder.EncodeHeaderSet(header_set1, &encoded_header_set1));
74 EXPECT_EQ("\x40\x05name1\x06value1"
75 "\x40\x05name2\x06value2", encoded_header_set1);
76
77 std::map<string, string> header_set2;
78 header_set2["name2"] = "different-value";
79 header_set2["name3"] = "value3";
80
81 string encoded_header_set2;
82 EXPECT_TRUE(encoder.EncodeHeaderSet(header_set2, &encoded_header_set2));
83 EXPECT_EQ("\x40\x05name2\x0f" "different-value"
84 "\x40\x05name3\x06value3", encoded_header_set2);
85 }
86
87 TEST(HpackEncoderTest, CookieCrumbling) {
88 HpackEncoder encoder;
89
90 std::map<string, string> header_set;
91 header_set["Cookie"] = "key1=value1; key2=value2";
92
93 string encoded_header_set;
94 EXPECT_TRUE(encoder.EncodeHeaderSet(header_set, &encoded_header_set));
95 EXPECT_EQ("\x40\x06""Cookie\x0bkey1=value1"
96 "\x40\x06""Cookie\x0bkey2=value2", encoded_header_set);
97 }
98
99 // Test that trying to encode a header set with a too-long header
100 // field will fail.
101 TEST(HpackEncoderTest, HeaderTooLarge) {
102 HpackEncoder encoder;
103 test::HpackEncoderPeer(&encoder).set_max_string_literal_size(10);
104
105 std::map<string, string> header_set;
106 header_set["name1"] = "too-long value";
107 header_set["name2"] = "value2";
108
109 // TODO(akalin): Verify that the encoder did not attempt to encode
110 // the second header field.
111 string encoded_header_set;
112 EXPECT_FALSE(encoder.EncodeHeaderSet(header_set, &encoded_header_set));
113 } 421 }
114 422
115 } // namespace 423 } // namespace
116 424
117 } // namespace net 425 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/hpack_encoder.cc ('k') | net/spdy/hpack_encoding_context.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698