• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/http/http_response_headers.h"
6 
7 #include <stdint.h>
8 
9 #include <iostream>
10 #include <limits>
11 #include <memory>
12 #include <unordered_set>
13 
14 #include "base/pickle.h"
15 #include "base/ranges/algorithm.h"
16 #include "base/time/time.h"
17 #include "base/values.h"
18 #include "net/base/cronet_buildflags.h"
19 #include "net/base/tracing.h"
20 #include "net/http/http_byte_range.h"
21 #include "net/http/http_util.h"
22 #include "net/log/net_log_capture_mode.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 
25 #if !BUILDFLAG(CRONET_BUILD)
26 #include "third_party/perfetto/include/perfetto/test/traced_value_test_support.h"
27 #endif
28 
29 namespace net {
30 
31 namespace {
32 
33 struct TestData {
34   const char* raw_headers;
35   const char* expected_headers;
36   HttpVersion expected_version;
37   int expected_response_code;
38   const char* expected_status_text;
39 };
40 
41 class HttpResponseHeadersTest : public testing::Test {
42 };
43 
44 // Transform "normal"-looking headers (\n-separated) to the appropriate
45 // input format for ParseRawHeaders (\0-separated).
HeadersToRaw(std::string * headers)46 void HeadersToRaw(std::string* headers) {
47   std::replace(headers->begin(), headers->end(), '\n', '\0');
48   if (!headers->empty())
49     *headers += '\0';
50 }
51 
52 class HttpResponseHeadersCacheControlTest : public HttpResponseHeadersTest {
53  protected:
54   // Make tests less verbose.
55   typedef base::TimeDelta TimeDelta;
56 
57   // Initilise the headers() value with a Cache-Control header set to
58   // |cache_control|. |cache_control| is copied and so can safely be a
59   // temporary.
InitializeHeadersWithCacheControl(const char * cache_control)60   void InitializeHeadersWithCacheControl(const char* cache_control) {
61     std::string raw_headers("HTTP/1.1 200 OK\n");
62     raw_headers += "Cache-Control: ";
63     raw_headers += cache_control;
64     raw_headers += "\n";
65     HeadersToRaw(&raw_headers);
66     headers_ = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
67   }
68 
headers()69   const scoped_refptr<HttpResponseHeaders>& headers() { return headers_; }
70 
71   // Return a pointer to a TimeDelta object. For use when the value doesn't
72   // matter.
TimeDeltaPointer()73   TimeDelta* TimeDeltaPointer() { return &delta_; }
74 
75   // Get the max-age value. This should only be used in tests where a valid
76   // max-age parameter is expected to be present.
GetMaxAgeValue()77   TimeDelta GetMaxAgeValue() {
78     DCHECK(headers_.get()) << "Call InitializeHeadersWithCacheControl() first";
79     TimeDelta max_age_value;
80     EXPECT_TRUE(headers()->GetMaxAgeValue(&max_age_value));
81     return max_age_value;
82   }
83 
84   // Get the stale-while-revalidate value. This should only be used in tests
85   // where a valid max-age parameter is expected to be present.
GetStaleWhileRevalidateValue()86   TimeDelta GetStaleWhileRevalidateValue() {
87     DCHECK(headers_.get()) << "Call InitializeHeadersWithCacheControl() first";
88     TimeDelta stale_while_revalidate_value;
89     EXPECT_TRUE(
90         headers()->GetStaleWhileRevalidateValue(&stale_while_revalidate_value));
91     return stale_while_revalidate_value;
92   }
93 
94  private:
95   scoped_refptr<HttpResponseHeaders> headers_;
96   TimeDelta delta_;
97 };
98 
99 class CommonHttpResponseHeadersTest
100     : public HttpResponseHeadersTest,
101       public ::testing::WithParamInterface<TestData> {
102 };
103 
104 // Returns a simple text serialization of the given
105 // |HttpResponseHeaders|. This is used by tests to verify that an
106 // |HttpResponseHeaders| matches an expectation string.
107 //
108 //  * One line per header, written as:
109 //        HEADER_NAME: HEADER_VALUE\n
110 //  * The original case of header names is preserved.
111 //  * Whitespace around head names/values is stripped.
112 //  * Repeated headers are not aggregated.
113 //  * Headers are listed in their original order.
ToSimpleString(const scoped_refptr<HttpResponseHeaders> & parsed)114 std::string ToSimpleString(const scoped_refptr<HttpResponseHeaders>& parsed) {
115   std::string result = parsed->GetStatusLine() + "\n";
116 
117   size_t iter = 0;
118   std::string name;
119   std::string value;
120   while (parsed->EnumerateHeaderLines(&iter, &name, &value)) {
121     std::string new_line = name + ": " + value + "\n";
122 
123     // Verify that |name| and |value| do not contain ':' or '\n' (if they did
124     // it would make this serialized format ambiguous).
125     if (base::ranges::count(new_line, '\n') != 1 ||
126         base::ranges::count(new_line, ':') != 1) {
127       ADD_FAILURE() << "Unexpected characters in the header name or value: "
128                     << new_line;
129       return result;
130     }
131 
132     result += new_line;
133   }
134 
135   return result;
136 }
137 
TEST_P(CommonHttpResponseHeadersTest,TestCommon)138 TEST_P(CommonHttpResponseHeadersTest, TestCommon) {
139   const TestData test = GetParam();
140 
141   std::string raw_headers(test.raw_headers);
142   HeadersToRaw(&raw_headers);
143   std::string expected_headers(test.expected_headers);
144 
145   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(raw_headers);
146   std::string headers = ToSimpleString(parsed);
147 
148   // Transform to readable output format (so it's easier to see diffs).
149   std::replace(headers.begin(), headers.end(), ' ', '_');
150   std::replace(headers.begin(), headers.end(), '\n', '\\');
151   std::replace(expected_headers.begin(), expected_headers.end(), ' ', '_');
152   std::replace(expected_headers.begin(), expected_headers.end(), '\n', '\\');
153 
154   EXPECT_EQ(expected_headers, headers);
155 
156   EXPECT_TRUE(test.expected_version == parsed->GetHttpVersion());
157   EXPECT_EQ(test.expected_response_code, parsed->response_code());
158   EXPECT_EQ(test.expected_status_text, parsed->GetStatusText());
159 }
160 
161 TestData response_headers_tests[] = {
162     {// Normalize whitespace.
163      "HTTP/1.1    202   Accepted  \n"
164      "Content-TYPE  : text/html; charset=utf-8  \n"
165      "Set-Cookie: a \n"
166      "Set-Cookie:   b \n",
167 
168      "HTTP/1.1 202 Accepted\n"
169      "Content-TYPE: text/html; charset=utf-8\n"
170      "Set-Cookie: a\n"
171      "Set-Cookie: b\n",
172 
173      HttpVersion(1, 1), 202, "Accepted"},
174     {// Normalize leading whitespace.
175      "HTTP/1.1    202   Accepted  \n"
176      // Starts with space -- will be skipped as invalid.
177      "  Content-TYPE  : text/html; charset=utf-8  \n"
178      "Set-Cookie: a \n"
179      "Set-Cookie:   b \n",
180 
181      "HTTP/1.1 202 Accepted\n"
182      "Set-Cookie: a\n"
183      "Set-Cookie: b\n",
184 
185      HttpVersion(1, 1), 202, "Accepted"},
186     {// Keep whitespace within status text.
187      "HTTP/1.0 404 Not   found  \n",
188 
189      "HTTP/1.0 404 Not   found\n",
190 
191      HttpVersion(1, 0), 404, "Not   found"},
192     {// Normalize blank headers.
193      "HTTP/1.1 200 OK\n"
194      "Header1 :          \n"
195      "Header2: \n"
196      "Header3:\n"
197      "Header4\n"
198      "Header5    :\n",
199 
200      "HTTP/1.1 200 OK\n"
201      "Header1: \n"
202      "Header2: \n"
203      "Header3: \n"
204      "Header5: \n",
205 
206      HttpVersion(1, 1), 200, "OK"},
207     {// Don't believe the http/0.9 version if there are headers!
208      "hTtP/0.9 201\n"
209      "Content-TYPE: text/html; charset=utf-8\n",
210 
211      "HTTP/1.0 201\n"
212      "Content-TYPE: text/html; charset=utf-8\n",
213 
214      HttpVersion(1, 0), 201, ""},
215     {// Accept the HTTP/0.9 version number if there are no headers.
216      // This is how HTTP/0.9 responses get constructed from
217      // HttpNetworkTransaction.
218      "hTtP/0.9 200 OK\n",
219 
220      "HTTP/0.9 200 OK\n",
221 
222      HttpVersion(0, 9), 200, "OK"},
223     {// Do not add missing status text.
224      "HTTP/1.1 201\n"
225      "Content-TYPE: text/html; charset=utf-8\n",
226 
227      "HTTP/1.1 201\n"
228      "Content-TYPE: text/html; charset=utf-8\n",
229 
230      HttpVersion(1, 1), 201, ""},
231     {// Normalize bad status line.
232      "SCREWED_UP_STATUS_LINE\n"
233      "Content-TYPE: text/html; charset=utf-8\n",
234 
235      "HTTP/1.0 200 OK\n"
236      "Content-TYPE: text/html; charset=utf-8\n",
237 
238      HttpVersion(1, 0), 200, "OK"},
239     {// Normalize bad status line.
240      "Foo bar.",
241 
242      "HTTP/1.0 200\n",
243 
244      HttpVersion(1, 0), 200, ""},
245     {// Normalize invalid status code.
246      "HTTP/1.1 -1  Unknown\n",
247 
248      "HTTP/1.1 200\n",
249 
250      HttpVersion(1, 1), 200, ""},
251     {// Normalize empty header.
252      "",
253 
254      "HTTP/1.0 200 OK\n",
255 
256      HttpVersion(1, 0), 200, "OK"},
257     {// Normalize headers that start with a colon.
258      "HTTP/1.1    202   Accepted  \n"
259      "foo: bar\n"
260      ": a \n"
261      " : b\n"
262      "baz: blat \n",
263 
264      "HTTP/1.1 202 Accepted\n"
265      "foo: bar\n"
266      "baz: blat\n",
267 
268      HttpVersion(1, 1), 202, "Accepted"},
269     {// Normalize headers that end with a colon.
270      "HTTP/1.1    202   Accepted  \n"
271      "foo:   \n"
272      "bar:\n"
273      "baz: blat \n"
274      "zip:\n",
275 
276      "HTTP/1.1 202 Accepted\n"
277      "foo: \n"
278      "bar: \n"
279      "baz: blat\n"
280      "zip: \n",
281 
282      HttpVersion(1, 1), 202, "Accepted"},
283     {// Normalize whitespace headers.
284      "\n   \n",
285 
286      "HTTP/1.0 200 OK\n",
287 
288      HttpVersion(1, 0), 200, "OK"},
289     {// Has multiple Set-Cookie headers.
290      "HTTP/1.1 200 OK\n"
291      "Set-Cookie: x=1\n"
292      "Set-Cookie: y=2\n",
293 
294      "HTTP/1.1 200 OK\n"
295      "Set-Cookie: x=1\n"
296      "Set-Cookie: y=2\n",
297 
298      HttpVersion(1, 1), 200, "OK"},
299     {// Has multiple cache-control headers.
300      "HTTP/1.1 200 OK\n"
301      "Cache-control: private\n"
302      "cache-Control: no-store\n",
303 
304      "HTTP/1.1 200 OK\n"
305      "Cache-control: private\n"
306      "cache-Control: no-store\n",
307 
308      HttpVersion(1, 1), 200, "OK"},
309 };
310 
311 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
312                          CommonHttpResponseHeadersTest,
313                          testing::ValuesIn(response_headers_tests));
314 
315 struct PersistData {
316   HttpResponseHeaders::PersistOptions options;
317   const char* raw_headers;
318   const char* expected_headers;
319 };
320 
321 class PersistenceTest
322     : public HttpResponseHeadersTest,
323       public ::testing::WithParamInterface<PersistData> {
324 };
325 
TEST_P(PersistenceTest,Persist)326 TEST_P(PersistenceTest, Persist) {
327   const PersistData test = GetParam();
328 
329   std::string headers = test.raw_headers;
330   HeadersToRaw(&headers);
331   auto parsed1 = base::MakeRefCounted<HttpResponseHeaders>(headers);
332 
333   base::Pickle pickle;
334   parsed1->Persist(&pickle, test.options);
335 
336   base::PickleIterator iter(pickle);
337   auto parsed2 = base::MakeRefCounted<HttpResponseHeaders>(&iter);
338 
339   EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed2));
340 }
341 
342 const struct PersistData persistence_tests[] = {
343     {HttpResponseHeaders::PERSIST_ALL,
344      "HTTP/1.1 200 OK\n"
345      "Cache-control:private\n"
346      "cache-Control:no-store\n",
347 
348      "HTTP/1.1 200 OK\n"
349      "Cache-control: private\n"
350      "cache-Control: no-store\n"},
351     {HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
352      "HTTP/1.1 200 OK\n"
353      "connection: keep-alive\n"
354      "server: blah\n",
355 
356      "HTTP/1.1 200 OK\n"
357      "server: blah\n"},
358     {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
359          HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
360      "HTTP/1.1 200 OK\n"
361      "fOo: 1\n"
362      "Foo: 2\n"
363      "Transfer-Encoding: chunked\n"
364      "CoNnection: keep-alive\n"
365      "cache-control: private, no-cache=\"foo\"\n",
366 
367      "HTTP/1.1 200 OK\n"
368      "cache-control: private, no-cache=\"foo\"\n"},
369     {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
370      "HTTP/1.1 200 OK\n"
371      "Foo: 2\n"
372      "Cache-Control: private,no-cache=\"foo, bar\"\n"
373      "bar",
374 
375      "HTTP/1.1 200 OK\n"
376      "Cache-Control: private,no-cache=\"foo, bar\"\n"},
377     // Ignore bogus no-cache value.
378     {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
379      "HTTP/1.1 200 OK\n"
380      "Foo: 2\n"
381      "Cache-Control: private,no-cache=foo\n",
382 
383      "HTTP/1.1 200 OK\n"
384      "Foo: 2\n"
385      "Cache-Control: private,no-cache=foo\n"},
386     // Ignore bogus no-cache value.
387     {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
388      "HTTP/1.1 200 OK\n"
389      "Foo: 2\n"
390      "Cache-Control: private, no-cache=\n",
391 
392      "HTTP/1.1 200 OK\n"
393      "Foo: 2\n"
394      "Cache-Control: private, no-cache=\n"},
395     // Ignore empty no-cache value.
396     {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
397      "HTTP/1.1 200 OK\n"
398      "Foo: 2\n"
399      "Cache-Control: private, no-cache=\"\"\n",
400 
401      "HTTP/1.1 200 OK\n"
402      "Foo: 2\n"
403      "Cache-Control: private, no-cache=\"\"\n"},
404     // Ignore wrong quotes no-cache value.
405     {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
406      "HTTP/1.1 200 OK\n"
407      "Foo: 2\n"
408      "Cache-Control: private, no-cache=\'foo\'\n",
409 
410      "HTTP/1.1 200 OK\n"
411      "Foo: 2\n"
412      "Cache-Control: private, no-cache=\'foo\'\n"},
413     // Ignore unterminated quotes no-cache value.
414     {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
415      "HTTP/1.1 200 OK\n"
416      "Foo: 2\n"
417      "Cache-Control: private, no-cache=\"foo\n",
418 
419      "HTTP/1.1 200 OK\n"
420      "Foo: 2\n"
421      "Cache-Control: private, no-cache=\"foo\n"},
422     // Accept sloppy LWS.
423     {HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
424      "HTTP/1.1 200 OK\n"
425      "Foo: 2\n"
426      "Cache-Control: private, no-cache=\" foo\t, bar\"\n",
427 
428      "HTTP/1.1 200 OK\n"
429      "Cache-Control: private, no-cache=\" foo\t, bar\"\n"},
430     // Header name appears twice, separated by another header.
431     {HttpResponseHeaders::PERSIST_ALL,
432      "HTTP/1.1 200 OK\n"
433      "Foo: 1\n"
434      "Bar: 2\n"
435      "Foo: 3\n",
436 
437      "HTTP/1.1 200 OK\n"
438      "Foo: 1\n"
439      "Bar: 2\n"
440      "Foo: 3\n"},
441     // Header name appears twice, separated by another header (type 2).
442     {HttpResponseHeaders::PERSIST_ALL,
443      "HTTP/1.1 200 OK\n"
444      "Foo: 1, 3\n"
445      "Bar: 2\n"
446      "Foo: 4\n",
447 
448      "HTTP/1.1 200 OK\n"
449      "Foo: 1, 3\n"
450      "Bar: 2\n"
451      "Foo: 4\n"},
452     // Test filtering of cookie headers.
453     {HttpResponseHeaders::PERSIST_SANS_COOKIES,
454      "HTTP/1.1 200 OK\n"
455      "Set-Cookie: foo=bar; httponly\n"
456      "Set-Cookie: bar=foo\n"
457      "Bar: 1\n"
458      "Set-Cookie2: bar2=foo2\n",
459 
460      "HTTP/1.1 200 OK\n"
461      "Bar: 1\n"},
462     {HttpResponseHeaders::PERSIST_SANS_COOKIES,
463      "HTTP/1.1 200 OK\n"
464      "Set-Cookie: foo=bar\n"
465      "Foo: 2\n"
466      "Clear-Site-Data: { \"types\" : [ \"cookies\" ] }\n"
467      "Bar: 3\n",
468 
469      "HTTP/1.1 200 OK\n"
470      "Foo: 2\n"
471      "Bar: 3\n"},
472     // Test LWS at the end of a header.
473     {HttpResponseHeaders::PERSIST_ALL,
474      "HTTP/1.1 200 OK\n"
475      "Content-Length: 450   \n"
476      "Content-Encoding: gzip\n",
477 
478      "HTTP/1.1 200 OK\n"
479      "Content-Length: 450\n"
480      "Content-Encoding: gzip\n"},
481     // Test LWS at the end of a header.
482     {HttpResponseHeaders::PERSIST_RAW,
483      "HTTP/1.1 200 OK\n"
484      "Content-Length: 450   \n"
485      "Content-Encoding: gzip\n",
486 
487      "HTTP/1.1 200 OK\n"
488      "Content-Length: 450\n"
489      "Content-Encoding: gzip\n"},
490     // Test filtering of transport security state headers.
491     {HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE,
492      "HTTP/1.1 200 OK\n"
493      "Strict-Transport-Security: max-age=1576800\n"
494      "Bar: 1\n",
495 
496      "HTTP/1.1 200 OK\n"
497      "Bar: 1\n"},
498 };
499 
500 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
501                          PersistenceTest,
502                          testing::ValuesIn(persistence_tests));
503 
TEST(HttpResponseHeadersTest,EnumerateHeader_Coalesced)504 TEST(HttpResponseHeadersTest, EnumerateHeader_Coalesced) {
505   // Ensure that commas in quoted strings are not regarded as value separators.
506   // Ensure that whitespace following a value is trimmed properly.
507   std::string headers =
508       "HTTP/1.1 200 OK\n"
509       "Cache-control:,,private , no-cache=\"set-cookie,server\",\n"
510       "cache-Control: no-store\n"
511       "cache-Control:\n";
512   HeadersToRaw(&headers);
513   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
514 
515   size_t iter = 0;
516   std::string value;
517   ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
518   EXPECT_EQ("", value);
519   ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
520   EXPECT_EQ("", value);
521   ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
522   EXPECT_EQ("private", value);
523   ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
524   EXPECT_EQ("no-cache=\"set-cookie,server\"", value);
525   ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
526   EXPECT_EQ("", value);
527   ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
528   EXPECT_EQ("no-store", value);
529   ASSERT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
530   EXPECT_EQ("", value);
531   EXPECT_FALSE(parsed->EnumerateHeader(&iter, "cache-control", &value));
532 }
533 
TEST(HttpResponseHeadersTest,EnumerateHeader_Challenge)534 TEST(HttpResponseHeadersTest, EnumerateHeader_Challenge) {
535   // Even though WWW-Authenticate has commas, it should not be treated as
536   // coalesced values.
537   std::string headers =
538       "HTTP/1.1 401 OK\n"
539       "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n"
540       "WWW-Authenticate:Basic realm=quatar\n";
541   HeadersToRaw(&headers);
542   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
543 
544   size_t iter = 0;
545   std::string value;
546   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
547   EXPECT_EQ("Digest realm=foobar, nonce=x, domain=y", value);
548   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
549   EXPECT_EQ("Basic realm=quatar", value);
550   EXPECT_FALSE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
551 }
552 
TEST(HttpResponseHeadersTest,EnumerateHeader_DateValued)553 TEST(HttpResponseHeadersTest, EnumerateHeader_DateValued) {
554   // The comma in a date valued header should not be treated as a
555   // field-value separator.
556   std::string headers =
557       "HTTP/1.1 200 OK\n"
558       "Date: Tue, 07 Aug 2007 23:10:55 GMT\n"
559       "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n";
560   HeadersToRaw(&headers);
561   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
562 
563   std::string value;
564   EXPECT_TRUE(parsed->EnumerateHeader(nullptr, "date", &value));
565   EXPECT_EQ("Tue, 07 Aug 2007 23:10:55 GMT", value);
566   EXPECT_TRUE(parsed->EnumerateHeader(nullptr, "last-modified", &value));
567   EXPECT_EQ("Wed, 01 Aug 2007 23:23:45 GMT", value);
568 }
569 
TEST(HttpResponseHeadersTest,DefaultDateToGMT)570 TEST(HttpResponseHeadersTest, DefaultDateToGMT) {
571   // Verify we make the best interpretation when parsing dates that incorrectly
572   // do not end in "GMT" as RFC2616 requires.
573   std::string headers =
574       "HTTP/1.1 200 OK\n"
575       "Date: Tue, 07 Aug 2007 23:10:55\n"
576       "Last-Modified: Tue, 07 Aug 2007 19:10:55 EDT\n"
577       "Expires: Tue, 07 Aug 2007 23:10:55 UTC\n";
578   HeadersToRaw(&headers);
579   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
580   base::Time expected_value;
581   ASSERT_TRUE(base::Time::FromString("Tue, 07 Aug 2007 23:10:55 GMT",
582                                      &expected_value));
583 
584   base::Time value;
585   // When the timezone is missing, GMT is a good guess as its what RFC2616
586   // requires.
587   EXPECT_TRUE(parsed->GetDateValue(&value));
588   EXPECT_EQ(expected_value, value);
589   // If GMT is missing but an RFC822-conforming one is present, use that.
590   EXPECT_TRUE(parsed->GetLastModifiedValue(&value));
591   EXPECT_EQ(expected_value, value);
592   // If an unknown timezone is present, treat like a missing timezone and
593   // default to GMT.  The only example of a web server not specifying "GMT"
594   // used "UTC" which is equivalent to GMT.
595   if (parsed->GetExpiresValue(&value))
596     EXPECT_EQ(expected_value, value);
597 }
598 
TEST(HttpResponseHeadersTest,GetAgeValue10)599 TEST(HttpResponseHeadersTest, GetAgeValue10) {
600   std::string headers =
601       "HTTP/1.1 200 OK\n"
602       "Age: 10\n";
603   HeadersToRaw(&headers);
604   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
605   base::TimeDelta age;
606   ASSERT_TRUE(parsed->GetAgeValue(&age));
607   EXPECT_EQ(10, age.InSeconds());
608 }
609 
TEST(HttpResponseHeadersTest,GetAgeValue0)610 TEST(HttpResponseHeadersTest, GetAgeValue0) {
611   std::string headers =
612       "HTTP/1.1 200 OK\n"
613       "Age: 0\n";
614   HeadersToRaw(&headers);
615   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
616   base::TimeDelta age;
617   ASSERT_TRUE(parsed->GetAgeValue(&age));
618   EXPECT_EQ(0, age.InSeconds());
619 }
620 
TEST(HttpResponseHeadersTest,GetAgeValueBogus)621 TEST(HttpResponseHeadersTest, GetAgeValueBogus) {
622   std::string headers =
623       "HTTP/1.1 200 OK\n"
624       "Age: donkey\n";
625   HeadersToRaw(&headers);
626   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
627   base::TimeDelta age;
628   ASSERT_FALSE(parsed->GetAgeValue(&age));
629 }
630 
TEST(HttpResponseHeadersTest,GetAgeValueNegative)631 TEST(HttpResponseHeadersTest, GetAgeValueNegative) {
632   std::string headers =
633       "HTTP/1.1 200 OK\n"
634       "Age: -10\n";
635   HeadersToRaw(&headers);
636   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
637   base::TimeDelta age;
638   ASSERT_FALSE(parsed->GetAgeValue(&age));
639 }
640 
TEST(HttpResponseHeadersTest,GetAgeValueLeadingPlus)641 TEST(HttpResponseHeadersTest, GetAgeValueLeadingPlus) {
642   std::string headers =
643       "HTTP/1.1 200 OK\n"
644       "Age: +10\n";
645   HeadersToRaw(&headers);
646   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
647   base::TimeDelta age;
648   ASSERT_FALSE(parsed->GetAgeValue(&age));
649 }
650 
TEST(HttpResponseHeadersTest,GetAgeValueOverflow)651 TEST(HttpResponseHeadersTest, GetAgeValueOverflow) {
652   std::string headers =
653       "HTTP/1.1 200 OK\n"
654       "Age: 999999999999999999999999999999999999999999\n";
655   HeadersToRaw(&headers);
656   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
657   base::TimeDelta age;
658   ASSERT_TRUE(parsed->GetAgeValue(&age));
659 
660   // Should have saturated to 2^32 - 1.
661   EXPECT_EQ(static_cast<int64_t>(0xFFFFFFFFL), age.InSeconds());
662 }
663 
664 struct ContentTypeTestData {
665   const std::string raw_headers;
666   const std::string mime_type;
667   const bool has_mimetype;
668   const std::string charset;
669   const bool has_charset;
670   const std::string all_content_type;
671 };
672 
673 class ContentTypeTest
674     : public HttpResponseHeadersTest,
675       public ::testing::WithParamInterface<ContentTypeTestData> {
676 };
677 
TEST_P(ContentTypeTest,GetMimeType)678 TEST_P(ContentTypeTest, GetMimeType) {
679   const ContentTypeTestData test = GetParam();
680 
681   std::string headers(test.raw_headers);
682   HeadersToRaw(&headers);
683   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
684 
685   std::string value;
686   EXPECT_EQ(test.has_mimetype, parsed->GetMimeType(&value));
687   EXPECT_EQ(test.mime_type, value);
688   value.clear();
689   EXPECT_EQ(test.has_charset, parsed->GetCharset(&value));
690   EXPECT_EQ(test.charset, value);
691   EXPECT_TRUE(parsed->GetNormalizedHeader("content-type", &value));
692   EXPECT_EQ(test.all_content_type, value);
693 }
694 
695 // clang-format off
696 const ContentTypeTestData mimetype_tests[] = {
697   { "HTTP/1.1 200 OK\n"
698     "Content-type: text/html\n",
699     "text/html", true,
700     "", false,
701     "text/html" },
702   // Multiple content-type headers should give us the last one.
703   { "HTTP/1.1 200 OK\n"
704     "Content-type: text/html\n"
705     "Content-type: text/html\n",
706     "text/html", true,
707     "", false,
708     "text/html, text/html" },
709   { "HTTP/1.1 200 OK\n"
710     "Content-type: text/plain\n"
711     "Content-type: text/html\n"
712     "Content-type: text/plain\n"
713     "Content-type: text/html\n",
714     "text/html", true,
715     "", false,
716     "text/plain, text/html, text/plain, text/html" },
717   // Test charset parsing.
718   { "HTTP/1.1 200 OK\n"
719     "Content-type: text/html\n"
720     "Content-type: text/html; charset=ISO-8859-1\n",
721     "text/html", true,
722     "iso-8859-1", true,
723     "text/html, text/html; charset=ISO-8859-1" },
724   // Test charset in double quotes.
725   { "HTTP/1.1 200 OK\n"
726     "Content-type: text/html\n"
727     "Content-type: text/html; charset=\"ISO-8859-1\"\n",
728     "text/html", true,
729     "iso-8859-1", true,
730     "text/html, text/html; charset=\"ISO-8859-1\"" },
731   // If there are multiple matching content-type headers, we carry
732   // over the charset value.
733   { "HTTP/1.1 200 OK\n"
734     "Content-type: text/html;charset=utf-8\n"
735     "Content-type: text/html\n",
736     "text/html", true,
737     "utf-8", true,
738     "text/html;charset=utf-8, text/html" },
739   // Regression test for https://crbug.com/772350:
740   // Single quotes are not delimiters but must be treated as part of charset.
741   { "HTTP/1.1 200 OK\n"
742     "Content-type: text/html;charset='utf-8'\n"
743     "Content-type: text/html\n",
744     "text/html", true,
745     "'utf-8'", true,
746     "text/html;charset='utf-8', text/html" },
747   // First charset wins if matching content-type.
748   { "HTTP/1.1 200 OK\n"
749     "Content-type: text/html;charset=utf-8\n"
750     "Content-type: text/html;charset=iso-8859-1\n",
751     "text/html", true,
752     "iso-8859-1", true,
753     "text/html;charset=utf-8, text/html;charset=iso-8859-1" },
754   // Charset is ignored if the content types change.
755   { "HTTP/1.1 200 OK\n"
756     "Content-type: text/plain;charset=utf-8\n"
757     "Content-type: text/html\n",
758     "text/html", true,
759     "", false,
760     "text/plain;charset=utf-8, text/html" },
761   // Empty content-type.
762   { "HTTP/1.1 200 OK\n"
763     "Content-type: \n",
764     "", false,
765     "", false,
766     "" },
767   // Emtpy charset.
768   { "HTTP/1.1 200 OK\n"
769     "Content-type: text/html;charset=\n",
770     "text/html", true,
771     "", false,
772     "text/html;charset=" },
773   // Multiple charsets, first one wins.
774   { "HTTP/1.1 200 OK\n"
775     "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n",
776     "text/html", true,
777     "utf-8", true,
778     "text/html;charset=utf-8; charset=iso-8859-1" },
779   // Multiple params.
780   { "HTTP/1.1 200 OK\n"
781     "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n",
782     "text/html", true,
783     "iso-8859-1", true,
784     "text/html; foo=utf-8; charset=iso-8859-1" },
785   { "HTTP/1.1 200 OK\n"
786     "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n",
787     "text/html", true,
788     "utf-8", true,
789     "text/html ; charset=utf-8 ; bar=iso-8859-1" },
790   // Comma embeded in quotes.
791   { "HTTP/1.1 200 OK\n"
792     "Content-type: text/html ; charset=\"utf-8,text/plain\" ;\n",
793     "text/html", true,
794     "utf-8,text/plain", true,
795     "text/html ; charset=\"utf-8,text/plain\" ;" },
796   // Charset with leading spaces.
797   { "HTTP/1.1 200 OK\n"
798     "Content-type: text/html ; charset= \"utf-8\" ;\n",
799     "text/html", true,
800     "utf-8", true,
801     "text/html ; charset= \"utf-8\" ;" },
802   // Media type comments in mime-type.
803   { "HTTP/1.1 200 OK\n"
804     "Content-type: text/html (html)\n",
805     "text/html", true,
806     "", false,
807    "text/html (html)" },
808   // Incomplete charset= param.
809   { "HTTP/1.1 200 OK\n"
810     "Content-type: text/html; char=\n",
811     "text/html", true,
812     "", false,
813     "text/html; char=" },
814   // Invalid media type: no slash.
815   { "HTTP/1.1 200 OK\n"
816     "Content-type: texthtml\n",
817     "", false,
818     "", false,
819     "texthtml" },
820   // Invalid media type: "*/*".
821   { "HTTP/1.1 200 OK\n"
822     "Content-type: */*\n",
823     "", false,
824     "", false,
825     "*/*" },
826 };
827 // clang-format on
828 
829 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
830                          ContentTypeTest,
831                          testing::ValuesIn(mimetype_tests));
832 
833 struct RequiresValidationTestData {
834   const char* headers;
835   ValidationType validation_type;
836 };
837 
838 class RequiresValidationTest
839     : public HttpResponseHeadersTest,
840       public ::testing::WithParamInterface<RequiresValidationTestData> {
841 };
842 
TEST_P(RequiresValidationTest,RequiresValidation)843 TEST_P(RequiresValidationTest, RequiresValidation) {
844   const RequiresValidationTestData test = GetParam();
845 
846   base::Time request_time, response_time, current_time;
847   ASSERT_TRUE(
848       base::Time::FromString("Wed, 28 Nov 2007 00:40:09 GMT", &request_time));
849   ASSERT_TRUE(
850       base::Time::FromString("Wed, 28 Nov 2007 00:40:12 GMT", &response_time));
851   ASSERT_TRUE(
852       base::Time::FromString("Wed, 28 Nov 2007 00:45:20 GMT", &current_time));
853 
854   std::string headers(test.headers);
855   HeadersToRaw(&headers);
856   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
857 
858   ValidationType validation_type =
859       parsed->RequiresValidation(request_time, response_time, current_time);
860   EXPECT_EQ(test.validation_type, validation_type);
861 }
862 
863 const struct RequiresValidationTestData requires_validation_tests[] = {
864     // No expiry info: expires immediately.
865     {"HTTP/1.1 200 OK\n"
866      "\n",
867      VALIDATION_SYNCHRONOUS},
868     // No expiry info: expires immediately.
869     {"HTTP/1.1 200 OK\n"
870      "\n",
871      VALIDATION_SYNCHRONOUS},
872     // Valid for a little while.
873     {"HTTP/1.1 200 OK\n"
874      "cache-control: max-age=10000\n"
875      "\n",
876      VALIDATION_NONE},
877     // Expires in the future.
878     {"HTTP/1.1 200 OK\n"
879      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
880      "expires: Wed, 28 Nov 2007 01:00:00 GMT\n"
881      "\n",
882      VALIDATION_NONE},
883     // Already expired.
884     {"HTTP/1.1 200 OK\n"
885      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
886      "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
887      "\n",
888      VALIDATION_SYNCHRONOUS},
889     // Max-age trumps expires.
890     {"HTTP/1.1 200 OK\n"
891      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
892      "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
893      "cache-control: max-age=10000\n"
894      "\n",
895      VALIDATION_NONE},
896     // Last-modified heuristic: modified a while ago.
897     {"HTTP/1.1 200 OK\n"
898      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
899      "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
900      "\n",
901      VALIDATION_NONE},
902     {"HTTP/1.1 203 Non-Authoritative Information\n"
903      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
904      "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
905      "\n",
906      VALIDATION_NONE},
907     {"HTTP/1.1 206 Partial Content\n"
908      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
909      "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
910      "\n",
911      VALIDATION_NONE},
912     // Last-modified heuristic: modified recently.
913     {"HTTP/1.1 200 OK\n"
914      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
915      "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
916      "\n",
917      VALIDATION_SYNCHRONOUS},
918     {"HTTP/1.1 203 Non-Authoritative Information\n"
919      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
920      "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
921      "\n",
922      VALIDATION_SYNCHRONOUS},
923     {"HTTP/1.1 206 Partial Content\n"
924      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
925      "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
926      "\n",
927      VALIDATION_SYNCHRONOUS},
928     // Cached permanent redirect.
929     {"HTTP/1.1 301 Moved Permanently\n"
930      "\n",
931      VALIDATION_NONE},
932     // Another cached permanent redirect.
933     {"HTTP/1.1 308 Permanent Redirect\n"
934      "\n",
935      VALIDATION_NONE},
936     // Cached redirect: not reusable even though by default it would be.
937     {"HTTP/1.1 300 Multiple Choices\n"
938      "Cache-Control: no-cache\n"
939      "\n",
940      VALIDATION_SYNCHRONOUS},
941     // Cached forever by default.
942     {"HTTP/1.1 410 Gone\n"
943      "\n",
944      VALIDATION_NONE},
945     // Cached temporary redirect: not reusable.
946     {"HTTP/1.1 302 Found\n"
947      "\n",
948      VALIDATION_SYNCHRONOUS},
949     // Cached temporary redirect: reusable.
950     {"HTTP/1.1 302 Found\n"
951      "cache-control: max-age=10000\n"
952      "\n",
953      VALIDATION_NONE},
954     // Cache-control: max-age=N overrides expires: date in the past.
955     {"HTTP/1.1 200 OK\n"
956      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
957      "expires: Wed, 28 Nov 2007 00:20:11 GMT\n"
958      "cache-control: max-age=10000\n"
959      "\n",
960      VALIDATION_NONE},
961     // Cache-control: no-store overrides expires: in the future.
962     {"HTTP/1.1 200 OK\n"
963      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
964      "expires: Wed, 29 Nov 2007 00:40:11 GMT\n"
965      "cache-control: no-store,private,no-cache=\"foo\"\n"
966      "\n",
967      VALIDATION_SYNCHRONOUS},
968     // Pragma: no-cache overrides last-modified heuristic.
969     {"HTTP/1.1 200 OK\n"
970      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
971      "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
972      "pragma: no-cache\n"
973      "\n",
974      VALIDATION_SYNCHRONOUS},
975     // max-age has expired, needs synchronous revalidation
976     {"HTTP/1.1 200 OK\n"
977      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
978      "cache-control: max-age=300\n"
979      "\n",
980      VALIDATION_SYNCHRONOUS},
981     // max-age has expired, stale-while-revalidate has not, eligible for
982     // asynchronous revalidation
983     {"HTTP/1.1 200 OK\n"
984      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
985      "cache-control: max-age=300, stale-while-revalidate=3600\n"
986      "\n",
987      VALIDATION_ASYNCHRONOUS},
988     // max-age and stale-while-revalidate have expired, needs synchronous
989     // revalidation
990     {"HTTP/1.1 200 OK\n"
991      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
992      "cache-control: max-age=300, stale-while-revalidate=5\n"
993      "\n",
994      VALIDATION_SYNCHRONOUS},
995     // max-age is 0, stale-while-revalidate is large enough to permit
996     // asynchronous revalidation
997     {"HTTP/1.1 200 OK\n"
998      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
999      "cache-control: max-age=0, stale-while-revalidate=360\n"
1000      "\n",
1001      VALIDATION_ASYNCHRONOUS},
1002     // stale-while-revalidate must not override no-cache or similar directives.
1003     {"HTTP/1.1 200 OK\n"
1004      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
1005      "cache-control: no-cache, stale-while-revalidate=360\n"
1006      "\n",
1007      VALIDATION_SYNCHRONOUS},
1008     // max-age has not expired, so no revalidation is needed.
1009     {"HTTP/1.1 200 OK\n"
1010      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
1011      "cache-control: max-age=3600, stale-while-revalidate=3600\n"
1012      "\n",
1013      VALIDATION_NONE},
1014     // must-revalidate overrides stale-while-revalidate, so synchronous
1015     // validation
1016     // is needed.
1017     {"HTTP/1.1 200 OK\n"
1018      "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
1019      "cache-control: must-revalidate, max-age=300, "
1020      "stale-while-revalidate=3600\n"
1021      "\n",
1022      VALIDATION_SYNCHRONOUS},
1023 
1024     // TODO(darin): Add many many more tests here.
1025 };
1026 
1027 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1028                          RequiresValidationTest,
1029                          testing::ValuesIn(requires_validation_tests));
1030 
1031 struct UpdateTestData {
1032   const char* orig_headers;
1033   const char* new_headers;
1034   const char* expected_headers;
1035 };
1036 
1037 class UpdateTest
1038     : public HttpResponseHeadersTest,
1039       public ::testing::WithParamInterface<UpdateTestData> {
1040 };
1041 
TEST_P(UpdateTest,Update)1042 TEST_P(UpdateTest, Update) {
1043   const UpdateTestData test = GetParam();
1044 
1045   std::string orig_headers(test.orig_headers);
1046   HeadersToRaw(&orig_headers);
1047   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
1048 
1049   std::string new_headers(test.new_headers);
1050   HeadersToRaw(&new_headers);
1051   auto new_parsed = base::MakeRefCounted<HttpResponseHeaders>(new_headers);
1052 
1053   parsed->Update(*new_parsed.get());
1054 
1055   EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
1056 }
1057 
1058 const UpdateTestData update_tests[] = {
1059     {"HTTP/1.1 200 OK\n",
1060 
1061      "HTTP/1/1 304 Not Modified\n"
1062      "connection: keep-alive\n"
1063      "Cache-control: max-age=10000\n",
1064 
1065      "HTTP/1.1 200 OK\n"
1066      "Cache-control: max-age=10000\n"},
1067     {"HTTP/1.1 200 OK\n"
1068      "Foo: 1\n"
1069      "Cache-control: private\n",
1070 
1071      "HTTP/1/1 304 Not Modified\n"
1072      "connection: keep-alive\n"
1073      "Cache-control: max-age=10000\n",
1074 
1075      "HTTP/1.1 200 OK\n"
1076      "Cache-control: max-age=10000\n"
1077      "Foo: 1\n"},
1078     {"HTTP/1.1 200 OK\n"
1079      "Foo: 1\n"
1080      "Cache-control: private\n",
1081 
1082      "HTTP/1/1 304 Not Modified\n"
1083      "connection: keep-alive\n"
1084      "Cache-CONTROL: max-age=10000\n",
1085 
1086      "HTTP/1.1 200 OK\n"
1087      "Cache-CONTROL: max-age=10000\n"
1088      "Foo: 1\n"},
1089     {"HTTP/1.1 200 OK\n"
1090      "Content-Length: 450\n",
1091 
1092      "HTTP/1/1 304 Not Modified\n"
1093      "connection: keep-alive\n"
1094      "Cache-control:      max-age=10001   \n",
1095 
1096      "HTTP/1.1 200 OK\n"
1097      "Cache-control: max-age=10001\n"
1098      "Content-Length: 450\n"},
1099     {
1100         "HTTP/1.1 200 OK\n"
1101         "X-Frame-Options: DENY\n",
1102 
1103         "HTTP/1/1 304 Not Modified\n"
1104         "X-Frame-Options: ALLOW\n",
1105 
1106         "HTTP/1.1 200 OK\n"
1107         "X-Frame-Options: DENY\n",
1108     },
1109     {
1110         "HTTP/1.1 200 OK\n"
1111         "X-WebKit-CSP: default-src 'none'\n",
1112 
1113         "HTTP/1/1 304 Not Modified\n"
1114         "X-WebKit-CSP: default-src *\n",
1115 
1116         "HTTP/1.1 200 OK\n"
1117         "X-WebKit-CSP: default-src 'none'\n",
1118     },
1119     {
1120         "HTTP/1.1 200 OK\n"
1121         "X-XSS-Protection: 1\n",
1122 
1123         "HTTP/1/1 304 Not Modified\n"
1124         "X-XSS-Protection: 0\n",
1125 
1126         "HTTP/1.1 200 OK\n"
1127         "X-XSS-Protection: 1\n",
1128     },
1129     {"HTTP/1.1 200 OK\n",
1130 
1131      "HTTP/1/1 304 Not Modified\n"
1132      "X-Content-Type-Options: nosniff\n",
1133 
1134      "HTTP/1.1 200 OK\n"},
1135     {"HTTP/1.1 200 OK\n"
1136      "Content-Encoding: identity\n"
1137      "Content-Length: 100\n"
1138      "Content-Type: text/html\n"
1139      "Content-Security-Policy: default-src 'none'\n",
1140 
1141      "HTTP/1/1 304 Not Modified\n"
1142      "Content-Encoding: gzip\n"
1143      "Content-Length: 200\n"
1144      "Content-Type: text/xml\n"
1145      "Content-Security-Policy: default-src 'self'\n",
1146 
1147      "HTTP/1.1 200 OK\n"
1148      "Content-Security-Policy: default-src 'self'\n"
1149      "Content-Encoding: identity\n"
1150      "Content-Length: 100\n"
1151      "Content-Type: text/html\n"},
1152     {"HTTP/1.1 200 OK\n"
1153      "Content-Location: /example_page.html\n",
1154 
1155      "HTTP/1/1 304 Not Modified\n"
1156      "Content-Location: /not_example_page.html\n",
1157 
1158      "HTTP/1.1 200 OK\n"
1159      "Content-Location: /example_page.html\n"},
1160 };
1161 
1162 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1163                          UpdateTest,
1164                          testing::ValuesIn(update_tests));
1165 
1166 struct EnumerateHeaderTestData {
1167   const char* headers;
1168   const char* expected_lines;
1169 };
1170 
1171 class EnumerateHeaderLinesTest
1172     : public HttpResponseHeadersTest,
1173       public ::testing::WithParamInterface<EnumerateHeaderTestData> {
1174 };
1175 
TEST_P(EnumerateHeaderLinesTest,EnumerateHeaderLines)1176 TEST_P(EnumerateHeaderLinesTest, EnumerateHeaderLines) {
1177   const EnumerateHeaderTestData test = GetParam();
1178 
1179   std::string headers(test.headers);
1180   HeadersToRaw(&headers);
1181   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1182 
1183   std::string name, value, lines;
1184 
1185   size_t iter = 0;
1186   while (parsed->EnumerateHeaderLines(&iter, &name, &value)) {
1187     lines.append(name);
1188     lines.append(": ");
1189     lines.append(value);
1190     lines.append("\n");
1191   }
1192 
1193   EXPECT_EQ(std::string(test.expected_lines), lines);
1194 }
1195 
1196 const EnumerateHeaderTestData enumerate_header_tests[] = {
1197     {"HTTP/1.1 200 OK\n",
1198 
1199      ""},
1200     {"HTTP/1.1 200 OK\n"
1201      "Foo: 1\n",
1202 
1203      "Foo: 1\n"},
1204     {"HTTP/1.1 200 OK\n"
1205      "Foo: 1\n"
1206      "Bar: 2\n"
1207      "Foo: 3\n",
1208 
1209      "Foo: 1\nBar: 2\nFoo: 3\n"},
1210     {"HTTP/1.1 200 OK\n"
1211      "Foo: 1, 2, 3\n",
1212 
1213      "Foo: 1, 2, 3\n"},
1214     {"HTTP/1.1 200 OK\n"
1215      "Foo: ,, 1,, 2, 3,, \n",
1216 
1217      "Foo: ,, 1,, 2, 3,,\n"},
1218 };
1219 
1220 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1221                          EnumerateHeaderLinesTest,
1222                          testing::ValuesIn(enumerate_header_tests));
1223 
1224 struct IsRedirectTestData {
1225   const char* headers;
1226   const char* location;
1227   bool is_redirect;
1228 };
1229 
1230 class IsRedirectTest
1231     : public HttpResponseHeadersTest,
1232       public ::testing::WithParamInterface<IsRedirectTestData> {
1233 };
1234 
TEST_P(IsRedirectTest,IsRedirect)1235 TEST_P(IsRedirectTest, IsRedirect) {
1236   const IsRedirectTestData test = GetParam();
1237 
1238   std::string headers(test.headers);
1239   HeadersToRaw(&headers);
1240   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1241 
1242   std::string location;
1243   EXPECT_EQ(parsed->IsRedirect(&location), test.is_redirect);
1244   EXPECT_EQ(location, test.location);
1245 }
1246 
1247 const IsRedirectTestData is_redirect_tests[] = {
1248   { "HTTP/1.1 200 OK\n",
1249     "",
1250     false
1251   },
1252   { "HTTP/1.1 301 Moved\n"
1253     "Location: http://foopy/\n",
1254     "http://foopy/",
1255     true
1256   },
1257   { "HTTP/1.1 301 Moved\n"
1258     "Location: \t \n",
1259     "",
1260     false
1261   },
1262   // We use the first location header as the target of the redirect.
1263   { "HTTP/1.1 301 Moved\n"
1264     "Location: http://foo/\n"
1265     "Location: http://bar/\n",
1266     "http://foo/",
1267     true
1268   },
1269   // We use the first _valid_ location header as the target of the redirect.
1270   { "HTTP/1.1 301 Moved\n"
1271     "Location: \n"
1272     "Location: http://bar/\n",
1273     "http://bar/",
1274     true
1275   },
1276   // Bug 1050541 (location header with an unescaped comma).
1277   { "HTTP/1.1 301 Moved\n"
1278     "Location: http://foo/bar,baz.html\n",
1279     "http://foo/bar,baz.html",
1280     true
1281   },
1282   // Bug 1224617 (location header with non-ASCII bytes).
1283   { "HTTP/1.1 301 Moved\n"
1284     "Location: http://foo/bar?key=\xE4\xF6\xFC\n",
1285     "http://foo/bar?key=%E4%F6%FC",
1286     true
1287   },
1288   // Shift_JIS, Big5, and GBK contain multibyte characters with the trailing
1289   // byte falling in the ASCII range.
1290   { "HTTP/1.1 301 Moved\n"
1291     "Location: http://foo/bar?key=\x81\x5E\xD8\xBF\n",
1292     "http://foo/bar?key=%81^%D8%BF",
1293     true
1294   },
1295   { "HTTP/1.1 301 Moved\n"
1296     "Location: http://foo/bar?key=\x82\x40\xBD\xC4\n",
1297     "http://foo/bar?key=%82@%BD%C4",
1298     true
1299   },
1300   { "HTTP/1.1 301 Moved\n"
1301     "Location: http://foo/bar?key=\x83\x5C\x82\x5D\xCB\xD7\n",
1302     "http://foo/bar?key=%83\\%82]%CB%D7",
1303     true
1304   },
1305 };
1306 
1307 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1308                          IsRedirectTest,
1309                          testing::ValuesIn(is_redirect_tests));
1310 
1311 struct ContentLengthTestData {
1312   const char* headers;
1313   int64_t expected_len;
1314 };
1315 
1316 class GetContentLengthTest
1317     : public HttpResponseHeadersTest,
1318       public ::testing::WithParamInterface<ContentLengthTestData> {
1319 };
1320 
TEST_P(GetContentLengthTest,GetContentLength)1321 TEST_P(GetContentLengthTest, GetContentLength) {
1322   const ContentLengthTestData test = GetParam();
1323 
1324   std::string headers(test.headers);
1325   HeadersToRaw(&headers);
1326   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1327 
1328   EXPECT_EQ(test.expected_len, parsed->GetContentLength());
1329 }
1330 
1331 const ContentLengthTestData content_length_tests[] = {
1332     {"HTTP/1.1 200 OK\n", -1},
1333     {"HTTP/1.1 200 OK\n"
1334      "Content-Length: 10\n",
1335      10},
1336     {"HTTP/1.1 200 OK\n"
1337      "Content-Length: \n",
1338      -1},
1339     {"HTTP/1.1 200 OK\n"
1340      "Content-Length: abc\n",
1341      -1},
1342     {"HTTP/1.1 200 OK\n"
1343      "Content-Length: -10\n",
1344      -1},
1345     {"HTTP/1.1 200 OK\n"
1346      "Content-Length:  +10\n",
1347      -1},
1348     {"HTTP/1.1 200 OK\n"
1349      "Content-Length: 23xb5\n",
1350      -1},
1351     {"HTTP/1.1 200 OK\n"
1352      "Content-Length: 0xA\n",
1353      -1},
1354     {"HTTP/1.1 200 OK\n"
1355      "Content-Length: 010\n",
1356      10},
1357     // Content-Length too big, will overflow an int64_t.
1358     {"HTTP/1.1 200 OK\n"
1359      "Content-Length: 40000000000000000000\n",
1360      -1},
1361     {"HTTP/1.1 200 OK\n"
1362      "Content-Length:       10\n",
1363      10},
1364     {"HTTP/1.1 200 OK\n"
1365      "Content-Length: 10  \n",
1366      10},
1367     {"HTTP/1.1 200 OK\n"
1368      "Content-Length: \t10\n",
1369      10},
1370     {"HTTP/1.1 200 OK\n"
1371      "Content-Length: \v10\n",
1372      -1},
1373     {"HTTP/1.1 200 OK\n"
1374      "Content-Length: \f10\n",
1375      -1},
1376     {"HTTP/1.1 200 OK\n"
1377      "cOnTeNt-LENgth: 33\n",
1378      33},
1379     {"HTTP/1.1 200 OK\n"
1380      "Content-Length: 34\r\n",
1381      -1},
1382 };
1383 
1384 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1385                          GetContentLengthTest,
1386                          testing::ValuesIn(content_length_tests));
1387 
1388 struct ContentRangeTestData {
1389   const char* headers;
1390   bool expected_return_value;
1391   int64_t expected_first_byte_position;
1392   int64_t expected_last_byte_position;
1393   int64_t expected_instance_size;
1394 };
1395 
1396 class ContentRangeTest
1397     : public HttpResponseHeadersTest,
1398       public ::testing::WithParamInterface<ContentRangeTestData> {
1399 };
1400 
TEST_P(ContentRangeTest,GetContentRangeFor206)1401 TEST_P(ContentRangeTest, GetContentRangeFor206) {
1402   const ContentRangeTestData test = GetParam();
1403 
1404   std::string headers(test.headers);
1405   HeadersToRaw(&headers);
1406   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1407 
1408   int64_t first_byte_position;
1409   int64_t last_byte_position;
1410   int64_t instance_size;
1411   bool return_value = parsed->GetContentRangeFor206(
1412       &first_byte_position, &last_byte_position, &instance_size);
1413   EXPECT_EQ(test.expected_return_value, return_value);
1414   EXPECT_EQ(test.expected_first_byte_position, first_byte_position);
1415   EXPECT_EQ(test.expected_last_byte_position, last_byte_position);
1416   EXPECT_EQ(test.expected_instance_size, instance_size);
1417 }
1418 
1419 const ContentRangeTestData content_range_tests[] = {
1420     {"HTTP/1.1 206 Partial Content", false, -1, -1, -1},
1421     {"HTTP/1.1 206 Partial Content\n"
1422      "Content-Range:",
1423      false, -1, -1, -1},
1424     {"HTTP/1.1 206 Partial Content\n"
1425      "Content-Range: bytes 0-50/51",
1426      true, 0, 50, 51},
1427     {"HTTP/1.1 206 Partial Content\n"
1428      "Content-Range: bytes 50-0/51",
1429      false, -1, -1, -1},
1430     {"HTTP/1.1 416 Requested range not satisfiable\n"
1431      "Content-Range: bytes */*",
1432      false, -1, -1, -1},
1433     {"HTTP/1.1 206 Partial Content\n"
1434      "Content-Range: bytes 0-50/*",
1435      false, -1, -1, -1},
1436 };
1437 
1438 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1439                          ContentRangeTest,
1440                          testing::ValuesIn(content_range_tests));
1441 
1442 struct KeepAliveTestData {
1443   const char* headers;
1444   bool expected_keep_alive;
1445 };
1446 
1447 // Enable GTest to print KeepAliveTestData in an intelligible way if the test
1448 // fails.
PrintTo(const KeepAliveTestData & keep_alive_test_data,std::ostream * os)1449 void PrintTo(const KeepAliveTestData& keep_alive_test_data,
1450              std::ostream* os) {
1451   *os << "{\"" << keep_alive_test_data.headers << "\", " << std::boolalpha
1452       << keep_alive_test_data.expected_keep_alive << "}";
1453 }
1454 
1455 class IsKeepAliveTest
1456     : public HttpResponseHeadersTest,
1457       public ::testing::WithParamInterface<KeepAliveTestData> {
1458 };
1459 
TEST_P(IsKeepAliveTest,IsKeepAlive)1460 TEST_P(IsKeepAliveTest, IsKeepAlive) {
1461   const KeepAliveTestData test = GetParam();
1462 
1463   std::string headers(test.headers);
1464   HeadersToRaw(&headers);
1465   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1466 
1467   EXPECT_EQ(test.expected_keep_alive, parsed->IsKeepAlive());
1468 }
1469 
1470 const KeepAliveTestData keepalive_tests[] = {
1471   // The status line fabricated by HttpNetworkTransaction for a 0.9 response.
1472   // Treated as 0.9.
1473   { "HTTP/0.9 200 OK",
1474     false
1475   },
1476   // This could come from a broken server.  Treated as 1.0 because it has a
1477   // header.
1478   { "HTTP/0.9 200 OK\n"
1479     "connection: keep-alive\n",
1480     true
1481   },
1482   { "HTTP/1.1 200 OK\n",
1483     true
1484   },
1485   { "HTTP/1.0 200 OK\n",
1486     false
1487   },
1488   { "HTTP/1.0 200 OK\n"
1489     "connection: close\n",
1490     false
1491   },
1492   { "HTTP/1.0 200 OK\n"
1493     "connection: keep-alive\n",
1494     true
1495   },
1496   { "HTTP/1.0 200 OK\n"
1497     "connection: kEeP-AliVe\n",
1498     true
1499   },
1500   { "HTTP/1.0 200 OK\n"
1501     "connection: keep-aliveX\n",
1502     false
1503   },
1504   { "HTTP/1.1 200 OK\n"
1505     "connection: close\n",
1506     false
1507   },
1508   { "HTTP/1.1 200 OK\n"
1509     "connection: keep-alive\n",
1510     true
1511   },
1512   { "HTTP/1.0 200 OK\n"
1513     "proxy-connection: close\n",
1514     false
1515   },
1516   { "HTTP/1.0 200 OK\n"
1517     "proxy-connection: keep-alive\n",
1518     true
1519   },
1520   { "HTTP/1.1 200 OK\n"
1521     "proxy-connection: close\n",
1522     false
1523   },
1524   { "HTTP/1.1 200 OK\n"
1525     "proxy-connection: keep-alive\n",
1526     true
1527   },
1528   { "HTTP/1.1 200 OK\n"
1529     "Connection: Upgrade, close\n",
1530     false
1531   },
1532   { "HTTP/1.1 200 OK\n"
1533     "Connection: Upgrade, keep-alive\n",
1534     true
1535   },
1536   { "HTTP/1.1 200 OK\n"
1537     "Connection: Upgrade\n"
1538     "Connection: close\n",
1539     false
1540   },
1541   { "HTTP/1.1 200 OK\n"
1542     "Connection: Upgrade\n"
1543     "Connection: keep-alive\n",
1544     true
1545   },
1546   { "HTTP/1.1 200 OK\n"
1547     "Connection: close, Upgrade\n",
1548     false
1549   },
1550   { "HTTP/1.1 200 OK\n"
1551     "Connection: keep-alive, Upgrade\n",
1552     true
1553   },
1554   { "HTTP/1.1 200 OK\n"
1555     "Connection: Upgrade\n"
1556     "Proxy-Connection: close\n",
1557     false
1558   },
1559   { "HTTP/1.1 200 OK\n"
1560     "Connection: Upgrade\n"
1561     "Proxy-Connection: keep-alive\n",
1562     true
1563   },
1564   // In situations where the response headers conflict with themselves, use the
1565   // first one for backwards-compatibility.
1566   { "HTTP/1.1 200 OK\n"
1567     "Connection: close\n"
1568     "Connection: keep-alive\n",
1569     false
1570   },
1571   { "HTTP/1.1 200 OK\n"
1572     "Connection: keep-alive\n"
1573     "Connection: close\n",
1574     true
1575   },
1576   { "HTTP/1.0 200 OK\n"
1577     "Connection: close\n"
1578     "Connection: keep-alive\n",
1579     false
1580   },
1581   { "HTTP/1.0 200 OK\n"
1582     "Connection: keep-alive\n"
1583     "Connection: close\n",
1584     true
1585   },
1586   // Ignore the Proxy-Connection header if at all possible.
1587   { "HTTP/1.0 200 OK\n"
1588     "Proxy-Connection: keep-alive\n"
1589     "Connection: close\n",
1590     false
1591   },
1592   { "HTTP/1.1 200 OK\n"
1593     "Proxy-Connection: close\n"
1594     "Connection: keep-alive\n",
1595     true
1596   },
1597   // Older versions of Chrome would have ignored Proxy-Connection in this case,
1598   // but it doesn't seem safe.
1599   { "HTTP/1.1 200 OK\n"
1600     "Proxy-Connection: close\n"
1601     "Connection: Transfer-Encoding\n",
1602     false
1603   },
1604 };
1605 
1606 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1607                          IsKeepAliveTest,
1608                          testing::ValuesIn(keepalive_tests));
1609 
1610 struct HasStrongValidatorsTestData {
1611   const char* headers;
1612   bool expected_result;
1613 };
1614 
1615 class HasStrongValidatorsTest
1616     : public HttpResponseHeadersTest,
1617       public ::testing::WithParamInterface<HasStrongValidatorsTestData> {
1618 };
1619 
TEST_P(HasStrongValidatorsTest,HasStrongValidators)1620 TEST_P(HasStrongValidatorsTest, HasStrongValidators) {
1621   const HasStrongValidatorsTestData test = GetParam();
1622 
1623   std::string headers(test.headers);
1624   HeadersToRaw(&headers);
1625   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1626 
1627   EXPECT_EQ(test.expected_result, parsed->HasStrongValidators());
1628 }
1629 
1630 const HasStrongValidatorsTestData strong_validators_tests[] = {
1631   { "HTTP/0.9 200 OK",
1632     false
1633   },
1634   { "HTTP/1.0 200 OK\n"
1635     "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1636     "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1637     "ETag: \"foo\"\n",
1638     false
1639   },
1640   { "HTTP/1.1 200 OK\n"
1641     "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1642     "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1643     "ETag: \"foo\"\n",
1644     true
1645   },
1646   { "HTTP/1.1 200 OK\n"
1647     "Date: Wed, 28 Nov 2007 00:41:10 GMT\n"
1648     "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1649     true
1650   },
1651   { "HTTP/1.1 200 OK\n"
1652     "Date: Wed, 28 Nov 2007 00:41:09 GMT\n"
1653     "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1654     false
1655   },
1656   { "HTTP/1.1 200 OK\n"
1657     "ETag: \"foo\"\n",
1658     true
1659   },
1660   // This is not really a weak etag:
1661   { "HTTP/1.1 200 OK\n"
1662     "etag: \"w/foo\"\n",
1663     true
1664   },
1665   // This is a weak etag:
1666   { "HTTP/1.1 200 OK\n"
1667     "etag: w/\"foo\"\n",
1668     false
1669   },
1670   { "HTTP/1.1 200 OK\n"
1671     "etag:    W  /   \"foo\"\n",
1672     false
1673   }
1674 };
1675 
1676 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1677                          HasStrongValidatorsTest,
1678                          testing::ValuesIn(strong_validators_tests));
1679 
TEST(HttpResponseHeadersTest,HasValidatorsNone)1680 TEST(HttpResponseHeadersTest, HasValidatorsNone) {
1681   std::string headers("HTTP/1.1 200 OK");
1682   HeadersToRaw(&headers);
1683   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1684   EXPECT_FALSE(parsed->HasValidators());
1685 }
1686 
TEST(HttpResponseHeadersTest,HasValidatorsEtag)1687 TEST(HttpResponseHeadersTest, HasValidatorsEtag) {
1688   std::string headers(
1689       "HTTP/1.1 200 OK\n"
1690       "etag: \"anything\"");
1691   HeadersToRaw(&headers);
1692   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1693   EXPECT_TRUE(parsed->HasValidators());
1694 }
1695 
TEST(HttpResponseHeadersTest,HasValidatorsLastModified)1696 TEST(HttpResponseHeadersTest, HasValidatorsLastModified) {
1697   std::string headers(
1698       "HTTP/1.1 200 OK\n"
1699       "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT");
1700   HeadersToRaw(&headers);
1701   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1702   EXPECT_TRUE(parsed->HasValidators());
1703 }
1704 
TEST(HttpResponseHeadersTest,HasValidatorsWeakEtag)1705 TEST(HttpResponseHeadersTest, HasValidatorsWeakEtag) {
1706   std::string headers(
1707       "HTTP/1.1 200 OK\n"
1708       "etag: W/\"anything\"");
1709   HeadersToRaw(&headers);
1710   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1711   EXPECT_TRUE(parsed->HasValidators());
1712 }
1713 
TEST(HttpResponseHeadersTest,GetNormalizedHeaderWithEmptyValues)1714 TEST(HttpResponseHeadersTest, GetNormalizedHeaderWithEmptyValues) {
1715   std::string headers(
1716       "HTTP/1.1 200 OK\n"
1717       "a:\n"
1718       "b: \n"
1719       "c:*\n"
1720       "d: *\n"
1721       "e:    \n"
1722       "a: \n"
1723       "b:*\n"
1724       "c:\n"
1725       "d:*\n"
1726       "a:\n");
1727   HeadersToRaw(&headers);
1728   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1729   std::string value;
1730 
1731   EXPECT_TRUE(parsed->GetNormalizedHeader("a", &value));
1732   EXPECT_EQ(value, ", , ");
1733   EXPECT_TRUE(parsed->GetNormalizedHeader("b", &value));
1734   EXPECT_EQ(value, ", *");
1735   EXPECT_TRUE(parsed->GetNormalizedHeader("c", &value));
1736   EXPECT_EQ(value, "*, ");
1737   EXPECT_TRUE(parsed->GetNormalizedHeader("d", &value));
1738   EXPECT_EQ(value, "*, *");
1739   EXPECT_TRUE(parsed->GetNormalizedHeader("e", &value));
1740   EXPECT_EQ(value, "");
1741   EXPECT_FALSE(parsed->GetNormalizedHeader("f", &value));
1742 }
1743 
TEST(HttpResponseHeadersTest,GetNormalizedHeaderWithCommas)1744 TEST(HttpResponseHeadersTest, GetNormalizedHeaderWithCommas) {
1745   std::string headers(
1746       "HTTP/1.1 200 OK\n"
1747       "a: foo, bar\n"
1748       "b: , foo, bar,\n"
1749       "c: ,,,\n"
1750       "d:  ,  ,  ,  \n"
1751       "e:\t,\t,\t,\t\n"
1752       "a: ,");
1753   HeadersToRaw(&headers);
1754   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
1755   std::string value;
1756 
1757   // TODO(mmenke): "Normalized" headers probably should preserve the
1758   // leading/trailing whitespace from the original headers.
1759   ASSERT_TRUE(parsed->GetNormalizedHeader("a", &value));
1760   EXPECT_EQ("foo, bar, ,", value);
1761   ASSERT_TRUE(parsed->GetNormalizedHeader("b", &value));
1762   EXPECT_EQ(", foo, bar,", value);
1763   ASSERT_TRUE(parsed->GetNormalizedHeader("c", &value));
1764   EXPECT_EQ(",,,", value);
1765   ASSERT_TRUE(parsed->GetNormalizedHeader("d", &value));
1766   EXPECT_EQ(",  ,  ,", value);
1767   ASSERT_TRUE(parsed->GetNormalizedHeader("e", &value));
1768   EXPECT_EQ(",\t,\t,", value);
1769   EXPECT_FALSE(parsed->GetNormalizedHeader("f", &value));
1770 }
1771 
TEST(HttpResponseHeadersTest,AddHeader)1772 TEST(HttpResponseHeadersTest, AddHeader) {
1773   scoped_refptr<HttpResponseHeaders> headers = HttpResponseHeaders::TryToCreate(
1774       "HTTP/1.1 200 OK\n"
1775       "connection: keep-alive\n"
1776       "Cache-control: max-age=10000\n");
1777   ASSERT_TRUE(headers);
1778 
1779   headers->AddHeader("Content-Length", "450");
1780   EXPECT_EQ(
1781       "HTTP/1.1 200 OK\n"
1782       "connection: keep-alive\n"
1783       "Cache-control: max-age=10000\n"
1784       "Content-Length: 450\n",
1785       ToSimpleString(headers));
1786 
1787   // Add a second Content-Length header with extra spaces in the value. It
1788   // should be added to the end, and the extra spaces removed.
1789   headers->AddHeader("Content-Length", "   42    ");
1790   EXPECT_EQ(
1791       "HTTP/1.1 200 OK\n"
1792       "connection: keep-alive\n"
1793       "Cache-control: max-age=10000\n"
1794       "Content-Length: 450\n"
1795       "Content-Length: 42\n",
1796       ToSimpleString(headers));
1797 }
1798 
TEST(HttpResponseHeadersTest,SetHeader)1799 TEST(HttpResponseHeadersTest, SetHeader) {
1800   scoped_refptr<HttpResponseHeaders> headers = HttpResponseHeaders::TryToCreate(
1801       "HTTP/1.1 200 OK\n"
1802       "connection: keep-alive\n"
1803       "Cache-control: max-age=10000\n");
1804   ASSERT_TRUE(headers);
1805 
1806   headers->SetHeader("Content-Length", "450");
1807   EXPECT_EQ(
1808       "HTTP/1.1 200 OK\n"
1809       "connection: keep-alive\n"
1810       "Cache-control: max-age=10000\n"
1811       "Content-Length: 450\n",
1812       ToSimpleString(headers));
1813 
1814   headers->SetHeader("Content-Length", "   42    ");
1815   EXPECT_EQ(
1816       "HTTP/1.1 200 OK\n"
1817       "connection: keep-alive\n"
1818       "Cache-control: max-age=10000\n"
1819       "Content-Length: 42\n",
1820       ToSimpleString(headers));
1821 
1822   headers->SetHeader("connection", "close");
1823   EXPECT_EQ(
1824       "HTTP/1.1 200 OK\n"
1825       "Cache-control: max-age=10000\n"
1826       "Content-Length: 42\n"
1827       "connection: close\n",
1828       ToSimpleString(headers));
1829 }
1830 
1831 #if !BUILDFLAG(CRONET_BUILD)
1832 // Cronet disables tracing so this test would fail.
TEST(HttpResponseHeadersTest,TracingSupport)1833 TEST(HttpResponseHeadersTest, TracingSupport) {
1834   scoped_refptr<HttpResponseHeaders> headers = HttpResponseHeaders::TryToCreate(
1835       "HTTP/1.1 200 OK\n"
1836       "connection: keep-alive\n");
1837   ASSERT_TRUE(headers);
1838 
1839   EXPECT_EQ(perfetto::TracedValueToString(headers),
1840             "{response_code:200,headers:[{name:connection,value:keep-alive}]}");
1841 }
1842 #endif
1843 
1844 struct RemoveHeaderTestData {
1845   const char* orig_headers;
1846   const char* to_remove;
1847   const char* expected_headers;
1848 };
1849 
1850 class RemoveHeaderTest
1851     : public HttpResponseHeadersTest,
1852       public ::testing::WithParamInterface<RemoveHeaderTestData> {
1853 };
1854 
TEST_P(RemoveHeaderTest,RemoveHeader)1855 TEST_P(RemoveHeaderTest, RemoveHeader) {
1856   const RemoveHeaderTestData test = GetParam();
1857 
1858   std::string orig_headers(test.orig_headers);
1859   HeadersToRaw(&orig_headers);
1860   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
1861 
1862   std::string name(test.to_remove);
1863   parsed->RemoveHeader(name);
1864 
1865   EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
1866 }
1867 
1868 const RemoveHeaderTestData remove_header_tests[] = {
1869   { "HTTP/1.1 200 OK\n"
1870     "connection: keep-alive\n"
1871     "Cache-control: max-age=10000\n"
1872     "Content-Length: 450\n",
1873 
1874     "Content-Length",
1875 
1876     "HTTP/1.1 200 OK\n"
1877     "connection: keep-alive\n"
1878     "Cache-control: max-age=10000\n"
1879   },
1880   { "HTTP/1.1 200 OK\n"
1881     "connection: keep-alive  \n"
1882     "Content-Length  : 450  \n"
1883     "Cache-control: max-age=10000\n",
1884 
1885     "Content-Length",
1886 
1887     "HTTP/1.1 200 OK\n"
1888     "connection: keep-alive\n"
1889     "Cache-control: max-age=10000\n"
1890   },
1891 };
1892 
1893 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1894                          RemoveHeaderTest,
1895                          testing::ValuesIn(remove_header_tests));
1896 
1897 struct RemoveHeadersTestData {
1898   const char* orig_headers;
1899   const char* to_remove[2];
1900   const char* expected_headers;
1901 };
1902 
1903 class RemoveHeadersTest
1904     : public HttpResponseHeadersTest,
1905       public ::testing::WithParamInterface<RemoveHeadersTestData> {};
1906 
TEST_P(RemoveHeadersTest,RemoveHeaders)1907 TEST_P(RemoveHeadersTest, RemoveHeaders) {
1908   const RemoveHeadersTestData test = GetParam();
1909 
1910   std::string orig_headers(test.orig_headers);
1911   HeadersToRaw(&orig_headers);
1912   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
1913 
1914   std::unordered_set<std::string> to_remove;
1915   for (auto* header : test.to_remove) {
1916     if (header)
1917       to_remove.insert(header);
1918   }
1919   parsed->RemoveHeaders(to_remove);
1920 
1921   EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
1922 }
1923 
1924 const RemoveHeadersTestData remove_headers_tests[] = {
1925     {"HTTP/1.1 200 OK\n"
1926      "connection: keep-alive\n"
1927      "Cache-control: max-age=10000\n"
1928      "Content-Length: 450\n",
1929 
1930      {"Content-Length", "CACHE-control"},
1931 
1932      "HTTP/1.1 200 OK\n"
1933      "connection: keep-alive\n"},
1934 
1935     {"HTTP/1.1 200 OK\n"
1936      "connection: keep-alive\n"
1937      "Content-Length: 450\n",
1938 
1939      {"foo", "bar"},
1940 
1941      "HTTP/1.1 200 OK\n"
1942      "connection: keep-alive\n"
1943      "Content-Length: 450\n"},
1944 
1945     {"HTTP/1.1 404 Kinda not OK\n"
1946      "connection: keep-alive  \n",
1947 
1948      {},
1949 
1950      "HTTP/1.1 404 Kinda not OK\n"
1951      "connection: keep-alive\n"},
1952 };
1953 
1954 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
1955                          RemoveHeadersTest,
1956                          testing::ValuesIn(remove_headers_tests));
1957 
1958 struct RemoveIndividualHeaderTestData {
1959   const char* orig_headers;
1960   const char* to_remove_name;
1961   const char* to_remove_value;
1962   const char* expected_headers;
1963 };
1964 
1965 class RemoveIndividualHeaderTest
1966     : public HttpResponseHeadersTest,
1967       public ::testing::WithParamInterface<RemoveIndividualHeaderTestData> {
1968 };
1969 
TEST_P(RemoveIndividualHeaderTest,RemoveIndividualHeader)1970 TEST_P(RemoveIndividualHeaderTest, RemoveIndividualHeader) {
1971   const RemoveIndividualHeaderTestData test = GetParam();
1972 
1973   std::string orig_headers(test.orig_headers);
1974   HeadersToRaw(&orig_headers);
1975   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
1976 
1977   std::string name(test.to_remove_name);
1978   std::string value(test.to_remove_value);
1979   parsed->RemoveHeaderLine(name, value);
1980 
1981   EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
1982 }
1983 
1984 const RemoveIndividualHeaderTestData remove_individual_header_tests[] = {
1985   { "HTTP/1.1 200 OK\n"
1986     "connection: keep-alive\n"
1987     "Cache-control: max-age=10000\n"
1988     "Content-Length: 450\n",
1989 
1990     "Content-Length",
1991 
1992     "450",
1993 
1994     "HTTP/1.1 200 OK\n"
1995     "connection: keep-alive\n"
1996     "Cache-control: max-age=10000\n"
1997   },
1998   { "HTTP/1.1 200 OK\n"
1999     "connection: keep-alive  \n"
2000     "Content-Length  : 450  \n"
2001     "Cache-control: max-age=10000\n",
2002 
2003     "Content-Length",
2004 
2005     "450",
2006 
2007     "HTTP/1.1 200 OK\n"
2008     "connection: keep-alive\n"
2009     "Cache-control: max-age=10000\n"
2010   },
2011   { "HTTP/1.1 200 OK\n"
2012     "connection: keep-alive  \n"
2013     "Content-Length: 450\n"
2014     "Cache-control: max-age=10000\n",
2015 
2016     "Content-Length",  // Matching name.
2017 
2018     "999",  // Mismatching value.
2019 
2020     "HTTP/1.1 200 OK\n"
2021     "connection: keep-alive\n"
2022     "Content-Length: 450\n"
2023     "Cache-control: max-age=10000\n"
2024   },
2025   { "HTTP/1.1 200 OK\n"
2026     "connection: keep-alive  \n"
2027     "Foo: bar, baz\n"
2028     "Foo: bar\n"
2029     "Cache-control: max-age=10000\n",
2030 
2031     "Foo",
2032 
2033     "bar, baz",  // Space in value.
2034 
2035     "HTTP/1.1 200 OK\n"
2036     "connection: keep-alive\n"
2037     "Foo: bar\n"
2038     "Cache-control: max-age=10000\n"
2039   },
2040   { "HTTP/1.1 200 OK\n"
2041     "connection: keep-alive  \n"
2042     "Foo: bar, baz\n"
2043     "Cache-control: max-age=10000\n",
2044 
2045     "Foo",
2046 
2047     "baz",  // Only partial match -> ignored.
2048 
2049     "HTTP/1.1 200 OK\n"
2050     "connection: keep-alive\n"
2051     "Foo: bar, baz\n"
2052     "Cache-control: max-age=10000\n"
2053   },
2054 };
2055 
2056 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
2057                          RemoveIndividualHeaderTest,
2058                          testing::ValuesIn(remove_individual_header_tests));
2059 
2060 struct ReplaceStatusTestData {
2061   const char* orig_headers;
2062   const char* new_status;
2063   const char* expected_headers;
2064 };
2065 
2066 class ReplaceStatusTest
2067     : public HttpResponseHeadersTest,
2068       public ::testing::WithParamInterface<ReplaceStatusTestData> {
2069 };
2070 
TEST_P(ReplaceStatusTest,ReplaceStatus)2071 TEST_P(ReplaceStatusTest, ReplaceStatus) {
2072   const ReplaceStatusTestData test = GetParam();
2073 
2074   std::string orig_headers(test.orig_headers);
2075   HeadersToRaw(&orig_headers);
2076   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers);
2077 
2078   std::string name(test.new_status);
2079   parsed->ReplaceStatusLine(name);
2080 
2081   EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
2082 }
2083 
2084 const ReplaceStatusTestData replace_status_tests[] = {
2085   { "HTTP/1.1 206 Partial Content\n"
2086     "connection: keep-alive\n"
2087     "Cache-control: max-age=10000\n"
2088     "Content-Length: 450\n",
2089 
2090     "HTTP/1.1 200 OK",
2091 
2092     "HTTP/1.1 200 OK\n"
2093     "connection: keep-alive\n"
2094     "Cache-control: max-age=10000\n"
2095     "Content-Length: 450\n"
2096   },
2097   { "HTTP/1.1 200 OK\n"
2098     "connection: keep-alive\n",
2099 
2100     "HTTP/1.1 304 Not Modified",
2101 
2102     "HTTP/1.1 304 Not Modified\n"
2103     "connection: keep-alive\n"
2104   },
2105   { "HTTP/1.1 200 OK\n"
2106     "connection: keep-alive  \n"
2107     "Content-Length  : 450   \n"
2108     "Cache-control: max-age=10000\n",
2109 
2110     "HTTP/1//1 304 Not Modified",
2111 
2112     "HTTP/1.0 304 Not Modified\n"
2113     "connection: keep-alive\n"
2114     "Content-Length: 450\n"
2115     "Cache-control: max-age=10000\n"
2116   },
2117 };
2118 
2119 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
2120                          ReplaceStatusTest,
2121                          testing::ValuesIn(replace_status_tests));
2122 
2123 struct UpdateWithNewRangeTestData {
2124   const char* orig_headers;
2125   const char* expected_headers;
2126   const char* expected_headers_with_replaced_status;
2127 };
2128 
2129 class UpdateWithNewRangeTest
2130     : public HttpResponseHeadersTest,
2131       public ::testing::WithParamInterface<UpdateWithNewRangeTestData> {
2132 };
2133 
TEST_P(UpdateWithNewRangeTest,UpdateWithNewRange)2134 TEST_P(UpdateWithNewRangeTest, UpdateWithNewRange) {
2135   const UpdateWithNewRangeTestData test = GetParam();
2136 
2137   const HttpByteRange range = HttpByteRange::Bounded(3, 5);
2138 
2139   std::string orig_headers(test.orig_headers);
2140   std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0');
2141   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(orig_headers + '\0');
2142   int64_t content_size = parsed->GetContentLength();
2143 
2144   // Update headers without replacing status line.
2145   parsed->UpdateWithNewRange(range, content_size, false);
2146   EXPECT_EQ(std::string(test.expected_headers), ToSimpleString(parsed));
2147 
2148   // Replace status line too.
2149   parsed->UpdateWithNewRange(range, content_size, true);
2150   EXPECT_EQ(std::string(test.expected_headers_with_replaced_status),
2151             ToSimpleString(parsed));
2152 }
2153 
2154 const UpdateWithNewRangeTestData update_range_tests[] = {
2155   { "HTTP/1.1 200 OK\n"
2156     "Content-Length: 450\n",
2157 
2158     "HTTP/1.1 200 OK\n"
2159     "Content-Range: bytes 3-5/450\n"
2160     "Content-Length: 3\n",
2161 
2162     "HTTP/1.1 206 Partial Content\n"
2163     "Content-Range: bytes 3-5/450\n"
2164     "Content-Length: 3\n",
2165   },
2166   { "HTTP/1.1 200 OK\n"
2167     "Content-Length: 5\n",
2168 
2169     "HTTP/1.1 200 OK\n"
2170     "Content-Range: bytes 3-5/5\n"
2171     "Content-Length: 3\n",
2172 
2173     "HTTP/1.1 206 Partial Content\n"
2174     "Content-Range: bytes 3-5/5\n"
2175     "Content-Length: 3\n",
2176   },
2177 };
2178 
2179 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
2180                          UpdateWithNewRangeTest,
2181                          testing::ValuesIn(update_range_tests));
2182 
TEST_F(HttpResponseHeadersCacheControlTest,AbsentMaxAgeReturnsFalse)2183 TEST_F(HttpResponseHeadersCacheControlTest, AbsentMaxAgeReturnsFalse) {
2184   InitializeHeadersWithCacheControl("nocache");
2185   EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2186 }
2187 
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithNoParameterRejected)2188 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithNoParameterRejected) {
2189   InitializeHeadersWithCacheControl("max-age=,private");
2190   EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2191 }
2192 
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithSpaceParameterRejected)2193 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithSpaceParameterRejected) {
2194   InitializeHeadersWithCacheControl("max-age= ,private");
2195   EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2196 }
2197 
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithInterimSpaceIsRejected)2198 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithInterimSpaceIsRejected) {
2199   InitializeHeadersWithCacheControl("max-age=1 2");
2200   EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2201 }
2202 
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithMinusSignIsRejected)2203 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithMinusSignIsRejected) {
2204   InitializeHeadersWithCacheControl("max-age=-7");
2205   EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2206 }
2207 
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithSpaceBeforeEqualsIsRejected)2208 TEST_F(HttpResponseHeadersCacheControlTest,
2209        MaxAgeWithSpaceBeforeEqualsIsRejected) {
2210   InitializeHeadersWithCacheControl("max-age = 7");
2211   EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2212 }
2213 
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeWithLeadingandTrailingSpaces)2214 TEST_F(HttpResponseHeadersCacheControlTest,
2215        MaxAgeWithLeadingandTrailingSpaces) {
2216   InitializeHeadersWithCacheControl("max-age= 7  ");
2217   EXPECT_EQ(base::Seconds(7), GetMaxAgeValue());
2218 }
2219 
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeFirstMatchUsed)2220 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeFirstMatchUsed) {
2221   InitializeHeadersWithCacheControl("max-age=10, max-age=20");
2222   EXPECT_EQ(base::Seconds(10), GetMaxAgeValue());
2223 }
2224 
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeBogusFirstMatchUsed)2225 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeBogusFirstMatchUsed) {
2226   // "max-age10" isn't parsed as "max-age"; "max-age=now" is bogus and
2227   // ignored and so "max-age=20" is used.
2228   InitializeHeadersWithCacheControl(
2229       "max-age10, max-age=now, max-age=20, max-age=30");
2230   EXPECT_EQ(base::Seconds(20), GetMaxAgeValue());
2231 }
2232 
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeCaseInsensitive)2233 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeCaseInsensitive) {
2234   InitializeHeadersWithCacheControl("Max-aGe=15");
2235   EXPECT_EQ(base::Seconds(15), GetMaxAgeValue());
2236 }
2237 
TEST_F(HttpResponseHeadersCacheControlTest,MaxAgeOverflow)2238 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeOverflow) {
2239   InitializeHeadersWithCacheControl("max-age=99999999999999999999");
2240   EXPECT_EQ(base::TimeDelta::FiniteMax().InSeconds(),
2241             GetMaxAgeValue().InSeconds());
2242 }
2243 
2244 struct MaxAgeTestData {
2245   const char* max_age_string;
2246   const absl::optional<int64_t> expected_seconds;
2247 };
2248 
2249 class MaxAgeEdgeCasesTest
2250     : public HttpResponseHeadersCacheControlTest,
2251       public ::testing::WithParamInterface<MaxAgeTestData> {
2252 };
2253 
TEST_P(MaxAgeEdgeCasesTest,MaxAgeEdgeCases)2254 TEST_P(MaxAgeEdgeCasesTest, MaxAgeEdgeCases) {
2255   const MaxAgeTestData test = GetParam();
2256 
2257   std::string max_age = "max-age=";
2258   InitializeHeadersWithCacheControl(
2259       (max_age + test.max_age_string).c_str());
2260   if (test.expected_seconds.has_value()) {
2261     EXPECT_EQ(test.expected_seconds.value(), GetMaxAgeValue().InSeconds())
2262         << " for max-age=" << test.max_age_string;
2263   } else {
2264     EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2265   }
2266 }
2267 
2268 const MaxAgeTestData max_age_tests[] = {
2269     {" 1 ", 1},  // Spaces are ignored.
2270     {"-1", absl::nullopt},
2271     {"--1", absl::nullopt},
2272     {"2s", absl::nullopt},
2273     {"3 days", absl::nullopt},
2274     {"'4'", absl::nullopt},
2275     {"\"5\"", absl::nullopt},
2276     {"0x6", absl::nullopt},  // Hex not parsed as hex.
2277     {"7F", absl::nullopt},   // Hex without 0x still not parsed as hex.
2278     {"010", 10},             // Octal not parsed as octal.
2279     {"9223372036853", 9223372036853},
2280     {"9223372036854", 9223372036854},
2281     {"9223372036855", 9223372036854},
2282     {"9223372036854775806", 9223372036854},
2283     {"9223372036854775807", 9223372036854},
2284     {"20000000000000000000", 9223372036854},  // Overflow int64_t.
2285 };
2286 
2287 INSTANTIATE_TEST_SUITE_P(HttpResponseHeadersCacheControl,
2288                          MaxAgeEdgeCasesTest,
2289                          testing::ValuesIn(max_age_tests));
2290 
TEST_F(HttpResponseHeadersCacheControlTest,AbsentStaleWhileRevalidateReturnsFalse)2291 TEST_F(HttpResponseHeadersCacheControlTest,
2292        AbsentStaleWhileRevalidateReturnsFalse) {
2293   InitializeHeadersWithCacheControl("max-age=3600");
2294   EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2295 }
2296 
TEST_F(HttpResponseHeadersCacheControlTest,StaleWhileRevalidateWithoutValueRejected)2297 TEST_F(HttpResponseHeadersCacheControlTest,
2298        StaleWhileRevalidateWithoutValueRejected) {
2299   InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=");
2300   EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2301 }
2302 
TEST_F(HttpResponseHeadersCacheControlTest,StaleWhileRevalidateWithInvalidValueIgnored)2303 TEST_F(HttpResponseHeadersCacheControlTest,
2304        StaleWhileRevalidateWithInvalidValueIgnored) {
2305   InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=true");
2306   EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2307 }
2308 
TEST_F(HttpResponseHeadersCacheControlTest,StaleWhileRevalidateValueReturned)2309 TEST_F(HttpResponseHeadersCacheControlTest, StaleWhileRevalidateValueReturned) {
2310   InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=7200");
2311   EXPECT_EQ(base::Seconds(7200), GetStaleWhileRevalidateValue());
2312 }
2313 
TEST_F(HttpResponseHeadersCacheControlTest,FirstStaleWhileRevalidateValueUsed)2314 TEST_F(HttpResponseHeadersCacheControlTest,
2315        FirstStaleWhileRevalidateValueUsed) {
2316   InitializeHeadersWithCacheControl(
2317       "stale-while-revalidate=1,stale-while-revalidate=7200");
2318   EXPECT_EQ(base::Seconds(1), GetStaleWhileRevalidateValue());
2319 }
2320 
2321 struct GetCurrentAgeTestData {
2322   const char* headers;
2323   const char* request_time;
2324   const char* response_time;
2325   const char* current_time;
2326   const int expected_age;
2327 };
2328 
2329 class GetCurrentAgeTest
2330     : public HttpResponseHeadersTest,
2331       public ::testing::WithParamInterface<GetCurrentAgeTestData> {
2332 };
2333 
TEST_P(GetCurrentAgeTest,GetCurrentAge)2334 TEST_P(GetCurrentAgeTest, GetCurrentAge) {
2335   const GetCurrentAgeTestData test = GetParam();
2336 
2337   base::Time request_time, response_time, current_time;
2338   ASSERT_TRUE(base::Time::FromString(test.request_time, &request_time));
2339   ASSERT_TRUE(base::Time::FromString(test.response_time, &response_time));
2340   ASSERT_TRUE(base::Time::FromString(test.current_time, &current_time));
2341 
2342   std::string headers(test.headers);
2343   HeadersToRaw(&headers);
2344   auto parsed = base::MakeRefCounted<HttpResponseHeaders>(headers);
2345 
2346   base::TimeDelta age =
2347       parsed->GetCurrentAge(request_time, response_time, current_time);
2348   EXPECT_EQ(test.expected_age, age.InSeconds());
2349 }
2350 
2351 const struct GetCurrentAgeTestData get_current_age_tests[] = {
2352     // Without Date header.
2353     {"HTTP/1.1 200 OK\n"
2354      "Age: 2",
2355      "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
2356      "Fri, 20 Jan 2011 10:40:14 GMT", 8},
2357     // Without Age header.
2358     {"HTTP/1.1 200 OK\n"
2359      "Date: Fri, 20 Jan 2011 10:40:10 GMT\n",
2360      "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
2361      "Fri, 20 Jan 2011 10:40:14 GMT", 6},
2362     // date_value > response_time with Age header.
2363     {"HTTP/1.1 200 OK\n"
2364      "Date: Fri, 20 Jan 2011 10:40:14 GMT\n"
2365      "Age: 2\n",
2366      "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
2367      "Fri, 20 Jan 2011 10:40:14 GMT", 8},
2368      // date_value > response_time without Age header.
2369      {"HTTP/1.1 200 OK\n"
2370      "Date: Fri, 20 Jan 2011 10:40:14 GMT\n",
2371      "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
2372      "Fri, 20 Jan 2011 10:40:14 GMT", 6},
2373     // apparent_age > corrected_age_value
2374     {"HTTP/1.1 200 OK\n"
2375      "Date: Fri, 20 Jan 2011 10:40:07 GMT\n"
2376      "Age: 0\n",
2377      "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
2378      "Fri, 20 Jan 2011 10:40:14 GMT", 7}};
2379 
2380 INSTANTIATE_TEST_SUITE_P(HttpResponseHeaders,
2381                          GetCurrentAgeTest,
2382                          testing::ValuesIn(get_current_age_tests));
2383 
2384 }  // namespace
2385 
2386 }  // namespace net
2387