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