1 // Copyright 2012 The Chromium Authors
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/http/http_util.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <string_view>
10
11 #include "base/strings/string_util.h"
12 #include "base/time/time.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace net {
16
TEST(HttpUtilTest,IsSafeHeader)17 TEST(HttpUtilTest, IsSafeHeader) {
18 static const char* const unsafe_headers[] = {
19 "sec-",
20 "sEc-",
21 "sec-foo",
22 "sEc-FoO",
23 "proxy-",
24 "pRoXy-",
25 "proxy-foo",
26 "pRoXy-FoO",
27 "accept-charset",
28 "accept-encoding",
29 "access-control-request-headers",
30 "access-control-request-method",
31 "access-control-request-private-network",
32 "connection",
33 "content-length",
34 "cookie",
35 "cookie2",
36 "date",
37 "dnt",
38 "expect",
39 "host",
40 "keep-alive",
41 "origin",
42 "referer",
43 "set-cookie",
44 "te",
45 "trailer",
46 "transfer-encoding",
47 "upgrade",
48 "user-agent",
49 "via",
50 };
51 for (const auto* unsafe_header : unsafe_headers) {
52 EXPECT_FALSE(HttpUtil::IsSafeHeader(unsafe_header, "")) << unsafe_header;
53 EXPECT_FALSE(HttpUtil::IsSafeHeader(base::ToUpperASCII(unsafe_header), ""))
54 << unsafe_header;
55 }
56 static const char* const safe_headers[] = {
57 "foo",
58 "x-",
59 "x-foo",
60 "content-disposition",
61 "update",
62 "accept-charseta",
63 "accept_charset",
64 "accept-encodinga",
65 "accept_encoding",
66 "access-control-request-headersa",
67 "access-control-request-header",
68 "access_control_request_header",
69 "access-control-request-methoda",
70 "access_control_request_method",
71 "connectiona",
72 "content-lengtha",
73 "content_length",
74 "content-transfer-encoding",
75 "cookiea",
76 "cookie2a",
77 "cookie3",
78 "content-transfer-encodinga",
79 "content_transfer_encoding",
80 "datea",
81 "expecta",
82 "hosta",
83 "keep-alivea",
84 "keep_alive",
85 "origina",
86 "referera",
87 "referrer",
88 "tea",
89 "trailera",
90 "transfer-encodinga",
91 "transfer_encoding",
92 "upgradea",
93 "user-agenta",
94 "user_agent",
95 "viaa",
96 // Following 3 headers are safe if there is no forbidden method in values.
97 "x-http-method",
98 "x-http-method-override",
99 "x-method-override",
100 };
101 for (const auto* safe_header : safe_headers) {
102 EXPECT_TRUE(HttpUtil::IsSafeHeader(safe_header, "")) << safe_header;
103 EXPECT_TRUE(HttpUtil::IsSafeHeader(base::ToUpperASCII(safe_header), ""))
104 << safe_header;
105 }
106
107 static const char* const disallowed_with_forbidden_methods_headers[] = {
108 "x-http-method",
109 "x-http-method-override",
110 "x-method-override",
111 };
112 static const struct {
113 const char* value;
114 bool is_safe;
115 } disallowed_values[] = {{"connect", false},
116 {"trace", false},
117 {"track", false},
118 {"CONNECT", false},
119 {"cOnnEcT", false},
120 {"get", true},
121 {"get,post", true},
122 {"get,connect", false},
123 {"get, connect", false},
124 {"get,connect ", false},
125 {"get,connect ,post", false},
126 {"get,,,,connect", false},
127 {"trace,get,PUT", false}};
128 for (const auto* header : disallowed_with_forbidden_methods_headers) {
129 for (const auto& test_case : disallowed_values) {
130 EXPECT_EQ(test_case.is_safe,
131 HttpUtil::IsSafeHeader(header, test_case.value))
132 << header << ": " << test_case.value;
133 }
134 }
135 }
136
TEST(HttpUtilTest,HeadersIterator)137 TEST(HttpUtilTest, HeadersIterator) {
138 std::string headers = "foo: 1\t\r\nbar: hello world\r\nbaz: 3 \r\n";
139
140 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
141
142 ASSERT_TRUE(it.GetNext());
143 EXPECT_EQ(std::string("foo"), it.name());
144 EXPECT_EQ(std::string("1"), it.values());
145
146 ASSERT_TRUE(it.GetNext());
147 EXPECT_EQ(std::string("bar"), it.name());
148 EXPECT_EQ(std::string("hello world"), it.values());
149
150 ASSERT_TRUE(it.GetNext());
151 EXPECT_EQ(std::string("baz"), it.name());
152 EXPECT_EQ(std::string("3"), it.values());
153
154 EXPECT_FALSE(it.GetNext());
155 }
156
TEST(HttpUtilTest,HeadersIterator_MalformedLine)157 TEST(HttpUtilTest, HeadersIterator_MalformedLine) {
158 std::string headers = "foo: 1\n: 2\n3\nbar: 4";
159
160 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n");
161
162 ASSERT_TRUE(it.GetNext());
163 EXPECT_EQ(std::string("foo"), it.name());
164 EXPECT_EQ(std::string("1"), it.values());
165
166 ASSERT_TRUE(it.GetNext());
167 EXPECT_EQ(std::string("bar"), it.name());
168 EXPECT_EQ(std::string("4"), it.values());
169
170 EXPECT_FALSE(it.GetNext());
171 }
172
TEST(HttpUtilTest,HeadersIterator_MalformedName)173 TEST(HttpUtilTest, HeadersIterator_MalformedName) {
174 std::string headers = "[ignore me] /: 3\r\n";
175
176 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
177
178 EXPECT_FALSE(it.GetNext());
179 }
180
TEST(HttpUtilTest,HeadersIterator_MalformedNameFollowedByValidLine)181 TEST(HttpUtilTest, HeadersIterator_MalformedNameFollowedByValidLine) {
182 std::string headers = "[ignore me] /: 3\r\nbar: 4\n";
183
184 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
185
186 ASSERT_TRUE(it.GetNext());
187 EXPECT_EQ(std::string("bar"), it.name());
188 EXPECT_EQ(std::string("4"), it.values());
189
190 EXPECT_FALSE(it.GetNext());
191 }
192
TEST(HttpUtilTest,HeadersIterator_AdvanceTo)193 TEST(HttpUtilTest, HeadersIterator_AdvanceTo) {
194 std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4";
195
196 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
197 EXPECT_TRUE(it.AdvanceTo("foo"));
198 EXPECT_EQ("foo", it.name());
199 EXPECT_TRUE(it.AdvanceTo("bar"));
200 EXPECT_EQ("bar", it.name());
201 EXPECT_FALSE(it.AdvanceTo("blat"));
202 EXPECT_FALSE(it.GetNext()); // should be at end of headers
203 }
204
TEST(HttpUtilTest,HeadersIterator_Reset)205 TEST(HttpUtilTest, HeadersIterator_Reset) {
206 std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4";
207 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
208 // Search past "foo".
209 EXPECT_TRUE(it.AdvanceTo("bar"));
210 // Now try advancing to "foo". This time it should fail since the iterator
211 // position is past it.
212 EXPECT_FALSE(it.AdvanceTo("foo"));
213 it.Reset();
214 // Now that we reset the iterator position, we should find 'foo'
215 EXPECT_TRUE(it.AdvanceTo("foo"));
216 }
217
TEST(HttpUtilTest,ValuesIterator)218 TEST(HttpUtilTest, ValuesIterator) {
219 std::string values = " must-revalidate, no-cache=\"foo, bar\"\t, private ";
220
221 HttpUtil::ValuesIterator it(values.begin(), values.end(), ',',
222 true /* ignore_empty_values */);
223
224 ASSERT_TRUE(it.GetNext());
225 EXPECT_EQ(std::string("must-revalidate"), it.value());
226
227 ASSERT_TRUE(it.GetNext());
228 EXPECT_EQ(std::string("no-cache=\"foo, bar\""), it.value());
229
230 ASSERT_TRUE(it.GetNext());
231 EXPECT_EQ(std::string("private"), it.value());
232
233 EXPECT_FALSE(it.GetNext());
234 }
235
TEST(HttpUtilTest,ValuesIterator_EmptyValues)236 TEST(HttpUtilTest, ValuesIterator_EmptyValues) {
237 std::string values = ", foopy , \t ,,,";
238
239 HttpUtil::ValuesIterator it(values.begin(), values.end(), ',',
240 true /* ignore_empty_values */);
241 ASSERT_TRUE(it.GetNext());
242 EXPECT_EQ(std::string("foopy"), it.value());
243 EXPECT_FALSE(it.GetNext());
244
245 HttpUtil::ValuesIterator it_with_empty_values(
246 values.begin(), values.end(), ',', false /* ignore_empty_values */);
247 ASSERT_TRUE(it_with_empty_values.GetNext());
248 EXPECT_EQ(std::string(""), it_with_empty_values.value());
249
250 ASSERT_TRUE(it_with_empty_values.GetNext());
251 EXPECT_EQ(std::string("foopy"), it_with_empty_values.value());
252
253 ASSERT_TRUE(it_with_empty_values.GetNext());
254 EXPECT_EQ(std::string(""), it_with_empty_values.value());
255
256 ASSERT_TRUE(it_with_empty_values.GetNext());
257 EXPECT_EQ(std::string(""), it_with_empty_values.value());
258
259 ASSERT_TRUE(it_with_empty_values.GetNext());
260 EXPECT_EQ(std::string(""), it_with_empty_values.value());
261
262 ASSERT_TRUE(it_with_empty_values.GetNext());
263 EXPECT_EQ(std::string(""), it_with_empty_values.value());
264
265 EXPECT_FALSE(it_with_empty_values.GetNext());
266 }
267
TEST(HttpUtilTest,ValuesIterator_Blanks)268 TEST(HttpUtilTest, ValuesIterator_Blanks) {
269 std::string values = " \t ";
270
271 HttpUtil::ValuesIterator it(values.begin(), values.end(), ',',
272 true /* ignore_empty_values */);
273 EXPECT_FALSE(it.GetNext());
274
275 HttpUtil::ValuesIterator it_with_empty_values(
276 values.begin(), values.end(), ',', false /* ignore_empty_values */);
277 ASSERT_TRUE(it_with_empty_values.GetNext());
278 EXPECT_EQ(std::string(""), it_with_empty_values.value());
279 EXPECT_FALSE(it_with_empty_values.GetNext());
280 }
281
TEST(HttpUtilTest,Unquote)282 TEST(HttpUtilTest, Unquote) {
283 // Replace <backslash> " with ".
284 EXPECT_STREQ("xyz\"abc", HttpUtil::Unquote("\"xyz\\\"abc\"").c_str());
285
286 // Replace <backslash> <backslash> with <backslash>
287 EXPECT_STREQ("xyz\\abc", HttpUtil::Unquote("\"xyz\\\\abc\"").c_str());
288 EXPECT_STREQ("xyz\\\\\\abc",
289 HttpUtil::Unquote("\"xyz\\\\\\\\\\\\abc\"").c_str());
290
291 // Replace <backslash> X with X
292 EXPECT_STREQ("xyzXabc", HttpUtil::Unquote("\"xyz\\Xabc\"").c_str());
293
294 // Act as identity function on unquoted inputs.
295 EXPECT_STREQ("X", HttpUtil::Unquote("X").c_str());
296 EXPECT_STREQ("\"", HttpUtil::Unquote("\"").c_str());
297
298 // Allow quotes in the middle of the input.
299 EXPECT_STREQ("foo\"bar", HttpUtil::Unquote("\"foo\"bar\"").c_str());
300
301 // Allow the final quote to be escaped.
302 EXPECT_STREQ("foo", HttpUtil::Unquote("\"foo\\\"").c_str());
303 }
304
TEST(HttpUtilTest,StrictUnquote)305 TEST(HttpUtilTest, StrictUnquote) {
306 std::string out;
307
308 // Replace <backslash> " with ".
309 EXPECT_TRUE(HttpUtil::StrictUnquote("\"xyz\\\"abc\"", &out));
310 EXPECT_STREQ("xyz\"abc", out.c_str());
311
312 // Replace <backslash> <backslash> with <backslash>.
313 EXPECT_TRUE(HttpUtil::StrictUnquote("\"xyz\\\\abc\"", &out));
314 EXPECT_STREQ("xyz\\abc", out.c_str());
315 EXPECT_TRUE(HttpUtil::StrictUnquote("\"xyz\\\\\\\\\\\\abc\"", &out));
316 EXPECT_STREQ("xyz\\\\\\abc", out.c_str());
317
318 // Replace <backslash> X with X.
319 EXPECT_TRUE(HttpUtil::StrictUnquote("\"xyz\\Xabc\"", &out));
320 EXPECT_STREQ("xyzXabc", out.c_str());
321
322 // Empty quoted string.
323 EXPECT_TRUE(HttpUtil::StrictUnquote("\"\"", &out));
324 EXPECT_STREQ("", out.c_str());
325
326 // Return false on unquoted inputs.
327 EXPECT_FALSE(HttpUtil::StrictUnquote("X", &out));
328 EXPECT_FALSE(HttpUtil::StrictUnquote("", &out));
329
330 // Return false on mismatched quotes.
331 EXPECT_FALSE(HttpUtil::StrictUnquote("\"", &out));
332 EXPECT_FALSE(HttpUtil::StrictUnquote("\"xyz", &out));
333 EXPECT_FALSE(HttpUtil::StrictUnquote("\"abc'", &out));
334
335 // Return false on escaped terminal quote.
336 EXPECT_FALSE(HttpUtil::StrictUnquote("\"abc\\\"", &out));
337 EXPECT_FALSE(HttpUtil::StrictUnquote("\"\\\"", &out));
338
339 // Allow escaped backslash before terminal quote.
340 EXPECT_TRUE(HttpUtil::StrictUnquote("\"\\\\\"", &out));
341 EXPECT_STREQ("\\", out.c_str());
342
343 // Don't allow single quotes to act as quote marks.
344 EXPECT_FALSE(HttpUtil::StrictUnquote("'x\"'", &out));
345 EXPECT_TRUE(HttpUtil::StrictUnquote("\"x'\"", &out));
346 EXPECT_STREQ("x'", out.c_str());
347 EXPECT_FALSE(HttpUtil::StrictUnquote("''", &out));
348 }
349
TEST(HttpUtilTest,Quote)350 TEST(HttpUtilTest, Quote) {
351 EXPECT_STREQ("\"xyz\\\"abc\"", HttpUtil::Quote("xyz\"abc").c_str());
352
353 // Replace <backslash> <backslash> with <backslash>
354 EXPECT_STREQ("\"xyz\\\\abc\"", HttpUtil::Quote("xyz\\abc").c_str());
355
356 // Replace <backslash> X with X
357 EXPECT_STREQ("\"xyzXabc\"", HttpUtil::Quote("xyzXabc").c_str());
358 }
359
TEST(HttpUtilTest,LocateEndOfHeaders)360 TEST(HttpUtilTest, LocateEndOfHeaders) {
361 struct {
362 const char* const input;
363 size_t expected_result;
364 } tests[] = {
365 {"\r\n", std::string::npos},
366 {"\n", std::string::npos},
367 {"\r", std::string::npos},
368 {"foo", std::string::npos},
369 {"\r\n\r\n", 4},
370 {"foo\r\nbar\r\n\r\n", 12},
371 {"foo\nbar\n\n", 9},
372 {"foo\r\nbar\r\n\r\njunk", 12},
373 {"foo\nbar\n\njunk", 9},
374 {"foo\nbar\n\r\njunk", 10},
375 {"foo\nbar\r\n\njunk", 10},
376 };
377 for (const auto& test : tests) {
378 size_t input_len = strlen(test.input);
379 size_t eoh = HttpUtil::LocateEndOfHeaders(test.input, input_len);
380 EXPECT_EQ(test.expected_result, eoh);
381 }
382 }
383
TEST(HttpUtilTest,LocateEndOfAdditionalHeaders)384 TEST(HttpUtilTest, LocateEndOfAdditionalHeaders) {
385 struct {
386 const char* const input;
387 size_t expected_result;
388 } tests[] = {
389 {"\r\n", 2},
390 {"\n", 1},
391 {"\r", std::string::npos},
392 {"foo", std::string::npos},
393 {"\r\n\r\n", 2},
394 {"foo\r\nbar\r\n\r\n", 12},
395 {"foo\nbar\n\n", 9},
396 {"foo\r\nbar\r\n\r\njunk", 12},
397 {"foo\nbar\n\njunk", 9},
398 {"foo\nbar\n\r\njunk", 10},
399 {"foo\nbar\r\n\njunk", 10},
400 };
401 for (const auto& test : tests) {
402 size_t input_len = strlen(test.input);
403 size_t eoh = HttpUtil::LocateEndOfAdditionalHeaders(test.input, input_len);
404 EXPECT_EQ(test.expected_result, eoh);
405 }
406 }
TEST(HttpUtilTest,AssembleRawHeaders)407 TEST(HttpUtilTest, AssembleRawHeaders) {
408 // clang-format off
409 struct {
410 const char* const input; // with '|' representing '\0'
411 const char* const expected_result; // with '\0' changed to '|'
412 } tests[] = {
413 { "HTTP/1.0 200 OK\r\nFoo: 1\r\nBar: 2\r\n\r\n",
414 "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
415
416 { "HTTP/1.0 200 OK\nFoo: 1\nBar: 2\n\n",
417 "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
418
419 // Valid line continuation (single SP).
420 {
421 "HTTP/1.0 200 OK\n"
422 "Foo: 1\n"
423 " continuation\n"
424 "Bar: 2\n\n",
425
426 "HTTP/1.0 200 OK|"
427 "Foo: 1 continuation|"
428 "Bar: 2||"
429 },
430
431 // Valid line continuation (single HT).
432 {
433 "HTTP/1.0 200 OK\n"
434 "Foo: 1\n"
435 "\tcontinuation\n"
436 "Bar: 2\n\n",
437
438 "HTTP/1.0 200 OK|"
439 "Foo: 1 continuation|"
440 "Bar: 2||"
441 },
442
443 // Valid line continuation (multiple SP).
444 {
445 "HTTP/1.0 200 OK\n"
446 "Foo: 1\n"
447 " continuation\n"
448 "Bar: 2\n\n",
449
450 "HTTP/1.0 200 OK|"
451 "Foo: 1 continuation|"
452 "Bar: 2||"
453 },
454
455 // Valid line continuation (multiple HT).
456 {
457 "HTTP/1.0 200 OK\n"
458 "Foo: 1\n"
459 "\t\t\tcontinuation\n"
460 "Bar: 2\n\n",
461
462 "HTTP/1.0 200 OK|"
463 "Foo: 1 continuation|"
464 "Bar: 2||"
465 },
466
467 // Valid line continuation (mixed HT, SP).
468 {
469 "HTTP/1.0 200 OK\n"
470 "Foo: 1\n"
471 " \t \t continuation\n"
472 "Bar: 2\n\n",
473
474 "HTTP/1.0 200 OK|"
475 "Foo: 1 continuation|"
476 "Bar: 2||"
477 },
478
479 // Valid multi-line continuation
480 {
481 "HTTP/1.0 200 OK\n"
482 "Foo: 1\n"
483 " continuation1\n"
484 "\tcontinuation2\n"
485 " continuation3\n"
486 "Bar: 2\n\n",
487
488 "HTTP/1.0 200 OK|"
489 "Foo: 1 continuation1 continuation2 continuation3|"
490 "Bar: 2||"
491 },
492
493 // Continuation of quoted value.
494 // This is different from what Firefox does, since it
495 // will preserve the LWS.
496 {
497 "HTTP/1.0 200 OK\n"
498 "Etag: \"34534-d3\n"
499 " 134q\"\n"
500 "Bar: 2\n\n",
501
502 "HTTP/1.0 200 OK|"
503 "Etag: \"34534-d3 134q\"|"
504 "Bar: 2||"
505 },
506
507 // Valid multi-line continuation, full LWS lines
508 {
509 "HTTP/1.0 200 OK\n"
510 "Foo: 1\n"
511 " \n"
512 "\t\t\t\t\n"
513 "\t continuation\n"
514 "Bar: 2\n\n",
515
516 // One SP per continued line = 3.
517 "HTTP/1.0 200 OK|"
518 "Foo: 1 continuation|"
519 "Bar: 2||"
520 },
521
522 // Valid multi-line continuation, all LWS
523 {
524 "HTTP/1.0 200 OK\n"
525 "Foo: 1\n"
526 " \n"
527 "\t\t\t\t\n"
528 "\t \n"
529 "Bar: 2\n\n",
530
531 // One SP per continued line = 3.
532 "HTTP/1.0 200 OK|"
533 "Foo: 1 |"
534 "Bar: 2||"
535 },
536
537 // Valid line continuation (No value bytes in first line).
538 {
539 "HTTP/1.0 200 OK\n"
540 "Foo:\n"
541 " value\n"
542 "Bar: 2\n\n",
543
544 "HTTP/1.0 200 OK|"
545 "Foo: value|"
546 "Bar: 2||"
547 },
548
549 // Not a line continuation (can't continue status line).
550 {
551 "HTTP/1.0 200 OK\n"
552 " Foo: 1\n"
553 "Bar: 2\n\n",
554
555 "HTTP/1.0 200 OK|"
556 " Foo: 1|"
557 "Bar: 2||"
558 },
559
560 // Not a line continuation (can't continue status line).
561 {
562 "HTTP/1.0\n"
563 " 200 OK\n"
564 "Foo: 1\n"
565 "Bar: 2\n\n",
566
567 "HTTP/1.0|"
568 " 200 OK|"
569 "Foo: 1|"
570 "Bar: 2||"
571 },
572
573 // Not a line continuation (can't continue status line).
574 {
575 "HTTP/1.0 404\n"
576 " Not Found\n"
577 "Foo: 1\n"
578 "Bar: 2\n\n",
579
580 "HTTP/1.0 404|"
581 " Not Found|"
582 "Foo: 1|"
583 "Bar: 2||"
584 },
585
586 // Unterminated status line.
587 {
588 "HTTP/1.0 200 OK",
589
590 "HTTP/1.0 200 OK||"
591 },
592
593 // Single terminated, with headers
594 {
595 "HTTP/1.0 200 OK\n"
596 "Foo: 1\n"
597 "Bar: 2\n",
598
599 "HTTP/1.0 200 OK|"
600 "Foo: 1|"
601 "Bar: 2||"
602 },
603
604 // Not terminated, with headers
605 {
606 "HTTP/1.0 200 OK\n"
607 "Foo: 1\n"
608 "Bar: 2",
609
610 "HTTP/1.0 200 OK|"
611 "Foo: 1|"
612 "Bar: 2||"
613 },
614
615 // Not a line continuation (VT)
616 {
617 "HTTP/1.0 200 OK\n"
618 "Foo: 1\n"
619 "\vInvalidContinuation\n"
620 "Bar: 2\n\n",
621
622 "HTTP/1.0 200 OK|"
623 "Foo: 1|"
624 "\vInvalidContinuation|"
625 "Bar: 2||"
626 },
627
628 // Not a line continuation (formfeed)
629 {
630 "HTTP/1.0 200 OK\n"
631 "Foo: 1\n"
632 "\fInvalidContinuation\n"
633 "Bar: 2\n\n",
634
635 "HTTP/1.0 200 OK|"
636 "Foo: 1|"
637 "\fInvalidContinuation|"
638 "Bar: 2||"
639 },
640
641 // Not a line continuation -- can't continue header names.
642 {
643 "HTTP/1.0 200 OK\n"
644 "Serv\n"
645 " er: Apache\n"
646 "\tInvalidContinuation\n"
647 "Bar: 2\n\n",
648
649 "HTTP/1.0 200 OK|"
650 "Serv|"
651 " er: Apache|"
652 "\tInvalidContinuation|"
653 "Bar: 2||"
654 },
655
656 // Not a line continuation -- no value to continue.
657 {
658 "HTTP/1.0 200 OK\n"
659 "Foo: 1\n"
660 "garbage\n"
661 " not-a-continuation\n"
662 "Bar: 2\n\n",
663
664 "HTTP/1.0 200 OK|"
665 "Foo: 1|"
666 "garbage|"
667 " not-a-continuation|"
668 "Bar: 2||",
669 },
670
671 // Not a line continuation -- no valid name.
672 {
673 "HTTP/1.0 200 OK\n"
674 ": 1\n"
675 " garbage\n"
676 "Bar: 2\n\n",
677
678 "HTTP/1.0 200 OK|"
679 ": 1|"
680 " garbage|"
681 "Bar: 2||",
682 },
683
684 // Not a line continuation -- no valid name (whitespace)
685 {
686 "HTTP/1.0 200 OK\n"
687 " : 1\n"
688 " garbage\n"
689 "Bar: 2\n\n",
690
691 "HTTP/1.0 200 OK|"
692 " : 1|"
693 " garbage|"
694 "Bar: 2||",
695 },
696
697 // Embed NULLs in the status line. They should not be understood
698 // as line separators.
699 {
700 "HTTP/1.0 200 OK|Bar2:0|Baz2:1\r\nFoo: 1\r\nBar: 2\r\n\r\n",
701 "HTTP/1.0 200 OKBar2:0Baz2:1|Foo: 1|Bar: 2||"
702 },
703
704 // Embed NULLs in a header line. They should not be understood as
705 // line separators.
706 {
707 "HTTP/1.0 200 OK\nFoo: 1|Foo2: 3\nBar: 2\n\n",
708 "HTTP/1.0 200 OK|Foo: 1Foo2: 3|Bar: 2||"
709 },
710
711 // The embedded NUL at the start of the line (before "Blah:") should not be
712 // interpreted as LWS (as that would mistake it for a header line
713 // continuation).
714 {
715 "HTTP/1.0 200 OK\n"
716 "Foo: 1\n"
717 "|Blah: 3\n"
718 "Bar: 2\n\n",
719 "HTTP/1.0 200 OK|Foo: 1|Blah: 3|Bar: 2||"
720 },
721 };
722 // clang-format on
723 for (const auto& test : tests) {
724 std::string input = test.input;
725 std::replace(input.begin(), input.end(), '|', '\0');
726 std::string raw = HttpUtil::AssembleRawHeaders(input);
727 std::replace(raw.begin(), raw.end(), '\0', '|');
728 EXPECT_EQ(test.expected_result, raw);
729 }
730 }
731
732 // Test SpecForRequest().
TEST(HttpUtilTest,RequestUrlSanitize)733 TEST(HttpUtilTest, RequestUrlSanitize) {
734 struct {
735 const char* const url;
736 const char* const expected_spec;
737 } tests[] = {
738 { // Check that #hash is removed.
739 "http://www.google.com:78/foobar?query=1#hash",
740 "http://www.google.com:78/foobar?query=1",
741 },
742 { // The reference may itself contain # -- strip all of it.
743 "http://192.168.0.1?query=1#hash#10#11#13#14",
744 "http://192.168.0.1/?query=1",
745 },
746 { // Strip username/password.
747 "http://user:[email protected]",
748 "http://google.com/",
749 },
750 { // https scheme
751 "https://www.google.com:78/foobar?query=1#hash",
752 "https://www.google.com:78/foobar?query=1",
753 },
754 { // WebSocket's ws scheme
755 "ws://www.google.com:78/foobar?query=1#hash",
756 "ws://www.google.com:78/foobar?query=1",
757 },
758 { // WebSocket's wss scheme
759 "wss://www.google.com:78/foobar?query=1#hash",
760 "wss://www.google.com:78/foobar?query=1",
761 }
762 };
763 for (size_t i = 0; i < std::size(tests); ++i) {
764 SCOPED_TRACE(i);
765
766 GURL url(GURL(tests[i].url));
767 std::string expected_spec(tests[i].expected_spec);
768
769 EXPECT_EQ(expected_spec, HttpUtil::SpecForRequest(url));
770 }
771 }
772
TEST(HttpUtilTest,GenerateAcceptLanguageHeader)773 TEST(HttpUtilTest, GenerateAcceptLanguageHeader) {
774 std::string header = HttpUtil::GenerateAcceptLanguageHeader("");
775 EXPECT_TRUE(header.empty());
776
777 header = HttpUtil::GenerateAcceptLanguageHeader("es");
778 EXPECT_EQ(std::string("es"), header);
779
780 header = HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de");
781 EXPECT_EQ(std::string("en-US,fr;q=0.9,de;q=0.8"), header);
782
783 header = HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de,ko,zh-CN,ja");
784 EXPECT_EQ(
785 std::string("en-US,fr;q=0.9,de;q=0.8,ko;q=0.7,zh-CN;q=0.6,ja;q=0.5"),
786 header);
787 }
788
789 // HttpResponseHeadersTest.GetMimeType also tests ParseContentType.
TEST(HttpUtilTest,ParseContentType)790 TEST(HttpUtilTest, ParseContentType) {
791 // clang-format off
792 const struct {
793 const char* const content_type;
794 const char* const expected_mime_type;
795 const char* const expected_charset;
796 const bool expected_had_charset;
797 const char* const expected_boundary;
798 } tests[] = {
799 { "text/html",
800 "text/html",
801 "",
802 false,
803 ""
804 },
805 { "text/html;",
806 "text/html",
807 "",
808 false,
809 ""
810 },
811 { "text/html; charset=utf-8",
812 "text/html",
813 "utf-8",
814 true,
815 ""
816 },
817 // Parameter name is "charset ", not "charset". See https://crbug.com/772834.
818 { "text/html; charset =utf-8",
819 "text/html",
820 "",
821 false,
822 ""
823 },
824 { "text/html; charset= utf-8",
825 "text/html",
826 "utf-8",
827 true,
828 ""
829 },
830 { "text/html; charset=utf-8 ",
831 "text/html",
832 "utf-8",
833 true,
834 ""
835 },
836
837 { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs\"",
838 "text/html",
839 "",
840 false,
841 "WebKit-ada-df-dsf-adsfadsfs"
842 },
843 // Parameter name is "boundary ", not "boundary".
844 // See https://crbug.com/772834.
845 { "text/html; boundary =\"WebKit-ada-df-dsf-adsfadsfs\"",
846 "text/html",
847 "",
848 false,
849 ""
850 },
851 // Parameter value includes leading space. See https://crbug.com/772834.
852 { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\"",
853 "text/html",
854 "",
855 false,
856 "WebKit-ada-df-dsf-adsfadsfs"
857 },
858 // Parameter value includes leading space. See https://crbug.com/772834.
859 { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\" ",
860 "text/html",
861 "",
862 false,
863 "WebKit-ada-df-dsf-adsfadsfs"
864 },
865 { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs \"",
866 "text/html",
867 "",
868 false,
869 "WebKit-ada-df-dsf-adsfadsfs"
870 },
871 { "text/html; boundary=WebKit-ada-df-dsf-adsfadsfs",
872 "text/html",
873 "",
874 false,
875 "WebKit-ada-df-dsf-adsfadsfs"
876 },
877 { "text/html; charset",
878 "text/html",
879 "",
880 false,
881 ""
882 },
883 { "text/html; charset=",
884 "text/html",
885 "",
886 false,
887 ""
888 },
889 { "text/html; charset= ",
890 "text/html",
891 "",
892 false,
893 ""
894 },
895 { "text/html; charset= ;",
896 "text/html",
897 "",
898 false,
899 ""
900 },
901 // Empty quoted strings are allowed.
902 { "text/html; charset=\"\"",
903 "text/html",
904 "",
905 true,
906 ""
907 },
908
909 // Leading and trailing whitespace in quotes is trimmed.
910 { "text/html; charset=\" \"",
911 "text/html",
912 "",
913 true,
914 ""
915 },
916 { "text/html; charset=\" foo \"",
917 "text/html",
918 "foo",
919 true,
920 ""
921 },
922
923 // With multiple values, should use the first one.
924 { "text/html; charset=foo; charset=utf-8",
925 "text/html",
926 "foo",
927 true,
928 ""
929 },
930 { "text/html; charset; charset=; charset=utf-8",
931 "text/html",
932 "utf-8",
933 true,
934 ""
935 },
936 { "text/html; charset=utf-8; charset=; charset",
937 "text/html",
938 "utf-8",
939 true,
940 ""
941 },
942 { "text/html; boundary=foo; boundary=bar",
943 "text/html",
944 "",
945 false,
946 "foo"
947 },
948
949 // Stray quotes ignored.
950 { "text/html; \"; \"\"; charset=utf-8",
951 "text/html",
952 "utf-8",
953 true,
954 ""
955 },
956 // Non-leading quotes kept as-is.
957 { "text/html; charset=u\"tf-8\"",
958 "text/html",
959 "u\"tf-8\"",
960 true,
961 ""
962 },
963 { "text/html; charset=\"utf-8\"",
964 "text/html",
965 "utf-8",
966 true,
967 ""
968 },
969 // No closing quote.
970 { "text/html; charset=\"utf-8",
971 "text/html",
972 "utf-8",
973 true,
974 ""
975 },
976 // Check that \ is treated as an escape character.
977 { "text/html; charset=\"\\utf\\-\\8\"",
978 "text/html",
979 "utf-8",
980 true,
981 ""
982 },
983 // More interseting escape character test - test escaped backslash, escaped
984 // quote, and backslash at end of input in unterminated quoted string.
985 { "text/html; charset=\"\\\\\\\"\\",
986 "text/html",
987 "\\\"\\",
988 true,
989 ""
990 },
991 // Check quoted semicolon.
992 { "text/html; charset=\";charset=utf-8;\"",
993 "text/html",
994 ";charset=utf-8;",
995 true,
996 ""
997 },
998 // Unclear if this one should just return utf-8 or not.
999 { "text/html; charset= \"utf-8\"",
1000 "text/html",
1001 "utf-8",
1002 true,
1003 ""
1004 },
1005 // Regression test for https://crbug.com/772350:
1006 // Single quotes are not delimiters but must be treated as part of charset.
1007 { "text/html; charset='utf-8'",
1008 "text/html",
1009 "'utf-8'",
1010 true,
1011 ""
1012 },
1013 // Empty subtype should be accepted.
1014 { "text/",
1015 "text/",
1016 "",
1017 false,
1018 ""
1019 },
1020 // "*/*" is ignored unless it has params, or is not an exact match.
1021 { "*/*", "", "", false, "" },
1022 { "*/*; charset=utf-8", "*/*", "utf-8", true, "" },
1023 { "*/* ", "*/*", "", false, "" },
1024 // Regression test for https://crbug.com/1326529
1025 { "teXT/html", "text/html", "", false, ""},
1026 // TODO(abarth): Add more interesting test cases.
1027 };
1028 // clang-format on
1029 for (const auto& test : tests) {
1030 std::string mime_type;
1031 std::string charset;
1032 bool had_charset = false;
1033 std::string boundary;
1034 HttpUtil::ParseContentType(test.content_type, &mime_type, &charset,
1035 &had_charset, &boundary);
1036 EXPECT_EQ(test.expected_mime_type, mime_type)
1037 << "content_type=" << test.content_type;
1038 EXPECT_EQ(test.expected_charset, charset)
1039 << "content_type=" << test.content_type;
1040 EXPECT_EQ(test.expected_had_charset, had_charset)
1041 << "content_type=" << test.content_type;
1042 EXPECT_EQ(test.expected_boundary, boundary)
1043 << "content_type=" << test.content_type;
1044 }
1045 }
1046
TEST(HttpUtilTest,ParseContentResetCharset)1047 TEST(HttpUtilTest, ParseContentResetCharset) {
1048 std::string mime_type;
1049 std::string charset;
1050 bool had_charset = false;
1051 std::string boundary;
1052
1053 // Set mime (capitalization should be ignored), but not charset.
1054 HttpUtil::ParseContentType("Text/Html", &mime_type, &charset, &had_charset,
1055 &boundary);
1056 EXPECT_EQ("text/html", mime_type);
1057 EXPECT_EQ("", charset);
1058 EXPECT_FALSE(had_charset);
1059
1060 // The same mime, add charset.
1061 HttpUtil::ParseContentType("tExt/hTml;charset=utf-8", &mime_type, &charset,
1062 &had_charset, &boundary);
1063 EXPECT_EQ("text/html", mime_type);
1064 EXPECT_EQ("utf-8", charset);
1065 EXPECT_TRUE(had_charset);
1066
1067 // The same mime (different capitalization), but no charset - should not clear
1068 // charset.
1069 HttpUtil::ParseContentType("teXt/htMl", &mime_type, &charset, &had_charset,
1070 &boundary);
1071 EXPECT_EQ("text/html", mime_type);
1072 EXPECT_EQ("utf-8", charset);
1073 EXPECT_TRUE(had_charset);
1074
1075 // A different mime will clear charset.
1076 HttpUtil::ParseContentType("texT/plaiN", &mime_type, &charset, &had_charset,
1077 &boundary);
1078 EXPECT_EQ("text/plain", mime_type);
1079 EXPECT_EQ("", charset);
1080 EXPECT_TRUE(had_charset);
1081 }
1082
TEST(HttpUtilTest,ParseContentRangeHeader)1083 TEST(HttpUtilTest, ParseContentRangeHeader) {
1084 const struct {
1085 const char* const content_range_header_spec;
1086 bool expected_return_value;
1087 int64_t expected_first_byte_position;
1088 int64_t expected_last_byte_position;
1089 int64_t expected_instance_length;
1090 } tests[] = {
1091 {"", false, -1, -1, -1},
1092 {"megabytes 0-10/50", false, -1, -1, -1},
1093 {"0-10/50", false, -1, -1, -1},
1094 {"Bytes 0-50/51", true, 0, 50, 51},
1095 {"bytes 0-50/51", true, 0, 50, 51},
1096 {"bytes\t0-50/51", false, -1, -1, -1},
1097 {" bytes 0-50/51", true, 0, 50, 51},
1098 {" bytes 0 - 50 \t / \t51", true, 0, 50, 51},
1099 {"bytes 0\t-\t50\t/\t51\t", true, 0, 50, 51},
1100 {" \tbytes\t\t\t 0\t-\t50\t/\t51\t", true, 0, 50, 51},
1101 {"\t bytes \t 0 - 50 / 5 1", false, -1, -1, -1},
1102 {"\t bytes \t 0 - 5 0 / 51", false, -1, -1, -1},
1103 {"bytes 50-0/51", false, -1, -1, -1},
1104 {"bytes * /*", false, -1, -1, -1},
1105 {"bytes * / * ", false, -1, -1, -1},
1106 {"bytes 0-50/*", false, -1, -1, -1},
1107 {"bytes 0-50 / * ", false, -1, -1, -1},
1108 {"bytes 0-10000000000/10000000001", true, 0, 10000000000ll,
1109 10000000001ll},
1110 {"bytes 0-10000000000/10000000000", false, -1, -1, -1},
1111 // 64 bit wraparound.
1112 {"bytes 0 - 9223372036854775807 / 100", false, -1, -1, -1},
1113 // 64 bit wraparound.
1114 {"bytes 0 - 100 / -9223372036854775808", false, -1, -1, -1},
1115 {"bytes */50", false, -1, -1, -1},
1116 {"bytes 0-50/10", false, -1, -1, -1},
1117 {"bytes 40-50/45", false, -1, -1, -1},
1118 {"bytes 0-50/-10", false, -1, -1, -1},
1119 {"bytes 0-0/1", true, 0, 0, 1},
1120 {"bytes 0-40000000000000000000/40000000000000000001", false, -1, -1, -1},
1121 {"bytes 1-/100", false, -1, -1, -1},
1122 {"bytes -/100", false, -1, -1, -1},
1123 {"bytes -1/100", false, -1, -1, -1},
1124 {"bytes 0-1233/*", false, -1, -1, -1},
1125 {"bytes -123 - -1/100", false, -1, -1, -1},
1126 };
1127
1128 for (const auto& test : tests) {
1129 int64_t first_byte_position, last_byte_position, instance_length;
1130 EXPECT_EQ(test.expected_return_value,
1131 HttpUtil::ParseContentRangeHeaderFor206(
1132 test.content_range_header_spec, &first_byte_position,
1133 &last_byte_position, &instance_length))
1134 << test.content_range_header_spec;
1135 EXPECT_EQ(test.expected_first_byte_position, first_byte_position)
1136 << test.content_range_header_spec;
1137 EXPECT_EQ(test.expected_last_byte_position, last_byte_position)
1138 << test.content_range_header_spec;
1139 EXPECT_EQ(test.expected_instance_length, instance_length)
1140 << test.content_range_header_spec;
1141 }
1142 }
1143
TEST(HttpUtilTest,ParseRetryAfterHeader)1144 TEST(HttpUtilTest, ParseRetryAfterHeader) {
1145 base::Time::Exploded now_exploded = {2014, 11, 4, 5, 22, 39, 30, 0};
1146 base::Time now;
1147 EXPECT_TRUE(base::Time::FromUTCExploded(now_exploded, &now));
1148
1149 base::Time::Exploded later_exploded = {2015, 1, 5, 1, 12, 34, 56, 0};
1150 base::Time later;
1151 EXPECT_TRUE(base::Time::FromUTCExploded(later_exploded, &later));
1152
1153 const struct {
1154 const char* retry_after_string;
1155 bool expected_return_value;
1156 base::TimeDelta expected_retry_after;
1157 } tests[] = {{"", false, base::TimeDelta()},
1158 {"-3", false, base::TimeDelta()},
1159 {"-2", false, base::TimeDelta()},
1160 {"-1", false, base::TimeDelta()},
1161 {"+0", false, base::TimeDelta()},
1162 {"+1", false, base::TimeDelta()},
1163 {"0", true, base::Seconds(0)},
1164 {"1", true, base::Seconds(1)},
1165 {"2", true, base::Seconds(2)},
1166 {"3", true, base::Seconds(3)},
1167 {"60", true, base::Seconds(60)},
1168 {"3600", true, base::Seconds(3600)},
1169 {"86400", true, base::Seconds(86400)},
1170 {"Thu, 1 Jan 2015 12:34:56 GMT", true, later - now},
1171 {"Mon, 1 Jan 1900 12:34:56 GMT", false, base::TimeDelta()}};
1172
1173 for (size_t i = 0; i < std::size(tests); ++i) {
1174 base::TimeDelta retry_after;
1175 bool return_value = HttpUtil::ParseRetryAfterHeader(
1176 tests[i].retry_after_string, now, &retry_after);
1177 EXPECT_EQ(tests[i].expected_return_value, return_value)
1178 << "Test case " << i << ": expected " << tests[i].expected_return_value
1179 << " but got " << return_value << ".";
1180 if (tests[i].expected_return_value && return_value) {
1181 EXPECT_EQ(tests[i].expected_retry_after, retry_after)
1182 << "Test case " << i << ": expected "
1183 << tests[i].expected_retry_after.InSeconds() << "s but got "
1184 << retry_after.InSeconds() << "s.";
1185 }
1186 }
1187 }
1188
TEST(HttpUtilTest,TimeFormatHTTP)1189 TEST(HttpUtilTest, TimeFormatHTTP) {
1190 constexpr base::Time::Exploded kTime = {.year = 2011,
1191 .month = 4,
1192 .day_of_week = 6,
1193 .day_of_month = 30,
1194 .hour = 22,
1195 .minute = 42,
1196 .second = 7};
1197 base::Time time;
1198 EXPECT_TRUE(base::Time::FromUTCExploded(kTime, &time));
1199 EXPECT_EQ("Sat, 30 Apr 2011 22:42:07 GMT", HttpUtil::TimeFormatHTTP(time));
1200 }
1201
1202 namespace {
CheckCurrentNameValuePair(HttpUtil::NameValuePairsIterator * parser,bool expect_valid,std::string expected_name,std::string expected_value)1203 void CheckCurrentNameValuePair(HttpUtil::NameValuePairsIterator* parser,
1204 bool expect_valid,
1205 std::string expected_name,
1206 std::string expected_value) {
1207 ASSERT_EQ(expect_valid, parser->valid());
1208 if (!expect_valid) {
1209 return;
1210 }
1211
1212 // Let's make sure that these never change (i.e., when a quoted value is
1213 // unquoted, it should be cached on the first calls and not regenerated
1214 // later).
1215 std::string::const_iterator first_value_begin = parser->value_begin();
1216 std::string::const_iterator first_value_end = parser->value_end();
1217
1218 ASSERT_EQ(expected_name, std::string(parser->name_begin(),
1219 parser->name_end()));
1220 ASSERT_EQ(expected_name, parser->name());
1221 ASSERT_EQ(expected_value, std::string(parser->value_begin(),
1222 parser->value_end()));
1223 ASSERT_EQ(expected_value, parser->value());
1224
1225 // Make sure they didn't/don't change.
1226 ASSERT_TRUE(first_value_begin == parser->value_begin());
1227 ASSERT_TRUE(first_value_end == parser->value_end());
1228 }
1229
CheckNextNameValuePair(HttpUtil::NameValuePairsIterator * parser,bool expect_next,bool expect_valid,std::string expected_name,std::string expected_value)1230 void CheckNextNameValuePair(HttpUtil::NameValuePairsIterator* parser,
1231 bool expect_next,
1232 bool expect_valid,
1233 std::string expected_name,
1234 std::string expected_value) {
1235 ASSERT_EQ(expect_next, parser->GetNext());
1236 ASSERT_EQ(expect_valid, parser->valid());
1237 if (!expect_next || !expect_valid) {
1238 return;
1239 }
1240
1241 CheckCurrentNameValuePair(parser,
1242 expect_valid,
1243 expected_name,
1244 expected_value);
1245 }
1246
CheckInvalidNameValuePair(std::string valid_part,std::string invalid_part)1247 void CheckInvalidNameValuePair(std::string valid_part,
1248 std::string invalid_part) {
1249 std::string whole_string = valid_part + invalid_part;
1250
1251 HttpUtil::NameValuePairsIterator valid_parser(valid_part.begin(),
1252 valid_part.end(),
1253 ';');
1254 HttpUtil::NameValuePairsIterator invalid_parser(whole_string.begin(),
1255 whole_string.end(),
1256 ';');
1257
1258 ASSERT_TRUE(valid_parser.valid());
1259 ASSERT_TRUE(invalid_parser.valid());
1260
1261 // Both parsers should return all the same values until "valid_parser" is
1262 // exhausted.
1263 while (valid_parser.GetNext()) {
1264 ASSERT_TRUE(invalid_parser.GetNext());
1265 ASSERT_TRUE(valid_parser.valid());
1266 ASSERT_TRUE(invalid_parser.valid());
1267 ASSERT_EQ(valid_parser.name(), invalid_parser.name());
1268 ASSERT_EQ(valid_parser.value(), invalid_parser.value());
1269 }
1270
1271 // valid_parser is exhausted and remains 'valid'
1272 ASSERT_TRUE(valid_parser.valid());
1273
1274 // invalid_parser's corresponding call to GetNext also returns false...
1275 ASSERT_FALSE(invalid_parser.GetNext());
1276 // ...but the parser is in an invalid state.
1277 ASSERT_FALSE(invalid_parser.valid());
1278 }
1279
1280 } // namespace
1281
TEST(HttpUtilTest,NameValuePairsIteratorCopyAndAssign)1282 TEST(HttpUtilTest, NameValuePairsIteratorCopyAndAssign) {
1283 std::string data =
1284 "alpha=\"\\\"a\\\"\"; beta=\" b \"; cappa=\"c;\"; delta=\"d\"";
1285 HttpUtil::NameValuePairsIterator parser_a(data.begin(), data.end(), ';');
1286
1287 EXPECT_TRUE(parser_a.valid());
1288 ASSERT_NO_FATAL_FAILURE(
1289 CheckNextNameValuePair(&parser_a, true, true, "alpha", "\"a\""));
1290
1291 HttpUtil::NameValuePairsIterator parser_b(parser_a);
1292 // a and b now point to same location
1293 ASSERT_NO_FATAL_FAILURE(
1294 CheckCurrentNameValuePair(&parser_b, true, "alpha", "\"a\""));
1295 ASSERT_NO_FATAL_FAILURE(
1296 CheckCurrentNameValuePair(&parser_a, true, "alpha", "\"a\""));
1297
1298 // advance a, no effect on b
1299 ASSERT_NO_FATAL_FAILURE(
1300 CheckNextNameValuePair(&parser_a, true, true, "beta", " b "));
1301 ASSERT_NO_FATAL_FAILURE(
1302 CheckCurrentNameValuePair(&parser_b, true, "alpha", "\"a\""));
1303
1304 // assign b the current state of a, no effect on a
1305 parser_b = parser_a;
1306 ASSERT_NO_FATAL_FAILURE(
1307 CheckCurrentNameValuePair(&parser_b, true, "beta", " b "));
1308 ASSERT_NO_FATAL_FAILURE(
1309 CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
1310
1311 // advance b, no effect on a
1312 ASSERT_NO_FATAL_FAILURE(
1313 CheckNextNameValuePair(&parser_b, true, true, "cappa", "c;"));
1314 ASSERT_NO_FATAL_FAILURE(
1315 CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
1316 }
1317
TEST(HttpUtilTest,NameValuePairsIteratorEmptyInput)1318 TEST(HttpUtilTest, NameValuePairsIteratorEmptyInput) {
1319 std::string data;
1320 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
1321
1322 EXPECT_TRUE(parser.valid());
1323 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1324 &parser, false, true, std::string(), std::string()));
1325 }
1326
TEST(HttpUtilTest,NameValuePairsIterator)1327 TEST(HttpUtilTest, NameValuePairsIterator) {
1328 std::string data =
1329 "alpha=1; beta= 2 ;"
1330 "cappa =' 3; foo=';"
1331 "cappa =\" 3; foo=\";"
1332 "delta= \" \\\"4\\\" \"; e= \" '5'\"; e=6;"
1333 "f=\"\\\"\\h\\e\\l\\l\\o\\ \\w\\o\\r\\l\\d\\\"\";"
1334 "g=\"\"; h=\"hello\"";
1335 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
1336 EXPECT_TRUE(parser.valid());
1337
1338 ASSERT_NO_FATAL_FAILURE(
1339 CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
1340 ASSERT_NO_FATAL_FAILURE(
1341 CheckNextNameValuePair(&parser, true, true, "beta", "2"));
1342
1343 // Single quotes shouldn't be treated as quotes.
1344 ASSERT_NO_FATAL_FAILURE(
1345 CheckNextNameValuePair(&parser, true, true, "cappa", "' 3"));
1346 ASSERT_NO_FATAL_FAILURE(
1347 CheckNextNameValuePair(&parser, true, true, "foo", "'"));
1348
1349 // But double quotes should be, and can contain semi-colons and equal signs.
1350 ASSERT_NO_FATAL_FAILURE(
1351 CheckNextNameValuePair(&parser, true, true, "cappa", " 3; foo="));
1352
1353 ASSERT_NO_FATAL_FAILURE(
1354 CheckNextNameValuePair(&parser, true, true, "delta", " \"4\" "));
1355 ASSERT_NO_FATAL_FAILURE(
1356 CheckNextNameValuePair(&parser, true, true, "e", " '5'"));
1357 ASSERT_NO_FATAL_FAILURE(
1358 CheckNextNameValuePair(&parser, true, true, "e", "6"));
1359 ASSERT_NO_FATAL_FAILURE(
1360 CheckNextNameValuePair(&parser, true, true, "f", "\"hello world\""));
1361 ASSERT_NO_FATAL_FAILURE(
1362 CheckNextNameValuePair(&parser, true, true, "g", std::string()));
1363 ASSERT_NO_FATAL_FAILURE(
1364 CheckNextNameValuePair(&parser, true, true, "h", "hello"));
1365 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1366 &parser, false, true, std::string(), std::string()));
1367 }
1368
TEST(HttpUtilTest,NameValuePairsIteratorOptionalValues)1369 TEST(HttpUtilTest, NameValuePairsIteratorOptionalValues) {
1370 std::string data = "alpha=1; beta;cappa ; delta; e ; f=1";
1371 // Test that the default parser requires values.
1372 HttpUtil::NameValuePairsIterator default_parser(data.begin(), data.end(),
1373 ';');
1374 EXPECT_TRUE(default_parser.valid());
1375 ASSERT_NO_FATAL_FAILURE(
1376 CheckNextNameValuePair(&default_parser, true, true, "alpha", "1"));
1377 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&default_parser, false, false,
1378 std::string(), std::string()));
1379
1380 HttpUtil::NameValuePairsIterator values_required_parser(
1381 data.begin(), data.end(), ';',
1382 HttpUtil::NameValuePairsIterator::Values::REQUIRED,
1383 HttpUtil::NameValuePairsIterator::Quotes::NOT_STRICT);
1384 EXPECT_TRUE(values_required_parser.valid());
1385 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&values_required_parser, true,
1386 true, "alpha", "1"));
1387 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1388 &values_required_parser, false, false, std::string(), std::string()));
1389
1390 HttpUtil::NameValuePairsIterator parser(
1391 data.begin(), data.end(), ';',
1392 HttpUtil::NameValuePairsIterator::Values::NOT_REQUIRED,
1393 HttpUtil::NameValuePairsIterator::Quotes::NOT_STRICT);
1394 EXPECT_TRUE(parser.valid());
1395
1396 ASSERT_NO_FATAL_FAILURE(
1397 CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
1398 ASSERT_NO_FATAL_FAILURE(
1399 CheckNextNameValuePair(&parser, true, true, "beta", std::string()));
1400 ASSERT_NO_FATAL_FAILURE(
1401 CheckNextNameValuePair(&parser, true, true, "cappa", std::string()));
1402 ASSERT_NO_FATAL_FAILURE(
1403 CheckNextNameValuePair(&parser, true, true, "delta", std::string()));
1404 ASSERT_NO_FATAL_FAILURE(
1405 CheckNextNameValuePair(&parser, true, true, "e", std::string()));
1406 ASSERT_NO_FATAL_FAILURE(
1407 CheckNextNameValuePair(&parser, true, true, "f", "1"));
1408 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&parser, false, true,
1409 std::string(), std::string()));
1410 EXPECT_TRUE(parser.valid());
1411 }
1412
TEST(HttpUtilTest,NameValuePairsIteratorIllegalInputs)1413 TEST(HttpUtilTest, NameValuePairsIteratorIllegalInputs) {
1414 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; beta"));
1415 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "beta"));
1416
1417 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; \"beta\"=2"));
1418 ASSERT_NO_FATAL_FAILURE(
1419 CheckInvalidNameValuePair(std::string(), "\"beta\"=2"));
1420 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";beta="));
1421 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1",
1422 ";beta=;cappa=2"));
1423
1424 // According to the spec this is an error, but it doesn't seem appropriate to
1425 // change our behaviour to be less permissive at this time.
1426 // See NameValuePairsIteratorExtraSeparators test
1427 // ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";; beta=2"));
1428 }
1429
1430 // If we are going to support extra separators against the spec, let's just make
1431 // sure they work rationally.
TEST(HttpUtilTest,NameValuePairsIteratorExtraSeparators)1432 TEST(HttpUtilTest, NameValuePairsIteratorExtraSeparators) {
1433 std::string data = " ; ;;alpha=1; ;; ; beta= 2;cappa=3;;; ; ";
1434 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
1435 EXPECT_TRUE(parser.valid());
1436
1437 ASSERT_NO_FATAL_FAILURE(
1438 CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
1439 ASSERT_NO_FATAL_FAILURE(
1440 CheckNextNameValuePair(&parser, true, true, "beta", "2"));
1441 ASSERT_NO_FATAL_FAILURE(
1442 CheckNextNameValuePair(&parser, true, true, "cappa", "3"));
1443 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1444 &parser, false, true, std::string(), std::string()));
1445 }
1446
1447 // See comments on the implementation of NameValuePairsIterator::GetNext
1448 // regarding this derogation from the spec.
TEST(HttpUtilTest,NameValuePairsIteratorMissingEndQuote)1449 TEST(HttpUtilTest, NameValuePairsIteratorMissingEndQuote) {
1450 std::string data = "name=\"value";
1451 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
1452 EXPECT_TRUE(parser.valid());
1453
1454 ASSERT_NO_FATAL_FAILURE(
1455 CheckNextNameValuePair(&parser, true, true, "name", "value"));
1456 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1457 &parser, false, true, std::string(), std::string()));
1458 }
1459
TEST(HttpUtilTest,NameValuePairsIteratorStrictQuotesEscapedEndQuote)1460 TEST(HttpUtilTest, NameValuePairsIteratorStrictQuotesEscapedEndQuote) {
1461 std::string data = "foo=bar; name=\"value\\\"";
1462 HttpUtil::NameValuePairsIterator parser(
1463 data.begin(), data.end(), ';',
1464 HttpUtil::NameValuePairsIterator::Values::REQUIRED,
1465 HttpUtil::NameValuePairsIterator::Quotes::STRICT_QUOTES);
1466 EXPECT_TRUE(parser.valid());
1467
1468 ASSERT_NO_FATAL_FAILURE(
1469 CheckNextNameValuePair(&parser, true, true, "foo", "bar"));
1470 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&parser, false, false,
1471 std::string(), std::string()));
1472 }
1473
TEST(HttpUtilTest,NameValuePairsIteratorStrictQuotesQuoteInValue)1474 TEST(HttpUtilTest, NameValuePairsIteratorStrictQuotesQuoteInValue) {
1475 std::string data = "foo=\"bar\"; name=\"va\"lue\"";
1476 HttpUtil::NameValuePairsIterator parser(
1477 data.begin(), data.end(), ';',
1478 HttpUtil::NameValuePairsIterator::Values::REQUIRED,
1479 HttpUtil::NameValuePairsIterator::Quotes::STRICT_QUOTES);
1480 EXPECT_TRUE(parser.valid());
1481
1482 ASSERT_NO_FATAL_FAILURE(
1483 CheckNextNameValuePair(&parser, true, true, "foo", "bar"));
1484 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&parser, false, false,
1485 std::string(), std::string()));
1486 }
1487
TEST(HttpUtilTest,NameValuePairsIteratorStrictQuotesMissingEndQuote)1488 TEST(HttpUtilTest, NameValuePairsIteratorStrictQuotesMissingEndQuote) {
1489 std::string data = "foo=\"bar\"; name=\"value";
1490 HttpUtil::NameValuePairsIterator parser(
1491 data.begin(), data.end(), ';',
1492 HttpUtil::NameValuePairsIterator::Values::REQUIRED,
1493 HttpUtil::NameValuePairsIterator::Quotes::STRICT_QUOTES);
1494 EXPECT_TRUE(parser.valid());
1495
1496 ASSERT_NO_FATAL_FAILURE(
1497 CheckNextNameValuePair(&parser, true, true, "foo", "bar"));
1498 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&parser, false, false,
1499 std::string(), std::string()));
1500 }
1501
TEST(HttpUtilTest,NameValuePairsIteratorStrictQuotesSingleQuotes)1502 TEST(HttpUtilTest, NameValuePairsIteratorStrictQuotesSingleQuotes) {
1503 std::string data = "foo=\"bar\"; name='value; ok=it'";
1504 HttpUtil::NameValuePairsIterator parser(
1505 data.begin(), data.end(), ';',
1506 HttpUtil::NameValuePairsIterator::Values::REQUIRED,
1507 HttpUtil::NameValuePairsIterator::Quotes::STRICT_QUOTES);
1508 EXPECT_TRUE(parser.valid());
1509
1510 ASSERT_NO_FATAL_FAILURE(
1511 CheckNextNameValuePair(&parser, true, true, "foo", "bar"));
1512 ASSERT_NO_FATAL_FAILURE(
1513 CheckNextNameValuePair(&parser, true, true, "name", "'value"));
1514 ASSERT_NO_FATAL_FAILURE(
1515 CheckNextNameValuePair(&parser, true, true, "ok", "it'"));
1516 }
1517
TEST(HttpUtilTest,HasValidators)1518 TEST(HttpUtilTest, HasValidators) {
1519 const char* const kMissing = "";
1520 const char* const kEtagEmpty = "\"\"";
1521 const char* const kEtagStrong = "\"strong\"";
1522 const char* const kEtagWeak = "W/\"weak\"";
1523 const char* const kLastModified = "Tue, 15 Nov 1994 12:45:26 GMT";
1524 const char* const kLastModifiedInvalid = "invalid";
1525
1526 const HttpVersion v0_9 = HttpVersion(0, 9);
1527 EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kMissing, kMissing));
1528 EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagStrong, kMissing));
1529 EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagWeak, kMissing));
1530 EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagEmpty, kMissing));
1531
1532 EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kMissing, kLastModified));
1533 EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagStrong, kLastModified));
1534 EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagWeak, kLastModified));
1535 EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagEmpty, kLastModified));
1536
1537 EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kMissing, kLastModifiedInvalid));
1538 EXPECT_FALSE(
1539 HttpUtil::HasValidators(v0_9, kEtagStrong, kLastModifiedInvalid));
1540 EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagWeak, kLastModifiedInvalid));
1541 EXPECT_FALSE(HttpUtil::HasValidators(v0_9, kEtagEmpty, kLastModifiedInvalid));
1542
1543 const HttpVersion v1_0 = HttpVersion(1, 0);
1544 EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kMissing, kMissing));
1545 EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kEtagStrong, kMissing));
1546 EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kEtagWeak, kMissing));
1547 EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kEtagEmpty, kMissing));
1548
1549 EXPECT_TRUE(HttpUtil::HasValidators(v1_0, kMissing, kLastModified));
1550 EXPECT_TRUE(HttpUtil::HasValidators(v1_0, kEtagStrong, kLastModified));
1551 EXPECT_TRUE(HttpUtil::HasValidators(v1_0, kEtagWeak, kLastModified));
1552 EXPECT_TRUE(HttpUtil::HasValidators(v1_0, kEtagEmpty, kLastModified));
1553
1554 EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kMissing, kLastModifiedInvalid));
1555 EXPECT_FALSE(
1556 HttpUtil::HasValidators(v1_0, kEtagStrong, kLastModifiedInvalid));
1557 EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kEtagWeak, kLastModifiedInvalid));
1558 EXPECT_FALSE(HttpUtil::HasValidators(v1_0, kEtagEmpty, kLastModifiedInvalid));
1559
1560 const HttpVersion v1_1 = HttpVersion(1, 1);
1561 EXPECT_FALSE(HttpUtil::HasValidators(v1_1, kMissing, kMissing));
1562 EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagStrong, kMissing));
1563 EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagWeak, kMissing));
1564 EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagEmpty, kMissing));
1565
1566 EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kMissing, kLastModified));
1567 EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagStrong, kLastModified));
1568 EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagWeak, kLastModified));
1569 EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagEmpty, kLastModified));
1570
1571 EXPECT_FALSE(HttpUtil::HasValidators(v1_1, kMissing, kLastModifiedInvalid));
1572 EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagStrong, kLastModifiedInvalid));
1573 EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagWeak, kLastModifiedInvalid));
1574 EXPECT_TRUE(HttpUtil::HasValidators(v1_1, kEtagEmpty, kLastModifiedInvalid));
1575 }
1576
TEST(HttpUtilTest,IsValidHeaderValue)1577 TEST(HttpUtilTest, IsValidHeaderValue) {
1578 const char* const invalid_values[] = {
1579 "X-Requested-With: chrome${NUL}Sec-Unsafe: injected",
1580 "X-Requested-With: chrome\r\nSec-Unsafe: injected",
1581 "X-Requested-With: chrome\nSec-Unsafe: injected",
1582 "X-Requested-With: chrome\rSec-Unsafe: injected",
1583 };
1584 for (const std::string& value : invalid_values) {
1585 std::string replaced = value;
1586 base::ReplaceSubstringsAfterOffset(&replaced, 0, "${NUL}",
1587 std::string(1, '\0'));
1588 EXPECT_FALSE(HttpUtil::IsValidHeaderValue(replaced)) << replaced;
1589 }
1590
1591 // Check that all characters permitted by RFC7230 3.2.6 are allowed.
1592 std::string allowed = "\t";
1593 for (char c = '\x20'; c < '\x7F'; ++c) {
1594 allowed.append(1, c);
1595 }
1596 for (int c = 0x80; c <= 0xFF; ++c) {
1597 allowed.append(1, static_cast<char>(c));
1598 }
1599 EXPECT_TRUE(HttpUtil::IsValidHeaderValue(allowed));
1600 }
1601
TEST(HttpUtilTest,IsToken)1602 TEST(HttpUtilTest, IsToken) {
1603 EXPECT_TRUE(HttpUtil::IsToken("valid"));
1604 EXPECT_TRUE(HttpUtil::IsToken("!"));
1605 EXPECT_TRUE(HttpUtil::IsToken("~"));
1606
1607 EXPECT_FALSE(HttpUtil::IsToken(""));
1608 EXPECT_FALSE(HttpUtil::IsToken(std::string_view()));
1609 EXPECT_FALSE(HttpUtil::IsToken("hello, world"));
1610 EXPECT_FALSE(HttpUtil::IsToken(" "));
1611 EXPECT_FALSE(HttpUtil::IsToken(std::string_view("\0", 1)));
1612 EXPECT_FALSE(HttpUtil::IsToken("\x01"));
1613 EXPECT_FALSE(HttpUtil::IsToken("\x7F"));
1614 EXPECT_FALSE(HttpUtil::IsToken("\x80"));
1615 EXPECT_FALSE(HttpUtil::IsToken("\xff"));
1616 }
1617
TEST(HttpUtilTest,IsLWS)1618 TEST(HttpUtilTest, IsLWS) {
1619 EXPECT_FALSE(HttpUtil::IsLWS('\v'));
1620 EXPECT_FALSE(HttpUtil::IsLWS('\0'));
1621 EXPECT_FALSE(HttpUtil::IsLWS('1'));
1622 EXPECT_FALSE(HttpUtil::IsLWS('a'));
1623 EXPECT_FALSE(HttpUtil::IsLWS('.'));
1624 EXPECT_FALSE(HttpUtil::IsLWS('\n'));
1625 EXPECT_FALSE(HttpUtil::IsLWS('\r'));
1626
1627 EXPECT_TRUE(HttpUtil::IsLWS('\t'));
1628 EXPECT_TRUE(HttpUtil::IsLWS(' '));
1629 }
1630
TEST(HttpUtilTest,IsControlChar)1631 TEST(HttpUtilTest, IsControlChar) {
1632 EXPECT_FALSE(HttpUtil::IsControlChar('1'));
1633 EXPECT_FALSE(HttpUtil::IsControlChar('a'));
1634 EXPECT_FALSE(HttpUtil::IsControlChar('.'));
1635 EXPECT_FALSE(HttpUtil::IsControlChar('$'));
1636 EXPECT_FALSE(HttpUtil::IsControlChar('\x7E'));
1637 EXPECT_FALSE(HttpUtil::IsControlChar('\x80'));
1638 EXPECT_FALSE(HttpUtil::IsControlChar('\xFF'));
1639
1640 EXPECT_TRUE(HttpUtil::IsControlChar('\0'));
1641 EXPECT_TRUE(HttpUtil::IsControlChar('\v'));
1642 EXPECT_TRUE(HttpUtil::IsControlChar('\n'));
1643 EXPECT_TRUE(HttpUtil::IsControlChar('\r'));
1644 EXPECT_TRUE(HttpUtil::IsControlChar('\t'));
1645 EXPECT_TRUE(HttpUtil::IsControlChar('\x01'));
1646 EXPECT_TRUE(HttpUtil::IsControlChar('\x7F'));
1647 }
1648
TEST(HttpUtilTest,ParseAcceptEncoding)1649 TEST(HttpUtilTest, ParseAcceptEncoding) {
1650 const struct {
1651 const char* const value;
1652 const char* const expected;
1653 } tests[] = {
1654 {"", "*"},
1655 {"identity;q=1, *;q=0", "identity"},
1656 {"identity", "identity"},
1657 {"FOO, Bar", "bar|foo|identity"},
1658 {"foo; q=1", "foo|identity"},
1659 {"abc, foo; Q=1.0", "abc|foo|identity"},
1660 {"abc, foo;q= 1.00 , bar", "abc|bar|foo|identity"},
1661 {"abc, foo; q=1.000, bar", "abc|bar|foo|identity"},
1662 {"abc, foo ; q = 0 , bar", "abc|bar|identity"},
1663 {"abc, foo; q=0.0, bar", "abc|bar|identity"},
1664 {"abc, foo; q=0.00, bar", "abc|bar|identity"},
1665 {"abc, foo; q=0.000, bar", "abc|bar|identity"},
1666 {"abc, foo; q=0.001, bar", "abc|bar|foo|identity"},
1667 {"gzip", "gzip|identity|x-gzip"},
1668 {"x-gzip", "gzip|identity|x-gzip"},
1669 {"compress", "compress|identity|x-compress"},
1670 {"x-compress", "compress|identity|x-compress"},
1671 {"x-compress", "compress|identity|x-compress"},
1672 {"foo bar", "INVALID"},
1673 {"foo;", "INVALID"},
1674 {"foo;w=1", "INVALID"},
1675 {"foo;q+1", "INVALID"},
1676 {"foo;q=2", "INVALID"},
1677 {"foo;q=1.001", "INVALID"},
1678 {"foo;q=0.", "INVALID"},
1679 {"foo,\"bar\"", "INVALID"},
1680 };
1681
1682 for (const auto& test : tests) {
1683 std::string value(test.value);
1684 std::string reformatted;
1685 std::set<std::string> allowed_encodings;
1686 if (!HttpUtil::ParseAcceptEncoding(value, &allowed_encodings)) {
1687 reformatted = "INVALID";
1688 } else {
1689 std::vector<std::string> encodings_list;
1690 for (auto const& encoding : allowed_encodings)
1691 encodings_list.push_back(encoding);
1692 reformatted = base::JoinString(encodings_list, "|");
1693 }
1694 EXPECT_STREQ(test.expected, reformatted.c_str())
1695 << "value=\"" << value << "\"";
1696 }
1697 }
1698
TEST(HttpUtilTest,ParseContentEncoding)1699 TEST(HttpUtilTest, ParseContentEncoding) {
1700 const struct {
1701 const char* const value;
1702 const char* const expected;
1703 } tests[] = {
1704 {"", ""},
1705 {"identity;q=1, *;q=0", "INVALID"},
1706 {"identity", "identity"},
1707 {"FOO, zergli , Bar", "bar|foo|zergli"},
1708 {"foo, *", "INVALID"},
1709 {"foo,\"bar\"", "INVALID"},
1710 };
1711
1712 for (const auto& test : tests) {
1713 std::string value(test.value);
1714 std::string reformatted;
1715 std::set<std::string> used_encodings;
1716 if (!HttpUtil::ParseContentEncoding(value, &used_encodings)) {
1717 reformatted = "INVALID";
1718 } else {
1719 std::vector<std::string> encodings_list;
1720 for (auto const& encoding : used_encodings)
1721 encodings_list.push_back(encoding);
1722 reformatted = base::JoinString(encodings_list, "|");
1723 }
1724 EXPECT_STREQ(test.expected, reformatted.c_str())
1725 << "value=\"" << value << "\"";
1726 }
1727 }
1728
1729 // Test the expansion of the Language List.
TEST(HttpUtilTest,ExpandLanguageList)1730 TEST(HttpUtilTest, ExpandLanguageList) {
1731 EXPECT_EQ("", HttpUtil::ExpandLanguageList(""));
1732 EXPECT_EQ("en-US,en", HttpUtil::ExpandLanguageList("en-US"));
1733 EXPECT_EQ("fr", HttpUtil::ExpandLanguageList("fr"));
1734
1735 // The base language is added after all regional codes...
1736 EXPECT_EQ("en-US,en-CA,en", HttpUtil::ExpandLanguageList("en-US,en-CA"));
1737
1738 // ... but before other language families.
1739 EXPECT_EQ("en-US,en-CA,en,fr",
1740 HttpUtil::ExpandLanguageList("en-US,en-CA,fr"));
1741 EXPECT_EQ("en-US,en-CA,en,fr,en-AU",
1742 HttpUtil::ExpandLanguageList("en-US,en-CA,fr,en-AU"));
1743 EXPECT_EQ("en-US,en-CA,en,fr-CA,fr",
1744 HttpUtil::ExpandLanguageList("en-US,en-CA,fr-CA"));
1745
1746 // Add a base language even if it's already in the list.
1747 EXPECT_EQ("en-US,en,fr-CA,fr,it,es-AR,es,it-IT",
1748 HttpUtil::ExpandLanguageList("en-US,fr-CA,it,fr,es-AR,it-IT"));
1749 // Trims a whitespace.
1750 EXPECT_EQ("en-US,en,fr", HttpUtil::ExpandLanguageList("en-US, fr"));
1751
1752 // Do not expand the single character subtag 'x' as a language.
1753 EXPECT_EQ("x-private-agreement-subtags",
1754 HttpUtil::ExpandLanguageList("x-private-agreement-subtags"));
1755 // Do not expand the single character subtag 'i' as a language.
1756 EXPECT_EQ("i-klingon", HttpUtil::ExpandLanguageList("i-klingon"));
1757 }
1758
1759 } // namespace net
1760