1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/platform/network/http_parsers.h"
6 
7 #include "base/stl_util.h"
8 #include "services/network/public/mojom/content_security_policy.mojom-blink.h"
9 #include "services/network/public/mojom/parsed_headers.mojom-blink.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/blink/renderer/platform/heap/handle.h"
12 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
13 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
14 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
15 
16 namespace blink {
17 
TEST(HTTPParsersTest,ParseCacheControl)18 TEST(HTTPParsersTest, ParseCacheControl) {
19   CacheControlHeader header;
20 
21   header = ParseCacheControlDirectives("no-cache", AtomicString());
22   EXPECT_TRUE(header.parsed);
23   EXPECT_TRUE(header.contains_no_cache);
24   EXPECT_FALSE(header.contains_no_store);
25   EXPECT_FALSE(header.contains_must_revalidate);
26   EXPECT_EQ(base::nullopt, header.max_age);
27   EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
28 
29   header = ParseCacheControlDirectives("no-cache no-store", AtomicString());
30   EXPECT_TRUE(header.parsed);
31   EXPECT_TRUE(header.contains_no_cache);
32   EXPECT_FALSE(header.contains_no_store);
33   EXPECT_FALSE(header.contains_must_revalidate);
34   EXPECT_EQ(base::nullopt, header.max_age);
35   EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
36 
37   header =
38       ParseCacheControlDirectives("no-store must-revalidate", AtomicString());
39   EXPECT_TRUE(header.parsed);
40   EXPECT_FALSE(header.contains_no_cache);
41   EXPECT_TRUE(header.contains_no_store);
42   EXPECT_FALSE(header.contains_must_revalidate);
43   EXPECT_EQ(base::nullopt, header.max_age);
44   EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
45 
46   header = ParseCacheControlDirectives("max-age=0", AtomicString());
47   EXPECT_TRUE(header.parsed);
48   EXPECT_FALSE(header.contains_no_cache);
49   EXPECT_FALSE(header.contains_no_store);
50   EXPECT_FALSE(header.contains_must_revalidate);
51   EXPECT_EQ(base::TimeDelta(), header.max_age.value());
52   EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
53 
54   header = ParseCacheControlDirectives("max-age", AtomicString());
55   EXPECT_TRUE(header.parsed);
56   EXPECT_FALSE(header.contains_no_cache);
57   EXPECT_FALSE(header.contains_no_store);
58   EXPECT_FALSE(header.contains_must_revalidate);
59   EXPECT_EQ(base::nullopt, header.max_age);
60   EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
61 
62   header = ParseCacheControlDirectives("max-age=0 no-cache", AtomicString());
63   EXPECT_TRUE(header.parsed);
64   EXPECT_FALSE(header.contains_no_cache);
65   EXPECT_FALSE(header.contains_no_store);
66   EXPECT_FALSE(header.contains_must_revalidate);
67   EXPECT_EQ(base::TimeDelta(), header.max_age.value());
68   EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
69 
70   header = ParseCacheControlDirectives("no-cache=foo", AtomicString());
71   EXPECT_TRUE(header.parsed);
72   EXPECT_FALSE(header.contains_no_cache);
73   EXPECT_FALSE(header.contains_no_store);
74   EXPECT_FALSE(header.contains_must_revalidate);
75   EXPECT_EQ(base::nullopt, header.max_age);
76   EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
77 
78   header = ParseCacheControlDirectives("nonsense", AtomicString());
79   EXPECT_TRUE(header.parsed);
80   EXPECT_FALSE(header.contains_no_cache);
81   EXPECT_FALSE(header.contains_no_store);
82   EXPECT_FALSE(header.contains_must_revalidate);
83   EXPECT_EQ(base::nullopt, header.max_age);
84   EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
85 
86   header = ParseCacheControlDirectives("\rno-cache\n\t\v\0\b", AtomicString());
87   EXPECT_TRUE(header.parsed);
88   EXPECT_TRUE(header.contains_no_cache);
89   EXPECT_FALSE(header.contains_no_store);
90   EXPECT_FALSE(header.contains_must_revalidate);
91   EXPECT_EQ(base::nullopt, header.max_age);
92   EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
93 
94   header = ParseCacheControlDirectives("      no-cache       ", AtomicString());
95   EXPECT_TRUE(header.parsed);
96   EXPECT_TRUE(header.contains_no_cache);
97   EXPECT_FALSE(header.contains_no_store);
98   EXPECT_FALSE(header.contains_must_revalidate);
99   EXPECT_EQ(base::nullopt, header.max_age);
100   EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
101 
102   header = ParseCacheControlDirectives(AtomicString(), "no-cache");
103   EXPECT_TRUE(header.parsed);
104   EXPECT_TRUE(header.contains_no_cache);
105   EXPECT_FALSE(header.contains_no_store);
106   EXPECT_FALSE(header.contains_must_revalidate);
107   EXPECT_EQ(base::nullopt, header.max_age);
108   EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
109 
110   header = ParseCacheControlDirectives(
111       "stale-while-revalidate=2,stale-while-revalidate=3", AtomicString());
112   EXPECT_TRUE(header.parsed);
113   EXPECT_FALSE(header.contains_no_cache);
114   EXPECT_FALSE(header.contains_no_store);
115   EXPECT_FALSE(header.contains_must_revalidate);
116   EXPECT_EQ(base::nullopt, header.max_age);
117   EXPECT_EQ(2.0, header.stale_while_revalidate.value().InSecondsF());
118 }
119 
TEST(HTTPParsersTest,CommaDelimitedHeaderSet)120 TEST(HTTPParsersTest, CommaDelimitedHeaderSet) {
121   CommaDelimitedHeaderSet set1;
122   CommaDelimitedHeaderSet set2;
123   ParseCommaDelimitedHeader("dpr, rw, whatever", set1);
124   EXPECT_TRUE(set1.Contains("dpr"));
125   EXPECT_TRUE(set1.Contains("rw"));
126   EXPECT_TRUE(set1.Contains("whatever"));
127   ParseCommaDelimitedHeader("dprw\t     , fo\to", set2);
128   EXPECT_FALSE(set2.Contains("dpr"));
129   EXPECT_FALSE(set2.Contains("rw"));
130   EXPECT_FALSE(set2.Contains("whatever"));
131   EXPECT_TRUE(set2.Contains("dprw"));
132   EXPECT_FALSE(set2.Contains("foo"));
133   EXPECT_TRUE(set2.Contains("fo\to"));
134 }
135 
TEST(HTTPParsersTest,HTTPToken)136 TEST(HTTPParsersTest, HTTPToken) {
137   const UChar kHiraganaA[2] = {0x3042, 0};
138   const UChar kLatinCapitalAWithMacron[2] = {0x100, 0};
139 
140   EXPECT_TRUE(blink::IsValidHTTPToken("gzip"));
141   EXPECT_TRUE(blink::IsValidHTTPToken("no-cache"));
142   EXPECT_TRUE(blink::IsValidHTTPToken("86400"));
143   EXPECT_TRUE(blink::IsValidHTTPToken("~"));
144   EXPECT_FALSE(blink::IsValidHTTPToken(""));
145   EXPECT_FALSE(blink::IsValidHTTPToken(" "));
146   EXPECT_FALSE(blink::IsValidHTTPToken("\t"));
147   EXPECT_FALSE(blink::IsValidHTTPToken("\x7f"));
148   EXPECT_FALSE(blink::IsValidHTTPToken("\xff"));
149   EXPECT_FALSE(blink::IsValidHTTPToken(String(kLatinCapitalAWithMacron)));
150   EXPECT_FALSE(blink::IsValidHTTPToken("t a"));
151   EXPECT_FALSE(blink::IsValidHTTPToken("()"));
152   EXPECT_FALSE(blink::IsValidHTTPToken("(foobar)"));
153   EXPECT_FALSE(blink::IsValidHTTPToken(String("\0", 1u)));
154   EXPECT_FALSE(blink::IsValidHTTPToken(String(kHiraganaA)));
155 }
156 
TEST(HTTPParsersTest,ExtractMIMETypeFromMediaType)157 TEST(HTTPParsersTest, ExtractMIMETypeFromMediaType) {
158   const AtomicString text_html("text/html");
159 
160   EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(AtomicString("text/html")));
161   EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
162                            AtomicString("text/html; charset=iso-8859-1")));
163 
164   // Quoted charset parameter
165   EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
166                            AtomicString("text/html; charset=\"quoted\"")));
167 
168   // Multiple parameters
169   EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
170                            AtomicString("text/html; charset=x; foo=bar")));
171 
172   // OWSes are trimmed.
173   EXPECT_EQ(text_html,
174             ExtractMIMETypeFromMediaType(AtomicString(" text/html   ")));
175   EXPECT_EQ(text_html,
176             ExtractMIMETypeFromMediaType(AtomicString("\ttext/html \t")));
177   EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
178                            AtomicString("text/html ; charset=iso-8859-1")));
179 
180   // Non-standard multiple type/subtype listing using a comma as a separator
181   // is accepted.
182   EXPECT_EQ(text_html,
183             ExtractMIMETypeFromMediaType(AtomicString("text/html,text/plain")));
184   EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
185                            AtomicString("text/html , text/plain")));
186   EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
187                            AtomicString("text/html\t,\ttext/plain")));
188   EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(AtomicString(
189                            "text/html,text/plain;charset=iso-8859-1")));
190 
191   // Preserves case.
192   EXPECT_EQ("tExt/hTMl",
193             ExtractMIMETypeFromMediaType(AtomicString("tExt/hTMl")));
194 
195   EXPECT_EQ(g_empty_string,
196             ExtractMIMETypeFromMediaType(AtomicString(", text/html")));
197   EXPECT_EQ(g_empty_string,
198             ExtractMIMETypeFromMediaType(AtomicString("; text/html")));
199 
200   // If no normalization is required, the same AtomicString should be returned.
201   const AtomicString& passthrough = ExtractMIMETypeFromMediaType(text_html);
202   EXPECT_EQ(text_html.Impl(), passthrough.Impl());
203 }
204 
TEST(HTTPParsersTest,ExtractMIMETypeFromMediaTypeInvalidInput)205 TEST(HTTPParsersTest, ExtractMIMETypeFromMediaTypeInvalidInput) {
206   // extractMIMETypeFromMediaType() returns the string before the first
207   // semicolon after trimming OWSes at the head and the tail even if the
208   // string doesn't conform to the media-type ABNF defined in the RFC 7231.
209 
210   // These behaviors could be fixed later when ready.
211 
212   // Non-OWS characters meaning space are not trimmed.
213   EXPECT_EQ(AtomicString("\r\ntext/html\r\n"),
214             ExtractMIMETypeFromMediaType(AtomicString("\r\ntext/html\r\n")));
215   // U+2003, EM SPACE (UTF-8: E2 80 83).
216   EXPECT_EQ(AtomicString::FromUTF8("\xE2\x80\x83text/html"),
217             ExtractMIMETypeFromMediaType(
218                 AtomicString::FromUTF8("\xE2\x80\x83text/html")));
219 
220   // Invalid type/subtype.
221   EXPECT_EQ(AtomicString("a"), ExtractMIMETypeFromMediaType(AtomicString("a")));
222 
223   // Invalid parameters.
224   EXPECT_EQ(AtomicString("text/html"),
225             ExtractMIMETypeFromMediaType(AtomicString("text/html;wow")));
226   EXPECT_EQ(AtomicString("text/html"),
227             ExtractMIMETypeFromMediaType(AtomicString("text/html;;;;;;")));
228   EXPECT_EQ(AtomicString("text/html"),
229             ExtractMIMETypeFromMediaType(AtomicString("text/html; = = = ")));
230 
231   // Only OWSes at either the beginning or the end of the type/subtype
232   // portion.
233   EXPECT_EQ(AtomicString("text / html"),
234             ExtractMIMETypeFromMediaType(AtomicString("text / html")));
235   EXPECT_EQ(AtomicString("t e x t / h t m l"),
236             ExtractMIMETypeFromMediaType(AtomicString("t e x t / h t m l")));
237 
238   EXPECT_EQ(AtomicString("text\r\n/\nhtml"),
239             ExtractMIMETypeFromMediaType(AtomicString("text\r\n/\nhtml")));
240   EXPECT_EQ(AtomicString("text\n/\nhtml"),
241             ExtractMIMETypeFromMediaType(AtomicString("text\n/\nhtml")));
242   EXPECT_EQ(AtomicString::FromUTF8("text\xE2\x80\x83/html"),
243             ExtractMIMETypeFromMediaType(
244                 AtomicString::FromUTF8("text\xE2\x80\x83/html")));
245 }
246 
TEST(HTTPParsersTest,ParseHTTPRefresh)247 TEST(HTTPParsersTest, ParseHTTPRefresh) {
248   base::TimeDelta delay;
249   String url;
250   EXPECT_FALSE(ParseHTTPRefresh("", nullptr, delay, url));
251   EXPECT_FALSE(ParseHTTPRefresh(" ", nullptr, delay, url));
252   EXPECT_FALSE(ParseHTTPRefresh("1.3xyz url=foo", nullptr, delay, url));
253   EXPECT_FALSE(ParseHTTPRefresh("1.3.4xyz url=foo", nullptr, delay, url));
254   EXPECT_FALSE(ParseHTTPRefresh("1e1 url=foo", nullptr, delay, url));
255 
256   EXPECT_TRUE(ParseHTTPRefresh("123 ", nullptr, delay, url));
257   EXPECT_EQ(base::TimeDelta::FromSeconds(123), delay);
258   EXPECT_TRUE(url.IsEmpty());
259 
260   EXPECT_TRUE(ParseHTTPRefresh("1 ; url=dest", nullptr, delay, url));
261   EXPECT_EQ(base::TimeDelta::FromSeconds(1), delay);
262   EXPECT_EQ("dest", url);
263   EXPECT_TRUE(
264       ParseHTTPRefresh("1 ;\nurl=dest", IsASCIISpace<UChar>, delay, url));
265   EXPECT_EQ(base::TimeDelta::FromSeconds(1), delay);
266   EXPECT_EQ("dest", url);
267   EXPECT_TRUE(ParseHTTPRefresh("1 ;\nurl=dest", nullptr, delay, url));
268   EXPECT_EQ(base::TimeDelta::FromSeconds(1), delay);
269   EXPECT_EQ("url=dest", url);
270 
271   EXPECT_TRUE(ParseHTTPRefresh("1 url=dest", nullptr, delay, url));
272   EXPECT_EQ(base::TimeDelta::FromSeconds(1), delay);
273   EXPECT_EQ("dest", url);
274 
275   EXPECT_TRUE(
276       ParseHTTPRefresh("10\nurl=dest", IsASCIISpace<UChar>, delay, url));
277   EXPECT_EQ(base::TimeDelta::FromSeconds(10), delay);
278   EXPECT_EQ("dest", url);
279 
280   EXPECT_TRUE(
281       ParseHTTPRefresh("1.5; url=dest", IsASCIISpace<UChar>, delay, url));
282   EXPECT_EQ(base::TimeDelta::FromSecondsD(1.5), delay);
283   EXPECT_EQ("dest", url);
284   EXPECT_TRUE(
285       ParseHTTPRefresh("1.5.9; url=dest", IsASCIISpace<UChar>, delay, url));
286   EXPECT_EQ(base::TimeDelta::FromSecondsD(1.5), delay);
287   EXPECT_EQ("dest", url);
288   EXPECT_TRUE(
289       ParseHTTPRefresh("7..; url=dest", IsASCIISpace<UChar>, delay, url));
290   EXPECT_EQ(base::TimeDelta::FromSeconds(7), delay);
291   EXPECT_EQ("dest", url);
292 }
293 
TEST(HTTPParsersTest,ParseMultipartHeadersResult)294 TEST(HTTPParsersTest, ParseMultipartHeadersResult) {
295   struct {
296     const char* data;
297     const bool result;
298     const size_t end;
299   } tests[] = {
300       {"This is junk", false, 0},
301       {"Foo: bar\nBaz:\n\nAfter:\n", true, 15},
302       {"Foo: bar\nBaz:\n", false, 0},
303       {"Foo: bar\r\nBaz:\r\n\r\nAfter:\r\n", true, 18},
304       {"Foo: bar\r\nBaz:\r\n", false, 0},
305       {"Foo: bar\nBaz:\r\n\r\nAfter:\n\n", true, 17},
306       {"Foo: bar\r\nBaz:\n", false, 0},
307       {"\r\n", true, 2},
308   };
309   for (size_t i = 0; i < base::size(tests); ++i) {
310     ResourceResponse response;
311     wtf_size_t end = 0;
312     bool result = ParseMultipartHeadersFromBody(
313         tests[i].data, static_cast<wtf_size_t>(strlen(tests[i].data)),
314         &response, &end);
315     EXPECT_EQ(tests[i].result, result);
316     EXPECT_EQ(tests[i].end, end);
317   }
318 }
319 
TEST(HTTPParsersTest,ParseMultipartHeaders)320 TEST(HTTPParsersTest, ParseMultipartHeaders) {
321   ResourceResponse response;
322   response.AddHttpHeaderField("foo", "bar");
323   response.AddHttpHeaderField("range", "piyo");
324   response.AddHttpHeaderField("content-length", "999");
325   response.AddHttpHeaderField("set-cookie", "a=1");
326 
327   const char kData[] =
328       "content-type: image/png\n"
329       "content-length: 10\n"
330       "set-cookie: x=2\n"
331       "set-cookie: y=3\n"
332       "\n";
333   wtf_size_t end = 0;
334   bool result =
335       ParseMultipartHeadersFromBody(kData, strlen(kData), &response, &end);
336 
337   EXPECT_TRUE(result);
338   EXPECT_EQ(strlen(kData), end);
339   EXPECT_EQ("image/png", response.HttpHeaderField("content-type"));
340   EXPECT_EQ("10", response.HttpHeaderField("content-length"));
341   EXPECT_EQ("bar", response.HttpHeaderField("foo"));
342   EXPECT_EQ(AtomicString(), response.HttpHeaderField("range"));
343   EXPECT_EQ("x=2, y=3", response.HttpHeaderField("set-cookie"));
344 }
345 
TEST(HTTPParsersTest,ParseMultipartHeadersContentCharset)346 TEST(HTTPParsersTest, ParseMultipartHeadersContentCharset) {
347   ResourceResponse response;
348   const char kData[] = "content-type: text/html; charset=utf-8\n\n";
349   wtf_size_t end = 0;
350   bool result =
351       ParseMultipartHeadersFromBody(kData, strlen(kData), &response, &end);
352 
353   EXPECT_TRUE(result);
354   EXPECT_EQ(strlen(kData), end);
355   EXPECT_EQ("text/html; charset=utf-8",
356             response.HttpHeaderField("content-type"));
357   EXPECT_EQ("utf-8", response.TextEncodingName());
358 }
359 
testServerTimingHeader(const char * headerValue,Vector<Vector<String>> expectedResults)360 void testServerTimingHeader(const char* headerValue,
361                             Vector<Vector<String>> expectedResults) {
362   std::unique_ptr<ServerTimingHeaderVector> results =
363       ParseServerTimingHeader(headerValue);
364   EXPECT_EQ((*results).size(), expectedResults.size());
365   unsigned i = 0;
366   for (const auto& header : *results) {
367     Vector<String> expectedResult = expectedResults[i++];
368     EXPECT_EQ(header->Name(), expectedResult[0]);
369     EXPECT_EQ(header->Duration(), expectedResult[1].ToDouble());
370     EXPECT_EQ(header->Description(), expectedResult[2]);
371   }
372 }
373 
TEST(HTTPParsersTest,ParseServerTimingHeader)374 TEST(HTTPParsersTest, ParseServerTimingHeader) {
375   // empty string
376   testServerTimingHeader("", {});
377 
378   // name only
379   testServerTimingHeader("metric", {{"metric", "0", ""}});
380 
381   // name and duration
382   testServerTimingHeader("metric;dur=123.4", {{"metric", "123.4", ""}});
383   testServerTimingHeader("metric;dur=\"123.4\"", {{"metric", "123.4", ""}});
384 
385   // name and description
386   testServerTimingHeader("metric;desc=description",
387                          {{"metric", "0", "description"}});
388   testServerTimingHeader("metric;desc=\"description\"",
389                          {{"metric", "0", "description"}});
390 
391   // name, duration, and description
392   testServerTimingHeader("metric;dur=123.4;desc=description",
393                          {{"metric", "123.4", "description"}});
394   testServerTimingHeader("metric;desc=description;dur=123.4",
395                          {{"metric", "123.4", "description"}});
396 
397   // special chars in name
398   testServerTimingHeader("aB3!#$%&'*+-.^_`|~",
399                          {{"aB3!#$%&'*+-.^_`|~", "0", ""}});
400 
401   // delimiter chars in quoted description
402   testServerTimingHeader("metric;desc=\"descr;,=iption\";dur=123.4",
403                          {{"metric", "123.4", "descr;,=iption"}});
404 
405   // spaces
406   testServerTimingHeader("metric ; ", {{"metric", "0", ""}});
407   testServerTimingHeader("metric , ", {{"metric", "0", ""}});
408   testServerTimingHeader("metric ; dur = 123.4 ; desc = description",
409                          {{"metric", "123.4", "description"}});
410   testServerTimingHeader("metric ; desc = description ; dur = 123.4",
411                          {{"metric", "123.4", "description"}});
412   testServerTimingHeader("metric;desc = \"description\"",
413                          {{"metric", "0", "description"}});
414 
415   // tabs
416   /* known failures:
417   https://bugs.chromium.org/p/chromium/issues/detail?id=798446
418   testServerTimingHeader("metric\t;\t", {{"metric", "0", ""}});
419   testServerTimingHeader("metric\t,\t", {{"metric", "0", ""}});
420   testServerTimingHeader("metric\t;\tdur\t=\t123.4\t;\tdesc\t=\tdescription",
421   {{"metric", "123.4", "description"}});
422   testServerTimingHeader("metric\t;\tdesc\t=\tdescription\t;\tdur\t=\t123.4",
423   {{"metric", "123.4", "description"}});
424   testServerTimingHeader("metric;desc\t=\t\"description\"", {{"metric", "0",
425   "description"}});
426   */
427 
428   // multiple entries
429   testServerTimingHeader(
430       "metric1;dur=12.3;desc=description1,metric2;dur=45.6;desc=description2,"
431       "metric3;dur=78.9;desc=description3",
432       {{"metric1", "12.3", "description1"},
433        {"metric2", "45.6", "description2"},
434        {"metric3", "78.9", "description3"}});
435   testServerTimingHeader("metric1,metric2 ,metric3, metric4 , metric5",
436                          {{"metric1", "0", ""},
437                           {"metric2", "0", ""},
438                           {"metric3", "0", ""},
439                           {"metric4", "0", ""},
440                           {"metric5", "0", ""}});
441 
442   // quoted-strings - happy path
443   testServerTimingHeader("metric;desc=\"description\"",
444                          {{"metric", "0", "description"}});
445   testServerTimingHeader("metric;desc=\"\t description \t\"",
446                          {{"metric", "0", "\t description \t"}});
447   testServerTimingHeader("metric;desc=\"descr\\\"iption\"",
448                          {{"metric", "0", "descr\"iption"}});
449 
450   // quoted-strings - others
451   // metric;desc=\ --> ''
452   testServerTimingHeader("metric;desc=\\", {{"metric", "0", ""}});
453   // metric;desc=" --> ''
454   testServerTimingHeader("metric;desc=\"", {{"metric", "0", ""}});
455   // metric;desc=\\ --> ''
456   testServerTimingHeader("metric;desc=\\\\", {{"metric", "0", ""}});
457   // metric;desc=\" --> ''
458   testServerTimingHeader("metric;desc=\\\"", {{"metric", "0", ""}});
459   // metric;desc="\ --> ''
460   testServerTimingHeader("metric;desc=\"\\", {{"metric", "0", ""}});
461   // metric;desc="" --> ''
462   testServerTimingHeader("metric;desc=\"\"", {{"metric", "0", ""}});
463   // metric;desc=\\\ --> ''
464   testServerTimingHeader("metric;desc=\\\\\\", {{"metric", "0", ""}});
465   // metric;desc=\\" --> ''
466   testServerTimingHeader("metric;desc=\\\\\"", {{"metric", "0", ""}});
467   // metric;desc=\"\ --> ''
468   testServerTimingHeader("metric;desc=\\\"\\", {{"metric", "0", ""}});
469   // metric;desc=\"" --> ''
470   testServerTimingHeader("metric;desc=\\\"\"", {{"metric", "0", ""}});
471   // metric;desc="\\ --> ''
472   testServerTimingHeader("metric;desc=\"\\\\", {{"metric", "0", ""}});
473   // metric;desc="\" --> ''
474   testServerTimingHeader("metric;desc=\"\\\"", {{"metric", "0", ""}});
475   // metric;desc=""\ --> ''
476   testServerTimingHeader("metric;desc=\"\"\\", {{"metric", "0", ""}});
477   // metric;desc=""" --> ''
478   testServerTimingHeader("metric;desc=\"\"\"", {{"metric", "0", ""}});
479   // metric;desc=\\\\ --> ''
480   testServerTimingHeader("metric;desc=\\\\\\\\", {{"metric", "0", ""}});
481   // metric;desc=\\\" --> ''
482   testServerTimingHeader("metric;desc=\\\\\\\"", {{"metric", "0", ""}});
483   // metric;desc=\\"\ --> ''
484   testServerTimingHeader("metric;desc=\\\\\"\\", {{"metric", "0", ""}});
485   // metric;desc=\\"" --> ''
486   testServerTimingHeader("metric;desc=\\\\\"\"", {{"metric", "0", ""}});
487   // metric;desc=\"\\ --> ''
488   testServerTimingHeader("metric;desc=\\\"\\\\", {{"metric", "0", ""}});
489   // metric;desc=\"\" --> ''
490   testServerTimingHeader("metric;desc=\\\"\\\"", {{"metric", "0", ""}});
491   // metric;desc=\""\ --> ''
492   testServerTimingHeader("metric;desc=\\\"\"\\", {{"metric", "0", ""}});
493   // metric;desc=\""" --> ''
494   testServerTimingHeader("metric;desc=\\\"\"\"", {{"metric", "0", ""}});
495   // metric;desc="\\\ --> ''
496   testServerTimingHeader("metric;desc=\"\\\\\\", {{"metric", "0", ""}});
497   // metric;desc="\\" --> '\'
498   testServerTimingHeader("metric;desc=\"\\\\\"", {{"metric", "0", "\\"}});
499   // metric;desc="\"\ --> ''
500   testServerTimingHeader("metric;desc=\"\\\"\\", {{"metric", "0", ""}});
501   // metric;desc="\"" --> '"'
502   testServerTimingHeader("metric;desc=\"\\\"\"", {{"metric", "0", "\""}});
503   // metric;desc=""\\ --> ''
504   testServerTimingHeader("metric;desc=\"\"\\\\", {{"metric", "0", ""}});
505   // metric;desc=""\" --> ''
506   testServerTimingHeader("metric;desc=\"\"\\\"", {{"metric", "0", ""}});
507   // metric;desc="""\ --> ''
508   testServerTimingHeader("metric;desc=\"\"\"\\", {{"metric", "0", ""}});
509   // metric;desc="""" --> ''
510   testServerTimingHeader("metric;desc=\"\"\"\"", {{"metric", "0", ""}});
511 
512   // duplicate entry names
513   testServerTimingHeader(
514       "metric;dur=12.3;desc=description1,metric;dur=45.6;desc=description2",
515       {{"metric", "12.3", "description1"}, {"metric", "45.6", "description2"}});
516 
517   // param name case sensitivity
518   testServerTimingHeader("metric;DuR=123.4;DeSc=description",
519                          {{"metric", "123.4", "description"}});
520 
521   // non-numeric durations
522   testServerTimingHeader("metric;dur=foo", {{"metric", "0", ""}});
523   testServerTimingHeader("metric;dur=\"foo\"", {{"metric", "0", ""}});
524 
525   // unrecognized param names
526   testServerTimingHeader(
527       "metric1;foo=bar;desc=description;foo=bar;dur=123.4;foo=bar,metric2",
528       {{"metric1", "123.4", "description"}, {"metric2", "0", ""}});
529 
530   // duplicate param names
531   testServerTimingHeader("metric;dur=123.4;dur=567.8",
532                          {{"metric", "123.4", ""}});
533   testServerTimingHeader("metric;dur=foo;dur=567.8", {{"metric", "0", ""}});
534   testServerTimingHeader("metric;desc=description1;desc=description2",
535                          {{"metric", "0", "description1"}});
536 
537   // incomplete params
538   testServerTimingHeader("metric;dur;dur=123.4;desc=description",
539                          {{"metric", "0", "description"}});
540   testServerTimingHeader("metric;dur=;dur=123.4;desc=description",
541                          {{"metric", "0", "description"}});
542   testServerTimingHeader("metric;desc;desc=description;dur=123.4",
543                          {{"metric", "123.4", ""}});
544   testServerTimingHeader("metric;desc=;desc=description;dur=123.4",
545                          {{"metric", "123.4", ""}});
546 
547   // extraneous characters after param value as token
548   testServerTimingHeader("metric;desc=d1 d2;dur=123.4",
549                          {{"metric", "123.4", "d1"}});
550   testServerTimingHeader("metric1;desc=d1 d2,metric2",
551                          {{"metric1", "0", "d1"}, {"metric2", "0", ""}});
552 
553   // extraneous characters after param value as quoted-string
554   testServerTimingHeader("metric;desc=\"d1\" d2;dur=123.4",
555                          {{"metric", "123.4", "d1"}});
556   testServerTimingHeader("metric1;desc=\"d1\" d2,metric2",
557                          {{"metric1", "0", "d1"}, {"metric2", "0", ""}});
558 
559   // nonsense - extraneous characters after entry name token
560   testServerTimingHeader("metric==   \"\"foo;dur=123.4", {{"metric", "0", ""}});
561   testServerTimingHeader("metric1==   \"\"foo,metric2", {{"metric1", "0", ""}});
562 
563   // nonsense - extraneous characters after param name token
564   testServerTimingHeader("metric;dur foo=12", {{"metric", "0", ""}});
565   testServerTimingHeader("metric;foo dur=12", {{"metric", "0", ""}});
566 
567   // nonsense - return zero entries
568   testServerTimingHeader(" ", {});
569   testServerTimingHeader("=", {});
570   testServerTimingHeader("[", {});
571   testServerTimingHeader("]", {});
572   testServerTimingHeader(";", {});
573   testServerTimingHeader(",", {});
574   testServerTimingHeader("=;", {});
575   testServerTimingHeader(";=", {});
576   testServerTimingHeader("=,", {});
577   testServerTimingHeader(",=", {});
578   testServerTimingHeader(";,", {});
579   testServerTimingHeader(",;", {});
580   testServerTimingHeader("=;,", {});
581 
582   // TODO(cvazac) the following tests should actually NOT pass
583   // According to the definition of token/tchar
584   // (https://tools.ietf.org/html/rfc7230#appendix-B),
585   // HeaderFieldTokenizer.IsTokenCharacter is being too permissive for the
586   // following chars (decimal):
587   // 123 '{', 125 '}', and 127 (not defined)
588   testServerTimingHeader("{", {{"{", "0", ""}});
589   testServerTimingHeader("}", {{"}", "0", ""}});
590   testServerTimingHeader("{}", {{"{}", "0", ""}});
591   testServerTimingHeader("{\"foo\":\"bar\"},metric", {{"{", "0", ""}});
592 }
593 
TEST(HTTPParsersTest,ParseContentTypeOptionsTest)594 TEST(HTTPParsersTest, ParseContentTypeOptionsTest) {
595   struct {
596     const char* value;
597     ContentTypeOptionsDisposition result;
598   } cases[] = {{"nosniff", kContentTypeOptionsNosniff},
599                {"NOSNIFF", kContentTypeOptionsNosniff},
600                {"NOsniFF", kContentTypeOptionsNosniff},
601                {"nosniff, nosniff", kContentTypeOptionsNosniff},
602                {"nosniff, not-nosniff", kContentTypeOptionsNosniff},
603                {"nosniff, none", kContentTypeOptionsNosniff},
604                {" nosniff", kContentTypeOptionsNosniff},
605                {"NOSNIFF ", kContentTypeOptionsNosniff},
606                {" NOsniFF ", kContentTypeOptionsNosniff},
607                {" nosniff, nosniff", kContentTypeOptionsNosniff},
608                {"nosniff , not-nosniff", kContentTypeOptionsNosniff},
609                {" nosniff , none", kContentTypeOptionsNosniff},
610                {"", kContentTypeOptionsNone},
611                {",", kContentTypeOptionsNone},
612                {"none", kContentTypeOptionsNone},
613                {"none, nosniff", kContentTypeOptionsNone}};
614   for (const auto& test : cases) {
615     SCOPED_TRACE(test.value);
616     EXPECT_EQ(test.result, ParseContentTypeOptionsHeader(test.value));
617   }
618 }
619 
620 // -----------------------------------------------------------------------------
621 // Blink's HTTP parser is reusing:
622 // services/network/public/cpp/content_security_policy/, which is already tested
623 // and fuzzed.
624 // What needs to be tested is the basic conversion from/to blink types.
625 // -----------------------------------------------------------------------------
626 namespace {
627 WTF::Vector<network::mojom::blink::ContentSecurityPolicyPtr>
ParseContentSecurityPolicy(String http_headers)628 ParseContentSecurityPolicy(String http_headers) {
629   return std::move(ParseHeaders("HTTP/1.1 200 OK\r\n" + http_headers,
630                                 KURL("http://example.com"))
631                        ->content_security_policy);
632 }
633 }  // namespace
634 
TEST(HTTPParsersTest,ParseContentSecurityPolicyEmpty)635 TEST(HTTPParsersTest, ParseContentSecurityPolicyEmpty) {
636   auto csp = ParseContentSecurityPolicy("");
637   EXPECT_TRUE(csp.IsEmpty());
638 }
639 
TEST(HTTPParsersTest,ParseContentSecurityPolicyMultiple)640 TEST(HTTPParsersTest, ParseContentSecurityPolicyMultiple) {
641   auto csp = ParseContentSecurityPolicy(
642       "Content-Security-Policy: frame-ancestors a.com\r\n"
643       "Content-Security-Policy: frame-ancestors b.com\r\n");
644   ASSERT_EQ(2u, csp.size());
645   EXPECT_EQ("frame-ancestors a.com", csp[0]->header->header_value);
646   EXPECT_EQ("frame-ancestors b.com", csp[1]->header->header_value);
647 }
648 
TEST(HTTPParsersTest,ParseContentSecurityPolicyCoalesce)649 TEST(HTTPParsersTest, ParseContentSecurityPolicyCoalesce) {
650   auto csp = ParseContentSecurityPolicy(
651       "Content-Security-Policy:"
652       "frame-ancestors a.com, frame-ancestors b.com\r\n");
653   ASSERT_EQ(2u, csp.size());
654   EXPECT_EQ("frame-ancestors a.com", csp[0]->header->header_value);
655   EXPECT_EQ("frame-ancestors b.com", csp[1]->header->header_value);
656 }
657 
TEST(HTTPParsersTest,ParseContentSecurityPolicyHeader)658 TEST(HTTPParsersTest, ParseContentSecurityPolicyHeader) {
659   auto csp = ParseContentSecurityPolicy(
660       "Content-Security-Policy: frame-ancestors a.com\r\n"
661       "Content-Security-Policy-Report-Only: frame-ancestors b.com");
662   ASSERT_EQ(2u, csp.size());
663 
664   // Header source:
665   EXPECT_EQ(network::mojom::ContentSecurityPolicySource::kHTTP,
666             csp[0]->header->source);
667   EXPECT_EQ(network::mojom::ContentSecurityPolicySource::kHTTP,
668             csp[1]->header->source);
669 
670   // Header type:
671   EXPECT_EQ(network::mojom::ContentSecurityPolicyType::kEnforce,
672             csp[0]->header->type);
673   EXPECT_EQ(network::mojom::ContentSecurityPolicyType::kReport,
674             csp[1]->header->type);
675 
676   // Header value
677   EXPECT_EQ("frame-ancestors a.com", csp[0]->header->header_value);
678   EXPECT_EQ("frame-ancestors b.com", csp[1]->header->header_value);
679 }
680 
TEST(HTTPParsersTest,ParseContentSecurityPolicyDirectiveName)681 TEST(HTTPParsersTest, ParseContentSecurityPolicyDirectiveName) {
682   auto policies = ParseContentSecurityPolicy(
683       "Content-Security-Policy: frame-ancestors 'none'\r\n"
684       "Content-Security-Policy: sandbox allow-script\r\n"
685       "Content-Security-Policy: form-action 'none'\r\n"
686       "Content-Security-Policy: navigate-to 'none'\r\n"
687       "Content-Security-Policy: frame-src 'none'\r\n"
688       "Content-Security-Policy: child-src 'none'\r\n"
689       "Content-Security-Policy: script-src 'none'\r\n"
690       "Content-Security-Policy: default-src 'none'\r\n"
691       "Content-Security-Policy: upgrade-insecure-requests\r\n");
692   EXPECT_EQ(9u, policies.size());
693   // frame-ancestors
694   EXPECT_EQ(1u, policies[0]->directives.size());
695   // sandbox. TODO(https://crbug.com/1041376) Implement this.
696   EXPECT_EQ(0u, policies[1]->directives.size());
697   // form-action.
698   EXPECT_EQ(1u, policies[2]->directives.size());
699   // navigate-to.
700   EXPECT_EQ(1u, policies[3]->directives.size());
701   // frame-src.
702   EXPECT_EQ(1u, policies[4]->directives.size());
703   // child-src.
704   EXPECT_EQ(1u, policies[5]->directives.size());
705   // script-src.
706   EXPECT_EQ(1u, policies[6]->directives.size());
707   // default-src.
708   EXPECT_EQ(1u, policies[7]->directives.size());
709   // upgrade-insecure-policies.
710   EXPECT_EQ(true, policies[8]->upgrade_insecure_requests);
711 }
712 
TEST(HTTPParsersTest,ParseContentSecurityPolicyReportTo)713 TEST(HTTPParsersTest, ParseContentSecurityPolicyReportTo) {
714   auto policies =
715       ParseContentSecurityPolicy("Content-Security-Policy: report-to a b\r\n");
716   EXPECT_TRUE(policies[0]->use_reporting_api);
717   // The specification https://w3c.github.io/webappsec-csp/#directive-report-to
718   // only allows for one endpoints to be defined. The other ones are ignored.
719   ASSERT_EQ(1u, policies[0]->report_endpoints.size());
720   EXPECT_EQ("a", policies[0]->report_endpoints[0]);
721 }
722 
TEST(HTTPParsersTest,ParseContentSecurityPolicyReportUri)723 TEST(HTTPParsersTest, ParseContentSecurityPolicyReportUri) {
724   auto policies = ParseContentSecurityPolicy(
725       "Content-Security-Policy: report-uri ./report.py\r\n");
726   EXPECT_FALSE(policies[0]->use_reporting_api);
727   ASSERT_EQ(1u, policies[0]->report_endpoints.size());
728   EXPECT_EQ("http://example.com/report.py", policies[0]->report_endpoints[0]);
729 }
730 
TEST(HTTPParsersTest,ParseContentSecurityPolicySourceBasic)731 TEST(HTTPParsersTest, ParseContentSecurityPolicySourceBasic) {
732   auto frame_ancestors = network::mojom::CSPDirectiveName::FrameAncestors;
733   auto policies = ParseContentSecurityPolicy(
734       "Content-Security-Policy: frame-ancestors 'none'\r\n"
735       "Content-Security-Policy: frame-ancestors *\r\n"
736       "Content-Security-Policy: frame-ancestors 'self'\r\n"
737       "Content-Security-Policy: frame-ancestors http://a.com:22/path\r\n"
738       "Content-Security-Policy: frame-ancestors a.com:*\r\n"
739       "Content-Security-Policy: frame-ancestors */report.py\r\n");
740   // 'none'
741   {
742     auto source_list = policies[0]->directives.Take(frame_ancestors);
743     EXPECT_EQ(0u, source_list->sources.size());
744     EXPECT_FALSE(source_list->allow_self);
745     EXPECT_FALSE(source_list->allow_star);
746     EXPECT_FALSE(source_list->allow_response_redirects);
747   }
748 
749   // *
750   {
751     auto source_list = policies[1]->directives.Take(frame_ancestors);
752     EXPECT_EQ(0u, source_list->sources.size());
753     EXPECT_FALSE(source_list->allow_self);
754     EXPECT_TRUE(source_list->allow_star);
755     EXPECT_FALSE(source_list->allow_response_redirects);
756   }
757 
758   // 'self'
759   {
760     auto source_list = policies[2]->directives.Take(frame_ancestors);
761     EXPECT_EQ(0u, source_list->sources.size());
762     EXPECT_TRUE(source_list->allow_self);
763     EXPECT_FALSE(source_list->allow_star);
764     EXPECT_FALSE(source_list->allow_response_redirects);
765   }
766 
767   // http://a.com:22/path
768   {
769     auto source_list = policies[3]->directives.Take(frame_ancestors);
770     EXPECT_FALSE(source_list->allow_self);
771     EXPECT_FALSE(source_list->allow_star);
772     EXPECT_FALSE(source_list->allow_response_redirects);
773     EXPECT_EQ(1u, source_list->sources.size());
774     auto& source = source_list->sources[0];
775     EXPECT_EQ("http", source->scheme);
776     EXPECT_EQ("a.com", source->host);
777     EXPECT_EQ("/path", source->path);
778     EXPECT_FALSE(source->is_host_wildcard);
779     EXPECT_FALSE(source->is_port_wildcard);
780   }
781 
782   // a.com:*
783   {
784     auto source_list = policies[4]->directives.Take(frame_ancestors);
785     EXPECT_FALSE(source_list->allow_self);
786     EXPECT_FALSE(source_list->allow_star);
787     EXPECT_FALSE(source_list->allow_response_redirects);
788     EXPECT_EQ(1u, source_list->sources.size());
789     auto& source = source_list->sources[0];
790     EXPECT_EQ("", source->scheme);
791     EXPECT_EQ("a.com", source->host);
792     EXPECT_EQ("", source->path);
793     EXPECT_FALSE(source->is_host_wildcard);
794     EXPECT_TRUE(source->is_port_wildcard);
795   }
796 
797   // frame-ancestors */report.py
798   {
799     auto source_list = policies[5]->directives.Take(frame_ancestors);
800     EXPECT_FALSE(source_list->allow_self);
801     EXPECT_FALSE(source_list->allow_star);
802     EXPECT_FALSE(source_list->allow_response_redirects);
803     EXPECT_EQ(1u, source_list->sources.size());
804     auto& source = source_list->sources[0];
805     EXPECT_EQ("", source->scheme);
806     EXPECT_EQ("", source->host);
807     EXPECT_EQ(-1, source->port);
808     EXPECT_EQ("/report.py", source->path);
809     EXPECT_TRUE(source->is_host_wildcard);
810     EXPECT_FALSE(source->is_port_wildcard);
811   }
812 }
813 
814 }  // namespace blink
815