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