OLD | NEW |
| (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/hpack_encoder.h" | |
6 | |
7 #include <map> | |
8 | |
9 #include "base/rand_util.h" | |
10 #include "net/base/arena.h" | |
11 #include "net/spdy/hpack/hpack_huffman_table.h" | |
12 #include "testing/gmock/include/gmock/gmock.h" | |
13 #include "testing/gtest/include/gtest/gtest.h" | |
14 | |
15 namespace net { | |
16 | |
17 using testing::ElementsAre; | |
18 | |
19 namespace test { | |
20 | |
21 class HpackHeaderTablePeer { | |
22 public: | |
23 explicit HpackHeaderTablePeer(HpackHeaderTable* table) : table_(table) {} | |
24 | |
25 HpackHeaderTable::EntryTable* dynamic_entries() { | |
26 return &table_->dynamic_entries_; | |
27 } | |
28 | |
29 private: | |
30 HpackHeaderTable* table_; | |
31 }; | |
32 | |
33 class HpackEncoderPeer { | |
34 public: | |
35 typedef HpackEncoder::Representation Representation; | |
36 typedef HpackEncoder::Representations Representations; | |
37 | |
38 explicit HpackEncoderPeer(HpackEncoder* encoder) : encoder_(encoder) {} | |
39 | |
40 bool compression_enabled() const { return encoder_->enable_compression_; } | |
41 HpackHeaderTable* table() { return &encoder_->header_table_; } | |
42 HpackHeaderTablePeer table_peer() { return HpackHeaderTablePeer(table()); } | |
43 const HpackHuffmanTable& huffman_table() const { | |
44 return encoder_->huffman_table_; | |
45 } | |
46 void EmitString(SpdyStringPiece str) { encoder_->EmitString(str); } | |
47 void TakeString(SpdyString* out) { encoder_->output_stream_.TakeString(out); } | |
48 static void CookieToCrumbs(SpdyStringPiece cookie, | |
49 std::vector<SpdyStringPiece>* out) { | |
50 Representations tmp; | |
51 HpackEncoder::CookieToCrumbs(std::make_pair("", cookie), &tmp); | |
52 | |
53 out->clear(); | |
54 for (size_t i = 0; i != tmp.size(); ++i) { | |
55 out->push_back(tmp[i].second); | |
56 } | |
57 } | |
58 static void DecomposeRepresentation(SpdyStringPiece value, | |
59 std::vector<SpdyStringPiece>* out) { | |
60 Representations tmp; | |
61 HpackEncoder::DecomposeRepresentation(std::make_pair("foobar", value), | |
62 &tmp); | |
63 | |
64 out->clear(); | |
65 for (size_t i = 0; i != tmp.size(); ++i) { | |
66 out->push_back(tmp[i].second); | |
67 } | |
68 } | |
69 | |
70 // TODO(dahollings): Remove or clean up these methods when deprecating | |
71 // non-incremental encoding path. | |
72 static bool EncodeHeaderSet(HpackEncoder* encoder, | |
73 const SpdyHeaderBlock& header_set, | |
74 SpdyString* output, | |
75 bool use_incremental) { | |
76 if (use_incremental) { | |
77 return EncodeIncremental(encoder, header_set, output); | |
78 } else { | |
79 return encoder->EncodeHeaderSet(header_set, output); | |
80 } | |
81 } | |
82 | |
83 static bool EncodeIncremental(HpackEncoder* encoder, | |
84 const SpdyHeaderBlock& header_set, | |
85 SpdyString* output) { | |
86 std::unique_ptr<HpackEncoder::ProgressiveEncoder> encoderator = | |
87 encoder->EncodeHeaderSet(header_set); | |
88 SpdyString output_buffer; | |
89 encoderator->Next(base::RandInt(0, 15), &output_buffer); | |
90 while (encoderator->HasNext()) { | |
91 SpdyString second_buffer; | |
92 encoderator->Next(base::RandInt(0, 15), &second_buffer); | |
93 output_buffer.append(second_buffer); | |
94 } | |
95 *output = std::move(output_buffer); | |
96 return true; | |
97 } | |
98 | |
99 private: | |
100 HpackEncoder* encoder_; | |
101 }; | |
102 | |
103 } // namespace test | |
104 | |
105 namespace { | |
106 | |
107 using std::map; | |
108 using testing::ElementsAre; | |
109 using testing::Pair; | |
110 | |
111 class HpackEncoderTest : public ::testing::TestWithParam<bool> { | |
112 protected: | |
113 typedef test::HpackEncoderPeer::Representations Representations; | |
114 | |
115 HpackEncoderTest() | |
116 : encoder_(ObtainHpackHuffmanTable()), | |
117 peer_(&encoder_), | |
118 static_(peer_.table()->GetByIndex(1)), | |
119 headers_storage_(1024 /* block size */) {} | |
120 | |
121 void SetUp() override { | |
122 use_incremental_ = GetParam(); | |
123 | |
124 // Populate dynamic entries into the table fixture. For simplicity each | |
125 // entry has name.size() + value.size() == 10. | |
126 key_1_ = peer_.table()->TryAddEntry("key1", "value1"); | |
127 key_2_ = peer_.table()->TryAddEntry("key2", "value2"); | |
128 cookie_a_ = peer_.table()->TryAddEntry("cookie", "a=bb"); | |
129 cookie_c_ = peer_.table()->TryAddEntry("cookie", "c=dd"); | |
130 | |
131 // No further insertions may occur without evictions. | |
132 peer_.table()->SetMaxSize(peer_.table()->size()); | |
133 } | |
134 | |
135 void SaveHeaders(SpdyStringPiece name, SpdyStringPiece value) { | |
136 SpdyStringPiece n(headers_storage_.Memdup(name.data(), name.size()), | |
137 name.size()); | |
138 SpdyStringPiece v(headers_storage_.Memdup(value.data(), value.size()), | |
139 value.size()); | |
140 headers_observed_.push_back(std::make_pair(n, v)); | |
141 } | |
142 | |
143 void ExpectIndex(size_t index) { | |
144 expected_.AppendPrefix(kIndexedOpcode); | |
145 expected_.AppendUint32(index); | |
146 } | |
147 void ExpectIndexedLiteral(const HpackEntry* key_entry, | |
148 SpdyStringPiece value) { | |
149 expected_.AppendPrefix(kLiteralIncrementalIndexOpcode); | |
150 expected_.AppendUint32(IndexOf(key_entry)); | |
151 ExpectString(&expected_, value); | |
152 } | |
153 void ExpectIndexedLiteral(SpdyStringPiece name, SpdyStringPiece value) { | |
154 expected_.AppendPrefix(kLiteralIncrementalIndexOpcode); | |
155 expected_.AppendUint32(0); | |
156 ExpectString(&expected_, name); | |
157 ExpectString(&expected_, value); | |
158 } | |
159 void ExpectNonIndexedLiteral(SpdyStringPiece name, SpdyStringPiece value) { | |
160 expected_.AppendPrefix(kLiteralNoIndexOpcode); | |
161 expected_.AppendUint32(0); | |
162 ExpectString(&expected_, name); | |
163 ExpectString(&expected_, value); | |
164 } | |
165 void ExpectString(HpackOutputStream* stream, SpdyStringPiece str) { | |
166 const HpackHuffmanTable& huffman_table = peer_.huffman_table(); | |
167 size_t encoded_size = peer_.compression_enabled() | |
168 ? huffman_table.EncodedSize(str) | |
169 : str.size(); | |
170 if (encoded_size < str.size()) { | |
171 expected_.AppendPrefix(kStringLiteralHuffmanEncoded); | |
172 expected_.AppendUint32(encoded_size); | |
173 huffman_table.EncodeString(str, stream); | |
174 } else { | |
175 expected_.AppendPrefix(kStringLiteralIdentityEncoded); | |
176 expected_.AppendUint32(str.size()); | |
177 expected_.AppendBytes(str); | |
178 } | |
179 } | |
180 void ExpectHeaderTableSizeUpdate(uint32_t size) { | |
181 expected_.AppendPrefix(kHeaderTableSizeUpdateOpcode); | |
182 expected_.AppendUint32(size); | |
183 } | |
184 void CompareWithExpectedEncoding(const SpdyHeaderBlock& header_set) { | |
185 SpdyString expected_out, actual_out; | |
186 expected_.TakeString(&expected_out); | |
187 EXPECT_TRUE(test::HpackEncoderPeer::EncodeHeaderSet( | |
188 &encoder_, header_set, &actual_out, use_incremental_)); | |
189 EXPECT_EQ(expected_out, actual_out); | |
190 } | |
191 size_t IndexOf(const HpackEntry* entry) { | |
192 return peer_.table()->IndexOf(entry); | |
193 } | |
194 | |
195 HpackEncoder encoder_; | |
196 test::HpackEncoderPeer peer_; | |
197 | |
198 const HpackEntry* static_; | |
199 const HpackEntry* key_1_; | |
200 const HpackEntry* key_2_; | |
201 const HpackEntry* cookie_a_; | |
202 const HpackEntry* cookie_c_; | |
203 | |
204 UnsafeArena headers_storage_; | |
205 std::vector<std::pair<SpdyStringPiece, SpdyStringPiece>> headers_observed_; | |
206 | |
207 HpackOutputStream expected_; | |
208 bool use_incremental_; | |
209 }; | |
210 | |
211 INSTANTIATE_TEST_CASE_P(HpackEncoderTests, HpackEncoderTest, ::testing::Bool()); | |
212 | |
213 TEST_P(HpackEncoderTest, SingleDynamicIndex) { | |
214 encoder_.SetHeaderListener( | |
215 [this](SpdyStringPiece name, SpdyStringPiece value) { | |
216 this->SaveHeaders(name, value); | |
217 }); | |
218 | |
219 ExpectIndex(IndexOf(key_2_)); | |
220 | |
221 SpdyHeaderBlock headers; | |
222 headers[key_2_->name().as_string()] = key_2_->value().as_string(); | |
223 CompareWithExpectedEncoding(headers); | |
224 EXPECT_THAT(headers_observed_, | |
225 ElementsAre(Pair(key_2_->name(), key_2_->value()))); | |
226 } | |
227 | |
228 TEST_P(HpackEncoderTest, SingleStaticIndex) { | |
229 ExpectIndex(IndexOf(static_)); | |
230 | |
231 SpdyHeaderBlock headers; | |
232 headers[static_->name().as_string()] = static_->value().as_string(); | |
233 CompareWithExpectedEncoding(headers); | |
234 } | |
235 | |
236 TEST_P(HpackEncoderTest, SingleStaticIndexTooLarge) { | |
237 peer_.table()->SetMaxSize(1); // Also evicts all fixtures. | |
238 ExpectIndex(IndexOf(static_)); | |
239 | |
240 SpdyHeaderBlock headers; | |
241 headers[static_->name().as_string()] = static_->value().as_string(); | |
242 CompareWithExpectedEncoding(headers); | |
243 | |
244 EXPECT_EQ(0u, peer_.table_peer().dynamic_entries()->size()); | |
245 } | |
246 | |
247 TEST_P(HpackEncoderTest, SingleLiteralWithIndexName) { | |
248 ExpectIndexedLiteral(key_2_, "value3"); | |
249 | |
250 SpdyHeaderBlock headers; | |
251 headers[key_2_->name().as_string()] = "value3"; | |
252 CompareWithExpectedEncoding(headers); | |
253 | |
254 // A new entry was inserted and added to the reference set. | |
255 HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front(); | |
256 EXPECT_EQ(new_entry->name(), key_2_->name()); | |
257 EXPECT_EQ(new_entry->value(), "value3"); | |
258 } | |
259 | |
260 TEST_P(HpackEncoderTest, SingleLiteralWithLiteralName) { | |
261 ExpectIndexedLiteral("key3", "value3"); | |
262 | |
263 SpdyHeaderBlock headers; | |
264 headers["key3"] = "value3"; | |
265 CompareWithExpectedEncoding(headers); | |
266 | |
267 HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front(); | |
268 EXPECT_EQ(new_entry->name(), "key3"); | |
269 EXPECT_EQ(new_entry->value(), "value3"); | |
270 } | |
271 | |
272 TEST_P(HpackEncoderTest, SingleLiteralTooLarge) { | |
273 peer_.table()->SetMaxSize(1); // Also evicts all fixtures. | |
274 | |
275 ExpectIndexedLiteral("key3", "value3"); | |
276 | |
277 // A header overflowing the header table is still emitted. | |
278 // The header table is empty. | |
279 SpdyHeaderBlock headers; | |
280 headers["key3"] = "value3"; | |
281 CompareWithExpectedEncoding(headers); | |
282 | |
283 EXPECT_EQ(0u, peer_.table_peer().dynamic_entries()->size()); | |
284 } | |
285 | |
286 TEST_P(HpackEncoderTest, EmitThanEvict) { | |
287 // |key_1_| is toggled and placed into the reference set, | |
288 // and then immediately evicted by "key3". | |
289 ExpectIndex(IndexOf(key_1_)); | |
290 ExpectIndexedLiteral("key3", "value3"); | |
291 | |
292 SpdyHeaderBlock headers; | |
293 headers[key_1_->name().as_string()] = key_1_->value().as_string(); | |
294 headers["key3"] = "value3"; | |
295 CompareWithExpectedEncoding(headers); | |
296 } | |
297 | |
298 TEST_P(HpackEncoderTest, CookieHeaderIsCrumbled) { | |
299 ExpectIndex(IndexOf(cookie_a_)); | |
300 ExpectIndex(IndexOf(cookie_c_)); | |
301 ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "e=ff"); | |
302 | |
303 SpdyHeaderBlock headers; | |
304 headers["cookie"] = "a=bb; c=dd; e=ff"; | |
305 CompareWithExpectedEncoding(headers); | |
306 } | |
307 | |
308 TEST_P(HpackEncoderTest, StringsDynamicallySelectHuffmanCoding) { | |
309 // Compactable string. Uses Huffman coding. | |
310 peer_.EmitString("feedbeef"); | |
311 expected_.AppendPrefix(kStringLiteralHuffmanEncoded); | |
312 expected_.AppendUint32(6); | |
313 expected_.AppendBytes("\x94\xA5\x92\x32\x96_"); | |
314 | |
315 // Non-compactable. Uses identity coding. | |
316 peer_.EmitString("@@@@@@"); | |
317 expected_.AppendPrefix(kStringLiteralIdentityEncoded); | |
318 expected_.AppendUint32(6); | |
319 expected_.AppendBytes("@@@@@@"); | |
320 | |
321 SpdyString expected_out, actual_out; | |
322 expected_.TakeString(&expected_out); | |
323 peer_.TakeString(&actual_out); | |
324 EXPECT_EQ(expected_out, actual_out); | |
325 } | |
326 | |
327 TEST_P(HpackEncoderTest, EncodingWithoutCompression) { | |
328 encoder_.SetHeaderListener( | |
329 [this](SpdyStringPiece name, SpdyStringPiece value) { | |
330 this->SaveHeaders(name, value); | |
331 }); | |
332 encoder_.DisableCompression(); | |
333 | |
334 ExpectNonIndexedLiteral(":path", "/index.html"); | |
335 ExpectNonIndexedLiteral("cookie", "foo=bar"); | |
336 ExpectNonIndexedLiteral("cookie", "baz=bing"); | |
337 ExpectNonIndexedLiteral("hello", "goodbye"); | |
338 | |
339 SpdyHeaderBlock headers; | |
340 headers[":path"] = "/index.html"; | |
341 headers["cookie"] = "foo=bar; baz=bing"; | |
342 headers["hello"] = "goodbye"; | |
343 | |
344 CompareWithExpectedEncoding(headers); | |
345 | |
346 EXPECT_THAT( | |
347 headers_observed_, | |
348 ElementsAre(Pair(":path", "/index.html"), Pair("cookie", "foo=bar"), | |
349 Pair("cookie", "baz=bing"), Pair("hello", "goodbye"))); | |
350 } | |
351 | |
352 TEST_P(HpackEncoderTest, MultipleEncodingPasses) { | |
353 encoder_.SetHeaderListener( | |
354 [this](SpdyStringPiece name, SpdyStringPiece value) { | |
355 this->SaveHeaders(name, value); | |
356 }); | |
357 | |
358 // Pass 1. | |
359 { | |
360 SpdyHeaderBlock headers; | |
361 headers["key1"] = "value1"; | |
362 headers["cookie"] = "a=bb"; | |
363 | |
364 ExpectIndex(IndexOf(key_1_)); | |
365 ExpectIndex(IndexOf(cookie_a_)); | |
366 CompareWithExpectedEncoding(headers); | |
367 } | |
368 // Header table is: | |
369 // 65: key1: value1 | |
370 // 64: key2: value2 | |
371 // 63: cookie: a=bb | |
372 // 62: cookie: c=dd | |
373 // Pass 2. | |
374 { | |
375 SpdyHeaderBlock headers; | |
376 headers["key2"] = "value2"; | |
377 headers["cookie"] = "c=dd; e=ff"; | |
378 | |
379 // "key2: value2" | |
380 ExpectIndex(64); | |
381 // "cookie: c=dd" | |
382 ExpectIndex(62); | |
383 // This cookie evicts |key1| from the dynamic table. | |
384 ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "e=ff"); | |
385 | |
386 CompareWithExpectedEncoding(headers); | |
387 } | |
388 // Header table is: | |
389 // 65: key2: value2 | |
390 // 64: cookie: a=bb | |
391 // 63: cookie: c=dd | |
392 // 62: cookie: e=ff | |
393 // Pass 3. | |
394 { | |
395 SpdyHeaderBlock headers; | |
396 headers["key2"] = "value2"; | |
397 headers["cookie"] = "a=bb; b=cc; c=dd"; | |
398 | |
399 // "key2: value2" | |
400 ExpectIndex(65); | |
401 // "cookie: a=bb" | |
402 ExpectIndex(64); | |
403 // This cookie evicts |key2| from the dynamic table. | |
404 ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "b=cc"); | |
405 // "cookie: c=dd" | |
406 ExpectIndex(64); | |
407 | |
408 CompareWithExpectedEncoding(headers); | |
409 } | |
410 | |
411 // clang-format off | |
412 EXPECT_THAT(headers_observed_, | |
413 ElementsAre(Pair("key1", "value1"), | |
414 Pair("cookie", "a=bb"), | |
415 Pair("key2", "value2"), | |
416 Pair("cookie", "c=dd"), | |
417 Pair("cookie", "e=ff"), | |
418 Pair("key2", "value2"), | |
419 Pair("cookie", "a=bb"), | |
420 Pair("cookie", "b=cc"), | |
421 Pair("cookie", "c=dd"))); | |
422 // clang-format on | |
423 } | |
424 | |
425 TEST_P(HpackEncoderTest, PseudoHeadersFirst) { | |
426 SpdyHeaderBlock headers; | |
427 // A pseudo-header that should not be indexed. | |
428 headers[":path"] = "/spam/eggs.html"; | |
429 // A pseudo-header to be indexed. | |
430 headers[":authority"] = "www.example.com"; | |
431 // A regular header which precedes ":" alphabetically, should still be encoded | |
432 // after pseudo-headers. | |
433 headers["-foo"] = "bar"; | |
434 headers["foo"] = "bar"; | |
435 headers["cookie"] = "c=dd"; | |
436 | |
437 // Headers are indexed in the order in which they were added. | |
438 // This entry pushes "cookie: a=bb" back to 63. | |
439 ExpectNonIndexedLiteral(":path", "/spam/eggs.html"); | |
440 ExpectIndexedLiteral(peer_.table()->GetByName(":authority"), | |
441 "www.example.com"); | |
442 ExpectIndexedLiteral("-foo", "bar"); | |
443 ExpectIndexedLiteral("foo", "bar"); | |
444 ExpectIndexedLiteral(peer_.table()->GetByName("cookie"), "c=dd"); | |
445 CompareWithExpectedEncoding(headers); | |
446 } | |
447 | |
448 TEST_P(HpackEncoderTest, CookieToCrumbs) { | |
449 test::HpackEncoderPeer peer(NULL); | |
450 std::vector<SpdyStringPiece> out; | |
451 | |
452 // Leading and trailing whitespace is consumed. A space after ';' is consumed. | |
453 // All other spaces remain. ';' at beginning and end of string produce empty | |
454 // crumbs. | |
455 // See section 8.1.3.4 "Compressing the Cookie Header Field" in the HTTP/2 | |
456 // specification at http://tools.ietf.org/html/draft-ietf-httpbis-http2-11 | |
457 peer.CookieToCrumbs(" foo=1;bar=2 ; bar=3; bing=4; ", &out); | |
458 EXPECT_THAT(out, ElementsAre("foo=1", "bar=2 ", "bar=3", " bing=4", "")); | |
459 | |
460 peer.CookieToCrumbs(";;foo = bar ;; ;baz =bing", &out); | |
461 EXPECT_THAT(out, ElementsAre("", "", "foo = bar ", "", "", "baz =bing")); | |
462 | |
463 peer.CookieToCrumbs("baz=bing; foo=bar; baz=bing", &out); | |
464 EXPECT_THAT(out, ElementsAre("baz=bing", "foo=bar", "baz=bing")); | |
465 | |
466 peer.CookieToCrumbs("baz=bing", &out); | |
467 EXPECT_THAT(out, ElementsAre("baz=bing")); | |
468 | |
469 peer.CookieToCrumbs("", &out); | |
470 EXPECT_THAT(out, ElementsAre("")); | |
471 | |
472 peer.CookieToCrumbs("foo;bar; baz;baz;bing;", &out); | |
473 EXPECT_THAT(out, ElementsAre("foo", "bar", "baz", "baz", "bing", "")); | |
474 | |
475 peer.CookieToCrumbs(" \t foo=1;bar=2 ; bar=3;\t ", &out); | |
476 EXPECT_THAT(out, ElementsAre("foo=1", "bar=2 ", "bar=3", "")); | |
477 | |
478 peer.CookieToCrumbs(" \t foo=1;bar=2 ; bar=3 \t ", &out); | |
479 EXPECT_THAT(out, ElementsAre("foo=1", "bar=2 ", "bar=3")); | |
480 } | |
481 | |
482 TEST_P(HpackEncoderTest, DecomposeRepresentation) { | |
483 test::HpackEncoderPeer peer(NULL); | |
484 std::vector<SpdyStringPiece> out; | |
485 | |
486 peer.DecomposeRepresentation("", &out); | |
487 EXPECT_THAT(out, ElementsAre("")); | |
488 | |
489 peer.DecomposeRepresentation("foobar", &out); | |
490 EXPECT_THAT(out, ElementsAre("foobar")); | |
491 | |
492 peer.DecomposeRepresentation(SpdyStringPiece("foo\0bar", 7), &out); | |
493 EXPECT_THAT(out, ElementsAre("foo", "bar")); | |
494 | |
495 peer.DecomposeRepresentation(SpdyStringPiece("\0foo\0bar", 8), &out); | |
496 EXPECT_THAT(out, ElementsAre("", "foo", "bar")); | |
497 | |
498 peer.DecomposeRepresentation(SpdyStringPiece("foo\0bar\0", 8), &out); | |
499 EXPECT_THAT(out, ElementsAre("foo", "bar", "")); | |
500 | |
501 peer.DecomposeRepresentation(SpdyStringPiece("\0foo\0bar\0", 9), &out); | |
502 EXPECT_THAT(out, ElementsAre("", "foo", "bar", "")); | |
503 } | |
504 | |
505 // Test that encoded headers do not have \0-delimited multiple values, as this | |
506 // became disallowed in HTTP/2 draft-14. | |
507 TEST_P(HpackEncoderTest, CrumbleNullByteDelimitedValue) { | |
508 SpdyHeaderBlock headers; | |
509 // A header field to be crumbled: "spam: foo\0bar". | |
510 headers["spam"] = SpdyString("foo\0bar", 7); | |
511 | |
512 ExpectIndexedLiteral("spam", "foo"); | |
513 expected_.AppendPrefix(kLiteralIncrementalIndexOpcode); | |
514 expected_.AppendUint32(62); | |
515 expected_.AppendPrefix(kStringLiteralIdentityEncoded); | |
516 expected_.AppendUint32(3); | |
517 expected_.AppendBytes("bar"); | |
518 CompareWithExpectedEncoding(headers); | |
519 } | |
520 | |
521 TEST_P(HpackEncoderTest, HeaderTableSizeUpdate) { | |
522 encoder_.ApplyHeaderTableSizeSetting(1024); | |
523 ExpectHeaderTableSizeUpdate(1024); | |
524 ExpectIndexedLiteral("key3", "value3"); | |
525 | |
526 SpdyHeaderBlock headers; | |
527 headers["key3"] = "value3"; | |
528 CompareWithExpectedEncoding(headers); | |
529 | |
530 HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front(); | |
531 EXPECT_EQ(new_entry->name(), "key3"); | |
532 EXPECT_EQ(new_entry->value(), "value3"); | |
533 } | |
534 | |
535 TEST_P(HpackEncoderTest, HeaderTableSizeUpdateWithMin) { | |
536 const size_t starting_size = peer_.table()->settings_size_bound(); | |
537 encoder_.ApplyHeaderTableSizeSetting(starting_size - 2); | |
538 encoder_.ApplyHeaderTableSizeSetting(starting_size - 1); | |
539 // We must encode the low watermark, so the peer knows to evict entries | |
540 // if necessary. | |
541 ExpectHeaderTableSizeUpdate(starting_size - 2); | |
542 ExpectHeaderTableSizeUpdate(starting_size - 1); | |
543 ExpectIndexedLiteral("key3", "value3"); | |
544 | |
545 SpdyHeaderBlock headers; | |
546 headers["key3"] = "value3"; | |
547 CompareWithExpectedEncoding(headers); | |
548 | |
549 HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front(); | |
550 EXPECT_EQ(new_entry->name(), "key3"); | |
551 EXPECT_EQ(new_entry->value(), "value3"); | |
552 } | |
553 | |
554 TEST_P(HpackEncoderTest, HeaderTableSizeUpdateWithExistingSize) { | |
555 encoder_.ApplyHeaderTableSizeSetting(peer_.table()->settings_size_bound()); | |
556 // No encoded size update. | |
557 ExpectIndexedLiteral("key3", "value3"); | |
558 | |
559 SpdyHeaderBlock headers; | |
560 headers["key3"] = "value3"; | |
561 CompareWithExpectedEncoding(headers); | |
562 | |
563 HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front(); | |
564 EXPECT_EQ(new_entry->name(), "key3"); | |
565 EXPECT_EQ(new_entry->value(), "value3"); | |
566 } | |
567 | |
568 TEST_P(HpackEncoderTest, HeaderTableSizeUpdatesWithGreaterSize) { | |
569 const size_t starting_size = peer_.table()->settings_size_bound(); | |
570 encoder_.ApplyHeaderTableSizeSetting(starting_size + 1); | |
571 encoder_.ApplyHeaderTableSizeSetting(starting_size + 2); | |
572 // Only a single size update to the final size. | |
573 ExpectHeaderTableSizeUpdate(starting_size + 2); | |
574 ExpectIndexedLiteral("key3", "value3"); | |
575 | |
576 SpdyHeaderBlock headers; | |
577 headers["key3"] = "value3"; | |
578 CompareWithExpectedEncoding(headers); | |
579 | |
580 HpackEntry* new_entry = &peer_.table_peer().dynamic_entries()->front(); | |
581 EXPECT_EQ(new_entry->name(), "key3"); | |
582 EXPECT_EQ(new_entry->value(), "value3"); | |
583 } | |
584 | |
585 } // namespace | |
586 | |
587 } // namespace net | |
OLD | NEW |