OLD | NEW |
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 |
OLD | NEW |