• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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