Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / net / http / http_response_headers_unittest.cc
1 // Copyright (c) 2012 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 <algorithm>
6 #include <limits>
7
8 #include "base/basictypes.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/pickle.h"
11 #include "base/time/time.h"
12 #include "base/values.h"
13 #include "net/http/http_byte_range.h"
14 #include "net/http/http_response_headers.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace {
18
19 struct TestData {
20   const char* raw_headers;
21   const char* expected_headers;
22   int expected_response_code;
23   net::HttpVersion expected_parsed_version;
24   net::HttpVersion expected_version;
25 };
26
27 struct ContentTypeTestData {
28   const std::string raw_headers;
29   const std::string mime_type;
30   const bool has_mimetype;
31   const std::string charset;
32   const bool has_charset;
33   const std::string all_content_type;
34 };
35
36 class HttpResponseHeadersTest : public testing::Test {
37 };
38
39 // Transform "normal"-looking headers (\n-separated) to the appropriate
40 // input format for ParseRawHeaders (\0-separated).
41 void HeadersToRaw(std::string* headers) {
42   std::replace(headers->begin(), headers->end(), '\n', '\0');
43   if (!headers->empty())
44     *headers += '\0';
45 }
46
47 class HttpResponseHeadersCacheControlTest : public HttpResponseHeadersTest {
48  protected:
49   // Make tests less verbose.
50   typedef base::TimeDelta TimeDelta;
51
52   // Initilise the headers() value with a Cache-Control header set to
53   // |cache_control|. |cache_control| is copied and so can safely be a
54   // temporary.
55   void InitializeHeadersWithCacheControl(const char* cache_control) {
56     std::string raw_headers("HTTP/1.1 200 OK\n");
57     raw_headers += "Cache-Control: ";
58     raw_headers += cache_control;
59     raw_headers += "\n";
60     HeadersToRaw(&raw_headers);
61     headers_ = new net::HttpResponseHeaders(raw_headers);
62   }
63
64   const scoped_refptr<net::HttpResponseHeaders>& headers() { return headers_; }
65
66   // Return a pointer to a TimeDelta object. For use when the value doesn't
67   // matter.
68   TimeDelta* TimeDeltaPointer() { return &delta_; }
69
70   // Get the max-age value. This should only be used in tests where a valid
71   // max-age parameter is expected to be present.
72   TimeDelta GetMaxAgeValue() {
73     DCHECK(headers_) << "Call InitializeHeadersWithCacheControl() first";
74     TimeDelta max_age_value;
75     EXPECT_TRUE(headers()->GetMaxAgeValue(&max_age_value));
76     return max_age_value;
77   }
78
79   // Get the stale-while-revalidate value. This should only be used in tests
80   // where a valid max-age parameter is expected to be present.
81   TimeDelta GetStaleWhileRevalidateValue() {
82     DCHECK(headers_) << "Call InitializeHeadersWithCacheControl() first";
83     TimeDelta stale_while_revalidate_value;
84     EXPECT_TRUE(
85         headers()->GetStaleWhileRevalidateValue(&stale_while_revalidate_value));
86     return stale_while_revalidate_value;
87   }
88
89  private:
90   scoped_refptr<net::HttpResponseHeaders> headers_;
91   TimeDelta delta_;
92 };
93
94 void TestCommon(const TestData& test) {
95   std::string raw_headers(test.raw_headers);
96   HeadersToRaw(&raw_headers);
97   std::string expected_headers(test.expected_headers);
98
99   std::string headers;
100   scoped_refptr<net::HttpResponseHeaders> parsed(
101       new net::HttpResponseHeaders(raw_headers));
102   parsed->GetNormalizedHeaders(&headers);
103
104   // Transform to readable output format (so it's easier to see diffs).
105   std::replace(headers.begin(), headers.end(), ' ', '_');
106   std::replace(headers.begin(), headers.end(), '\n', '\\');
107   std::replace(expected_headers.begin(), expected_headers.end(), ' ', '_');
108   std::replace(expected_headers.begin(), expected_headers.end(), '\n', '\\');
109
110   EXPECT_EQ(expected_headers, headers);
111
112   EXPECT_EQ(test.expected_response_code, parsed->response_code());
113
114   EXPECT_TRUE(test.expected_parsed_version == parsed->GetParsedHttpVersion());
115   EXPECT_TRUE(test.expected_version == parsed->GetHttpVersion());
116 }
117
118 } // end namespace
119
120 // Check that we normalize headers properly.
121 TEST(HttpResponseHeadersTest, NormalizeHeadersWhitespace) {
122   TestData test = {
123     "HTTP/1.1    202   Accepted  \n"
124     "Content-TYPE  : text/html; charset=utf-8  \n"
125     "Set-Cookie: a \n"
126     "Set-Cookie:   b \n",
127
128     "HTTP/1.1 202 Accepted\n"
129     "Content-TYPE: text/html; charset=utf-8\n"
130     "Set-Cookie: a, b\n",
131
132     202,
133     net::HttpVersion(1,1),
134     net::HttpVersion(1,1)
135   };
136   TestCommon(test);
137 }
138
139 // Check that we normalize headers properly (header name is invalid if starts
140 // with LWS).
141 TEST(HttpResponseHeadersTest, NormalizeHeadersLeadingWhitespace) {
142   TestData test = {
143     "HTTP/1.1    202   Accepted  \n"
144     // Starts with space -- will be skipped as invalid.
145     "  Content-TYPE  : text/html; charset=utf-8  \n"
146     "Set-Cookie: a \n"
147     "Set-Cookie:   b \n",
148
149     "HTTP/1.1 202 Accepted\n"
150     "Set-Cookie: a, b\n",
151
152     202,
153     net::HttpVersion(1,1),
154     net::HttpVersion(1,1)
155   };
156   TestCommon(test);
157 }
158
159 TEST(HttpResponseHeadersTest, BlankHeaders) {
160   TestData test = {
161     "HTTP/1.1 200 OK\n"
162     "Header1 :          \n"
163     "Header2: \n"
164     "Header3:\n"
165     "Header4\n"
166     "Header5    :\n",
167
168     "HTTP/1.1 200 OK\n"
169     "Header1: \n"
170     "Header2: \n"
171     "Header3: \n"
172     "Header5: \n",
173
174     200,
175     net::HttpVersion(1,1),
176     net::HttpVersion(1,1)
177   };
178   TestCommon(test);
179 }
180
181 TEST(HttpResponseHeadersTest, NormalizeHeadersVersion) {
182   // Don't believe the http/0.9 version if there are headers!
183   TestData test = {
184     "hTtP/0.9 201\n"
185     "Content-TYPE: text/html; charset=utf-8\n",
186
187     "HTTP/1.0 201 OK\n"
188     "Content-TYPE: text/html; charset=utf-8\n",
189
190     201,
191     net::HttpVersion(0,9),
192     net::HttpVersion(1,0)
193   };
194   TestCommon(test);
195 }
196
197 TEST(HttpResponseHeadersTest, PreserveHttp09) {
198   // Accept the HTTP/0.9 version number if there are no headers.
199   // This is how HTTP/0.9 responses get constructed from HttpNetworkTransaction.
200   TestData test = {
201     "hTtP/0.9 200 OK\n",
202
203     "HTTP/0.9 200 OK\n",
204
205     200,
206     net::HttpVersion(0,9),
207     net::HttpVersion(0,9)
208   };
209   TestCommon(test);
210 }
211
212 TEST(HttpResponseHeadersTest, NormalizeHeadersMissingOK) {
213   TestData test = {
214     "HTTP/1.1 201\n"
215     "Content-TYPE: text/html; charset=utf-8\n",
216
217     "HTTP/1.1 201 OK\n"
218     "Content-TYPE: text/html; charset=utf-8\n",
219
220     201,
221     net::HttpVersion(1,1),
222     net::HttpVersion(1,1)
223   };
224   TestCommon(test);
225 }
226
227 TEST(HttpResponseHeadersTest, NormalizeHeadersBadStatus) {
228   TestData test = {
229     "SCREWED_UP_STATUS_LINE\n"
230     "Content-TYPE: text/html; charset=utf-8\n",
231
232     "HTTP/1.0 200 OK\n"
233     "Content-TYPE: text/html; charset=utf-8\n",
234
235     200,
236     net::HttpVersion(0,0), // Parse error
237     net::HttpVersion(1,0)
238   };
239   TestCommon(test);
240 }
241
242 TEST(HttpResponseHeadersTest, NormalizeHeadersInvalidStatusCode) {
243   TestData test = {
244     "HTTP/1.1 -1  Unknown\n",
245
246     "HTTP/1.1 200 OK\n",
247
248     200,
249     net::HttpVersion(1,1),
250     net::HttpVersion(1,1)
251   };
252   TestCommon(test);
253 }
254
255 TEST(HttpResponseHeadersTest, NormalizeHeadersEmpty) {
256   TestData test = {
257     "",
258
259     "HTTP/1.0 200 OK\n",
260
261     200,
262     net::HttpVersion(0,0), // Parse Error
263     net::HttpVersion(1,0)
264   };
265   TestCommon(test);
266 }
267
268 TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColon) {
269   TestData test = {
270     "HTTP/1.1    202   Accepted  \n"
271     "foo: bar\n"
272     ": a \n"
273     " : b\n"
274     "baz: blat \n",
275
276     "HTTP/1.1 202 Accepted\n"
277     "foo: bar\n"
278     "baz: blat\n",
279
280     202,
281     net::HttpVersion(1,1),
282     net::HttpVersion(1,1)
283   };
284   TestCommon(test);
285 }
286
287 TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColonAtEOL) {
288   TestData test = {
289     "HTTP/1.1    202   Accepted  \n"
290     "foo:   \n"
291     "bar:\n"
292     "baz: blat \n"
293     "zip:\n",
294
295     "HTTP/1.1 202 Accepted\n"
296     "foo: \n"
297     "bar: \n"
298     "baz: blat\n"
299     "zip: \n",
300
301     202,
302     net::HttpVersion(1,1),
303     net::HttpVersion(1,1)
304   };
305   TestCommon(test);
306 }
307
308 TEST(HttpResponseHeadersTest, NormalizeHeadersOfWhitepace) {
309   TestData test = {
310     "\n   \n",
311
312     "HTTP/1.0 200 OK\n",
313
314     200,
315     net::HttpVersion(0,0),  // Parse error
316     net::HttpVersion(1,0)
317   };
318   TestCommon(test);
319 }
320
321 TEST(HttpResponseHeadersTest, RepeatedSetCookie) {
322   TestData test = {
323     "HTTP/1.1 200 OK\n"
324     "Set-Cookie: x=1\n"
325     "Set-Cookie: y=2\n",
326
327     "HTTP/1.1 200 OK\n"
328     "Set-Cookie: x=1, y=2\n",
329
330     200,
331     net::HttpVersion(1,1),
332     net::HttpVersion(1,1)
333   };
334   TestCommon(test);
335 }
336
337 TEST(HttpResponseHeadersTest, GetNormalizedHeader) {
338   std::string headers =
339       "HTTP/1.1 200 OK\n"
340       "Cache-control: private\n"
341       "cache-Control: no-store\n";
342   HeadersToRaw(&headers);
343   scoped_refptr<net::HttpResponseHeaders> parsed(
344       new net::HttpResponseHeaders(headers));
345
346   std::string value;
347   EXPECT_TRUE(parsed->GetNormalizedHeader("cache-control", &value));
348   EXPECT_EQ("private, no-store", value);
349 }
350
351 TEST(HttpResponseHeadersTest, Persist) {
352   const struct {
353     net::HttpResponseHeaders::PersistOptions options;
354     const char* raw_headers;
355     const char* expected_headers;
356   } tests[] = {
357     { net::HttpResponseHeaders::PERSIST_ALL,
358       "HTTP/1.1 200 OK\n"
359       "Cache-control:private\n"
360       "cache-Control:no-store\n",
361
362       "HTTP/1.1 200 OK\n"
363       "Cache-control: private, no-store\n"
364     },
365     { net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
366       "HTTP/1.1 200 OK\n"
367       "connection: keep-alive\n"
368       "server: blah\n",
369
370       "HTTP/1.1 200 OK\n"
371       "server: blah\n"
372     },
373     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
374       net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
375       "HTTP/1.1 200 OK\n"
376       "fOo: 1\n"
377       "Foo: 2\n"
378       "Transfer-Encoding: chunked\n"
379       "CoNnection: keep-alive\n"
380       "cache-control: private, no-cache=\"foo\"\n",
381
382       "HTTP/1.1 200 OK\n"
383       "cache-control: private, no-cache=\"foo\"\n"
384     },
385     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
386       "HTTP/1.1 200 OK\n"
387       "Foo: 2\n"
388       "Cache-Control: private,no-cache=\"foo, bar\"\n"
389       "bar",
390
391       "HTTP/1.1 200 OK\n"
392       "Cache-Control: private,no-cache=\"foo, bar\"\n"
393     },
394     // ignore bogus no-cache value
395     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
396       "HTTP/1.1 200 OK\n"
397       "Foo: 2\n"
398       "Cache-Control: private,no-cache=foo\n",
399
400       "HTTP/1.1 200 OK\n"
401       "Foo: 2\n"
402       "Cache-Control: private,no-cache=foo\n"
403     },
404     // ignore bogus no-cache value
405     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
406       "HTTP/1.1 200 OK\n"
407       "Foo: 2\n"
408       "Cache-Control: private, no-cache=\n",
409
410       "HTTP/1.1 200 OK\n"
411       "Foo: 2\n"
412       "Cache-Control: private, no-cache=\n"
413     },
414     // ignore empty no-cache value
415     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
416       "HTTP/1.1 200 OK\n"
417       "Foo: 2\n"
418       "Cache-Control: private, no-cache=\"\"\n",
419
420       "HTTP/1.1 200 OK\n"
421       "Foo: 2\n"
422       "Cache-Control: private, no-cache=\"\"\n"
423     },
424     // ignore wrong quotes no-cache value
425     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
426       "HTTP/1.1 200 OK\n"
427       "Foo: 2\n"
428       "Cache-Control: private, no-cache=\'foo\'\n",
429
430       "HTTP/1.1 200 OK\n"
431       "Foo: 2\n"
432       "Cache-Control: private, no-cache=\'foo\'\n"
433     },
434     // ignore unterminated quotes no-cache value
435     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
436       "HTTP/1.1 200 OK\n"
437       "Foo: 2\n"
438       "Cache-Control: private, no-cache=\"foo\n",
439
440       "HTTP/1.1 200 OK\n"
441       "Foo: 2\n"
442       "Cache-Control: private, no-cache=\"foo\n"
443     },
444     // accept sloppy LWS
445     { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
446       "HTTP/1.1 200 OK\n"
447       "Foo: 2\n"
448       "Cache-Control: private, no-cache=\" foo\t, bar\"\n",
449
450       "HTTP/1.1 200 OK\n"
451       "Cache-Control: private, no-cache=\" foo\t, bar\"\n"
452     },
453     // header name appears twice, separated by another header
454     { net::HttpResponseHeaders::PERSIST_ALL,
455       "HTTP/1.1 200 OK\n"
456       "Foo: 1\n"
457       "Bar: 2\n"
458       "Foo: 3\n",
459
460       "HTTP/1.1 200 OK\n"
461       "Foo: 1, 3\n"
462       "Bar: 2\n"
463     },
464     // header name appears twice, separated by another header (type 2)
465     { net::HttpResponseHeaders::PERSIST_ALL,
466       "HTTP/1.1 200 OK\n"
467       "Foo: 1, 3\n"
468       "Bar: 2\n"
469       "Foo: 4\n",
470
471       "HTTP/1.1 200 OK\n"
472       "Foo: 1, 3, 4\n"
473       "Bar: 2\n"
474     },
475     // Test filtering of cookie headers.
476     { net::HttpResponseHeaders::PERSIST_SANS_COOKIES,
477       "HTTP/1.1 200 OK\n"
478       "Set-Cookie: foo=bar; httponly\n"
479       "Set-Cookie: bar=foo\n"
480       "Bar: 1\n"
481       "Set-Cookie2: bar2=foo2\n",
482
483       "HTTP/1.1 200 OK\n"
484       "Bar: 1\n"
485     },
486     // Test LWS at the end of a header.
487     { net::HttpResponseHeaders::PERSIST_ALL,
488       "HTTP/1.1 200 OK\n"
489       "Content-Length: 450   \n"
490       "Content-Encoding: gzip\n",
491
492       "HTTP/1.1 200 OK\n"
493       "Content-Length: 450\n"
494       "Content-Encoding: gzip\n"
495     },
496     // Test LWS at the end of a header.
497     { net::HttpResponseHeaders::PERSIST_RAW,
498       "HTTP/1.1 200 OK\n"
499       "Content-Length: 450   \n"
500       "Content-Encoding: gzip\n",
501
502       "HTTP/1.1 200 OK\n"
503       "Content-Length: 450\n"
504       "Content-Encoding: gzip\n"
505     },
506     // Test filtering of transport security state headers.
507     { net::HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE,
508       "HTTP/1.1 200 OK\n"
509       "Strict-Transport-Security: max-age=1576800\n"
510       "Bar: 1\n"
511       "Public-Key-Pins: max-age=100000; "
512           "pin-sha1=\"ObT42aoSpAqWdY9WfRfL7i0HsVk=\";"
513           "pin-sha1=\"7kW49EVwZG0hSNx41ZO/fUPN0ek=\"",
514
515       "HTTP/1.1 200 OK\n"
516       "Bar: 1\n"
517     },
518   };
519
520   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
521     std::string headers = tests[i].raw_headers;
522     HeadersToRaw(&headers);
523     scoped_refptr<net::HttpResponseHeaders> parsed1(
524         new net::HttpResponseHeaders(headers));
525
526     Pickle pickle;
527     parsed1->Persist(&pickle, tests[i].options);
528
529     PickleIterator iter(pickle);
530     scoped_refptr<net::HttpResponseHeaders> parsed2(
531         new net::HttpResponseHeaders(pickle, &iter));
532
533     std::string h2;
534     parsed2->GetNormalizedHeaders(&h2);
535     EXPECT_EQ(std::string(tests[i].expected_headers), h2);
536   }
537 }
538
539 TEST(HttpResponseHeadersTest, EnumerateHeader_Coalesced) {
540   // Ensure that commas in quoted strings are not regarded as value separators.
541   // Ensure that whitespace following a value is trimmed properly
542   std::string headers =
543       "HTTP/1.1 200 OK\n"
544       "Cache-control:private , no-cache=\"set-cookie,server\" \n"
545       "cache-Control: no-store\n";
546   HeadersToRaw(&headers);
547   scoped_refptr<net::HttpResponseHeaders> parsed(
548       new net::HttpResponseHeaders(headers));
549
550   void* iter = NULL;
551   std::string value;
552   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
553   EXPECT_EQ("private", value);
554   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
555   EXPECT_EQ("no-cache=\"set-cookie,server\"", value);
556   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
557   EXPECT_EQ("no-store", value);
558   EXPECT_FALSE(parsed->EnumerateHeader(&iter, "cache-control", &value));
559 }
560
561 TEST(HttpResponseHeadersTest, EnumerateHeader_Challenge) {
562   // Even though WWW-Authenticate has commas, it should not be treated as
563   // coalesced values.
564   std::string headers =
565       "HTTP/1.1 401 OK\n"
566       "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n"
567       "WWW-Authenticate:Basic realm=quatar\n";
568   HeadersToRaw(&headers);
569   scoped_refptr<net::HttpResponseHeaders> parsed(
570       new net::HttpResponseHeaders(headers));
571
572   void* iter = NULL;
573   std::string value;
574   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
575   EXPECT_EQ("Digest realm=foobar, nonce=x, domain=y", value);
576   EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
577   EXPECT_EQ("Basic realm=quatar", value);
578   EXPECT_FALSE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
579 }
580
581 TEST(HttpResponseHeadersTest, EnumerateHeader_DateValued) {
582   // The comma in a date valued header should not be treated as a
583   // field-value separator
584   std::string headers =
585       "HTTP/1.1 200 OK\n"
586       "Date: Tue, 07 Aug 2007 23:10:55 GMT\n"
587       "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n";
588   HeadersToRaw(&headers);
589   scoped_refptr<net::HttpResponseHeaders> parsed(
590       new net::HttpResponseHeaders(headers));
591
592   std::string value;
593   EXPECT_TRUE(parsed->EnumerateHeader(NULL, "date", &value));
594   EXPECT_EQ("Tue, 07 Aug 2007 23:10:55 GMT", value);
595   EXPECT_TRUE(parsed->EnumerateHeader(NULL, "last-modified", &value));
596   EXPECT_EQ("Wed, 01 Aug 2007 23:23:45 GMT", value);
597 }
598
599 TEST(HttpResponseHeadersTest, DefaultDateToGMT) {
600   // Verify we make the best interpretation when parsing dates that incorrectly
601   // do not end in "GMT" as RFC2616 requires.
602   std::string headers =
603       "HTTP/1.1 200 OK\n"
604       "Date: Tue, 07 Aug 2007 23:10:55\n"
605       "Last-Modified: Tue, 07 Aug 2007 19:10:55 EDT\n"
606       "Expires: Tue, 07 Aug 2007 23:10:55 UTC\n";
607   HeadersToRaw(&headers);
608   scoped_refptr<net::HttpResponseHeaders> parsed(
609       new net::HttpResponseHeaders(headers));
610   base::Time expected_value;
611   ASSERT_TRUE(base::Time::FromString("Tue, 07 Aug 2007 23:10:55 GMT",
612                                      &expected_value));
613
614   base::Time value;
615   // When the timezone is missing, GMT is a good guess as its what RFC2616
616   // requires.
617   EXPECT_TRUE(parsed->GetDateValue(&value));
618   EXPECT_EQ(expected_value, value);
619   // If GMT is missing but an RFC822-conforming one is present, use that.
620   EXPECT_TRUE(parsed->GetLastModifiedValue(&value));
621   EXPECT_EQ(expected_value, value);
622   // If an unknown timezone is present, treat like a missing timezone and
623   // default to GMT.  The only example of a web server not specifying "GMT"
624   // used "UTC" which is equivalent to GMT.
625   if (parsed->GetExpiresValue(&value))
626     EXPECT_EQ(expected_value, value);
627 }
628
629 TEST(HttpResponseHeadersTest, GetMimeType) {
630   const ContentTypeTestData tests[] = {
631     { "HTTP/1.1 200 OK\n"
632       "Content-type: text/html\n",
633       "text/html", true,
634       "", false,
635       "text/html" },
636     // Multiple content-type headers should give us the last one.
637     { "HTTP/1.1 200 OK\n"
638       "Content-type: text/html\n"
639       "Content-type: text/html\n",
640       "text/html", true,
641       "", false,
642       "text/html, text/html" },
643     { "HTTP/1.1 200 OK\n"
644       "Content-type: text/plain\n"
645       "Content-type: text/html\n"
646       "Content-type: text/plain\n"
647       "Content-type: text/html\n",
648       "text/html", true,
649       "", false,
650       "text/plain, text/html, text/plain, text/html" },
651     // Test charset parsing.
652     { "HTTP/1.1 200 OK\n"
653       "Content-type: text/html\n"
654       "Content-type: text/html; charset=ISO-8859-1\n",
655       "text/html", true,
656       "iso-8859-1", true,
657       "text/html, text/html; charset=ISO-8859-1" },
658     // Test charset in double quotes.
659     { "HTTP/1.1 200 OK\n"
660       "Content-type: text/html\n"
661       "Content-type: text/html; charset=\"ISO-8859-1\"\n",
662       "text/html", true,
663       "iso-8859-1", true,
664       "text/html, text/html; charset=\"ISO-8859-1\"" },
665     // If there are multiple matching content-type headers, we carry
666     // over the charset value.
667     { "HTTP/1.1 200 OK\n"
668       "Content-type: text/html;charset=utf-8\n"
669       "Content-type: text/html\n",
670       "text/html", true,
671       "utf-8", true,
672       "text/html;charset=utf-8, text/html" },
673     // Test single quotes.
674     { "HTTP/1.1 200 OK\n"
675       "Content-type: text/html;charset='utf-8'\n"
676       "Content-type: text/html\n",
677       "text/html", true,
678       "utf-8", true,
679       "text/html;charset='utf-8', text/html" },
680     // Last charset wins if matching content-type.
681     { "HTTP/1.1 200 OK\n"
682       "Content-type: text/html;charset=utf-8\n"
683       "Content-type: text/html;charset=iso-8859-1\n",
684       "text/html", true,
685       "iso-8859-1", true,
686       "text/html;charset=utf-8, text/html;charset=iso-8859-1" },
687     // Charset is ignored if the content types change.
688     { "HTTP/1.1 200 OK\n"
689       "Content-type: text/plain;charset=utf-8\n"
690       "Content-type: text/html\n",
691       "text/html", true,
692       "", false,
693       "text/plain;charset=utf-8, text/html" },
694     // Empty content-type
695     { "HTTP/1.1 200 OK\n"
696       "Content-type: \n",
697       "", false,
698       "", false,
699       "" },
700     // Emtpy charset
701     { "HTTP/1.1 200 OK\n"
702       "Content-type: text/html;charset=\n",
703       "text/html", true,
704       "", false,
705       "text/html;charset=" },
706     // Multiple charsets, last one wins.
707     { "HTTP/1.1 200 OK\n"
708       "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n",
709       "text/html", true,
710       "iso-8859-1", true,
711       "text/html;charset=utf-8; charset=iso-8859-1" },
712     // Multiple params.
713     { "HTTP/1.1 200 OK\n"
714       "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n",
715       "text/html", true,
716       "iso-8859-1", true,
717       "text/html; foo=utf-8; charset=iso-8859-1" },
718     { "HTTP/1.1 200 OK\n"
719       "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n",
720       "text/html", true,
721       "utf-8", true,
722       "text/html ; charset=utf-8 ; bar=iso-8859-1" },
723     // Comma embeded in quotes.
724     { "HTTP/1.1 200 OK\n"
725       "Content-type: text/html ; charset='utf-8,text/plain' ;\n",
726       "text/html", true,
727       "utf-8,text/plain", true,
728       "text/html ; charset='utf-8,text/plain' ;" },
729     // Charset with leading spaces.
730     { "HTTP/1.1 200 OK\n"
731       "Content-type: text/html ; charset= 'utf-8' ;\n",
732       "text/html", true,
733       "utf-8", true,
734       "text/html ; charset= 'utf-8' ;" },
735     // Media type comments in mime-type.
736     { "HTTP/1.1 200 OK\n"
737       "Content-type: text/html (html)\n",
738       "text/html", true,
739       "", false,
740       "text/html (html)" },
741     // Incomplete charset= param
742     { "HTTP/1.1 200 OK\n"
743       "Content-type: text/html; char=\n",
744       "text/html", true,
745       "", false,
746       "text/html; char=" },
747     // Invalid media type: no slash
748     { "HTTP/1.1 200 OK\n"
749       "Content-type: texthtml\n",
750       "", false,
751       "", false,
752       "texthtml" },
753     // Invalid media type: */*
754     { "HTTP/1.1 200 OK\n"
755       "Content-type: */*\n",
756       "", false,
757       "", false,
758       "*/*" },
759   };
760
761   for (size_t i = 0; i < arraysize(tests); ++i) {
762     std::string headers(tests[i].raw_headers);
763     HeadersToRaw(&headers);
764     scoped_refptr<net::HttpResponseHeaders> parsed(
765         new net::HttpResponseHeaders(headers));
766
767     std::string value;
768     EXPECT_EQ(tests[i].has_mimetype, parsed->GetMimeType(&value));
769     EXPECT_EQ(tests[i].mime_type, value);
770     value.clear();
771     EXPECT_EQ(tests[i].has_charset, parsed->GetCharset(&value));
772     EXPECT_EQ(tests[i].charset, value);
773     EXPECT_TRUE(parsed->GetNormalizedHeader("content-type", &value));
774     EXPECT_EQ(tests[i].all_content_type, value);
775   }
776 }
777
778 TEST(HttpResponseHeadersTest, RequiresValidation) {
779   const struct {
780     const char* headers;
781     bool requires_validation;
782   } tests[] = {
783     // no expiry info: expires immediately
784     { "HTTP/1.1 200 OK\n"
785       "\n",
786       true
787     },
788     // valid for a little while
789     { "HTTP/1.1 200 OK\n"
790       "cache-control: max-age=10000\n"
791       "\n",
792       false
793     },
794     // expires in the future
795     { "HTTP/1.1 200 OK\n"
796       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
797       "expires: Wed, 28 Nov 2007 01:00:00 GMT\n"
798       "\n",
799       false
800     },
801     // expired already
802     { "HTTP/1.1 200 OK\n"
803       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
804       "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
805       "\n",
806       true
807     },
808     // max-age trumps expires
809     { "HTTP/1.1 200 OK\n"
810       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
811       "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
812       "cache-control: max-age=10000\n"
813       "\n",
814       false
815     },
816     // last-modified heuristic: modified a while ago
817     { "HTTP/1.1 200 OK\n"
818       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
819       "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
820       "\n",
821       false
822     },
823     { "HTTP/1.1 203 Non-Authoritative Information\n"
824       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
825       "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
826       "\n",
827       false
828     },
829     { "HTTP/1.1 206 Partial Content\n"
830       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
831       "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
832       "\n",
833       false
834     },
835     // last-modified heuristic: modified recently
836     { "HTTP/1.1 200 OK\n"
837       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
838       "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
839       "\n",
840       true
841     },
842     { "HTTP/1.1 203 Non-Authoritative Information\n"
843       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
844       "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
845       "\n",
846       true
847     },
848     { "HTTP/1.1 206 Partial Content\n"
849       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
850       "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
851       "\n",
852       true
853     },
854     // cached permanent redirect
855     { "HTTP/1.1 301 Moved Permanently\n"
856       "\n",
857       false
858     },
859     // another cached permanent redirect
860     { "HTTP/1.1 308 Permanent Redirect\n"
861       "\n",
862       false
863     },
864     // cached redirect: not reusable even though by default it would be
865     { "HTTP/1.1 300 Multiple Choices\n"
866       "Cache-Control: no-cache\n"
867       "\n",
868       true
869     },
870     // cached forever by default
871     { "HTTP/1.1 410 Gone\n"
872       "\n",
873       false
874     },
875     // cached temporary redirect: not reusable
876     { "HTTP/1.1 302 Found\n"
877       "\n",
878       true
879     },
880     // cached temporary redirect: reusable
881     { "HTTP/1.1 302 Found\n"
882       "cache-control: max-age=10000\n"
883       "\n",
884       false
885     },
886     // cache-control: max-age=N overrides expires: date in the past
887     { "HTTP/1.1 200 OK\n"
888       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
889       "expires: Wed, 28 Nov 2007 00:20:11 GMT\n"
890       "cache-control: max-age=10000\n"
891       "\n",
892       false
893     },
894     // cache-control: no-store overrides expires: in the future
895     { "HTTP/1.1 200 OK\n"
896       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
897       "expires: Wed, 29 Nov 2007 00:40:11 GMT\n"
898       "cache-control: no-store,private,no-cache=\"foo\"\n"
899       "\n",
900       true
901     },
902     // pragma: no-cache overrides last-modified heuristic
903     { "HTTP/1.1 200 OK\n"
904       "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
905       "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
906       "pragma: no-cache\n"
907       "\n",
908       true
909     },
910     // TODO(darin): add many many more tests here
911   };
912   base::Time request_time, response_time, current_time;
913   base::Time::FromString("Wed, 28 Nov 2007 00:40:09 GMT", &request_time);
914   base::Time::FromString("Wed, 28 Nov 2007 00:40:12 GMT", &response_time);
915   base::Time::FromString("Wed, 28 Nov 2007 00:45:20 GMT", &current_time);
916
917   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
918     std::string headers(tests[i].headers);
919     HeadersToRaw(&headers);
920     scoped_refptr<net::HttpResponseHeaders> parsed(
921         new net::HttpResponseHeaders(headers));
922
923     bool requires_validation =
924         parsed->RequiresValidation(request_time, response_time, current_time);
925     EXPECT_EQ(tests[i].requires_validation, requires_validation);
926   }
927 }
928
929 TEST(HttpResponseHeadersTest, Update) {
930   const struct {
931     const char* orig_headers;
932     const char* new_headers;
933     const char* expected_headers;
934   } tests[] = {
935     { "HTTP/1.1 200 OK\n",
936
937       "HTTP/1/1 304 Not Modified\n"
938       "connection: keep-alive\n"
939       "Cache-control: max-age=10000\n",
940
941       "HTTP/1.1 200 OK\n"
942       "Cache-control: max-age=10000\n"
943     },
944     { "HTTP/1.1 200 OK\n"
945       "Foo: 1\n"
946       "Cache-control: private\n",
947
948       "HTTP/1/1 304 Not Modified\n"
949       "connection: keep-alive\n"
950       "Cache-control: max-age=10000\n",
951
952       "HTTP/1.1 200 OK\n"
953       "Cache-control: max-age=10000\n"
954       "Foo: 1\n"
955     },
956     { "HTTP/1.1 200 OK\n"
957       "Foo: 1\n"
958       "Cache-control: private\n",
959
960       "HTTP/1/1 304 Not Modified\n"
961       "connection: keep-alive\n"
962       "Cache-CONTROL: max-age=10000\n",
963
964       "HTTP/1.1 200 OK\n"
965       "Cache-CONTROL: max-age=10000\n"
966       "Foo: 1\n"
967     },
968     { "HTTP/1.1 200 OK\n"
969       "Content-Length: 450\n",
970
971       "HTTP/1/1 304 Not Modified\n"
972       "connection: keep-alive\n"
973       "Cache-control:      max-age=10001   \n",
974
975       "HTTP/1.1 200 OK\n"
976       "Cache-control: max-age=10001\n"
977       "Content-Length: 450\n"
978     },
979     { "HTTP/1.1 200 OK\n"
980       "X-Frame-Options: DENY\n",
981
982       "HTTP/1/1 304 Not Modified\n"
983       "X-Frame-Options: ALLOW\n",
984
985       "HTTP/1.1 200 OK\n"
986       "X-Frame-Options: DENY\n",
987     },
988     { "HTTP/1.1 200 OK\n"
989       "X-WebKit-CSP: default-src 'none'\n",
990
991       "HTTP/1/1 304 Not Modified\n"
992       "X-WebKit-CSP: default-src *\n",
993
994       "HTTP/1.1 200 OK\n"
995       "X-WebKit-CSP: default-src 'none'\n",
996     },
997     { "HTTP/1.1 200 OK\n"
998       "X-XSS-Protection: 1\n",
999
1000       "HTTP/1/1 304 Not Modified\n"
1001       "X-XSS-Protection: 0\n",
1002
1003       "HTTP/1.1 200 OK\n"
1004       "X-XSS-Protection: 1\n",
1005     },
1006     { "HTTP/1.1 200 OK\n",
1007
1008       "HTTP/1/1 304 Not Modified\n"
1009       "X-Content-Type-Options: nosniff\n",
1010
1011       "HTTP/1.1 200 OK\n"
1012     },
1013   };
1014
1015   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1016     std::string orig_headers(tests[i].orig_headers);
1017     HeadersToRaw(&orig_headers);
1018     scoped_refptr<net::HttpResponseHeaders> parsed(
1019         new net::HttpResponseHeaders(orig_headers));
1020
1021     std::string new_headers(tests[i].new_headers);
1022     HeadersToRaw(&new_headers);
1023     scoped_refptr<net::HttpResponseHeaders> new_parsed(
1024         new net::HttpResponseHeaders(new_headers));
1025
1026     parsed->Update(*new_parsed.get());
1027
1028     std::string resulting_headers;
1029     parsed->GetNormalizedHeaders(&resulting_headers);
1030     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1031   }
1032 }
1033
1034 TEST(HttpResponseHeadersTest, EnumerateHeaderLines) {
1035   const struct {
1036     const char* headers;
1037     const char* expected_lines;
1038   } tests[] = {
1039     { "HTTP/1.1 200 OK\n",
1040
1041       ""
1042     },
1043     { "HTTP/1.1 200 OK\n"
1044       "Foo: 1\n",
1045
1046       "Foo: 1\n"
1047     },
1048     { "HTTP/1.1 200 OK\n"
1049       "Foo: 1\n"
1050       "Bar: 2\n"
1051       "Foo: 3\n",
1052
1053       "Foo: 1\nBar: 2\nFoo: 3\n"
1054     },
1055     { "HTTP/1.1 200 OK\n"
1056       "Foo: 1, 2, 3\n",
1057
1058       "Foo: 1, 2, 3\n"
1059     },
1060   };
1061   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1062     std::string headers(tests[i].headers);
1063     HeadersToRaw(&headers);
1064     scoped_refptr<net::HttpResponseHeaders> parsed(
1065         new net::HttpResponseHeaders(headers));
1066
1067     std::string name, value, lines;
1068
1069     void* iter = NULL;
1070     while (parsed->EnumerateHeaderLines(&iter, &name, &value)) {
1071       lines.append(name);
1072       lines.append(": ");
1073       lines.append(value);
1074       lines.append("\n");
1075     }
1076
1077     EXPECT_EQ(std::string(tests[i].expected_lines), lines);
1078   }
1079 }
1080
1081 TEST(HttpResponseHeadersTest, IsRedirect) {
1082   const struct {
1083     const char* headers;
1084     const char* location;
1085     bool is_redirect;
1086   } tests[] = {
1087     { "HTTP/1.1 200 OK\n",
1088       "",
1089       false
1090     },
1091     { "HTTP/1.1 301 Moved\n"
1092       "Location: http://foopy/\n",
1093       "http://foopy/",
1094       true
1095     },
1096     { "HTTP/1.1 301 Moved\n"
1097       "Location: \t \n",
1098       "",
1099       false
1100     },
1101     // we use the first location header as the target of the redirect
1102     { "HTTP/1.1 301 Moved\n"
1103       "Location: http://foo/\n"
1104       "Location: http://bar/\n",
1105       "http://foo/",
1106       true
1107     },
1108     // we use the first _valid_ location header as the target of the redirect
1109     { "HTTP/1.1 301 Moved\n"
1110       "Location: \n"
1111       "Location: http://bar/\n",
1112       "http://bar/",
1113       true
1114     },
1115     // bug 1050541 (location header w/ an unescaped comma)
1116     { "HTTP/1.1 301 Moved\n"
1117       "Location: http://foo/bar,baz.html\n",
1118       "http://foo/bar,baz.html",
1119       true
1120     },
1121     // bug 1224617 (location header w/ non-ASCII bytes)
1122     { "HTTP/1.1 301 Moved\n"
1123       "Location: http://foo/bar?key=\xE4\xF6\xFC\n",
1124       "http://foo/bar?key=%E4%F6%FC",
1125       true
1126     },
1127     // Shift_JIS, Big5, and GBK contain multibyte characters with the trailing
1128     // byte falling in the ASCII range.
1129     { "HTTP/1.1 301 Moved\n"
1130       "Location: http://foo/bar?key=\x81\x5E\xD8\xBF\n",
1131       "http://foo/bar?key=%81^%D8%BF",
1132       true
1133     },
1134     { "HTTP/1.1 301 Moved\n"
1135       "Location: http://foo/bar?key=\x82\x40\xBD\xC4\n",
1136       "http://foo/bar?key=%82@%BD%C4",
1137       true
1138     },
1139     { "HTTP/1.1 301 Moved\n"
1140       "Location: http://foo/bar?key=\x83\x5C\x82\x5D\xCB\xD7\n",
1141       "http://foo/bar?key=%83\\%82]%CB%D7",
1142       true
1143     },
1144   };
1145   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1146     std::string headers(tests[i].headers);
1147     HeadersToRaw(&headers);
1148     scoped_refptr<net::HttpResponseHeaders> parsed(
1149         new net::HttpResponseHeaders(headers));
1150
1151     std::string location;
1152     EXPECT_EQ(parsed->IsRedirect(&location), tests[i].is_redirect);
1153     EXPECT_EQ(location, tests[i].location);
1154   }
1155 }
1156
1157 TEST(HttpResponseHeadersTest, GetContentLength) {
1158   const struct {
1159     const char* headers;
1160     int64 expected_len;
1161   } tests[] = {
1162     { "HTTP/1.1 200 OK\n",
1163       -1
1164     },
1165     { "HTTP/1.1 200 OK\n"
1166       "Content-Length: 10\n",
1167       10
1168     },
1169     { "HTTP/1.1 200 OK\n"
1170       "Content-Length: \n",
1171       -1
1172     },
1173     { "HTTP/1.1 200 OK\n"
1174       "Content-Length: abc\n",
1175       -1
1176     },
1177     { "HTTP/1.1 200 OK\n"
1178       "Content-Length: -10\n",
1179       -1
1180     },
1181     { "HTTP/1.1 200 OK\n"
1182       "Content-Length:  +10\n",
1183       -1
1184     },
1185     { "HTTP/1.1 200 OK\n"
1186       "Content-Length: 23xb5\n",
1187       -1
1188     },
1189     { "HTTP/1.1 200 OK\n"
1190       "Content-Length: 0xA\n",
1191       -1
1192     },
1193     { "HTTP/1.1 200 OK\n"
1194       "Content-Length: 010\n",
1195       10
1196     },
1197     // Content-Length too big, will overflow an int64
1198     { "HTTP/1.1 200 OK\n"
1199       "Content-Length: 40000000000000000000\n",
1200       -1
1201     },
1202     { "HTTP/1.1 200 OK\n"
1203       "Content-Length:       10\n",
1204       10
1205     },
1206     { "HTTP/1.1 200 OK\n"
1207       "Content-Length: 10  \n",
1208       10
1209     },
1210     { "HTTP/1.1 200 OK\n"
1211       "Content-Length: \t10\n",
1212       10
1213     },
1214     { "HTTP/1.1 200 OK\n"
1215       "Content-Length: \v10\n",
1216       -1
1217     },
1218     { "HTTP/1.1 200 OK\n"
1219       "Content-Length: \f10\n",
1220       -1
1221     },
1222     { "HTTP/1.1 200 OK\n"
1223       "cOnTeNt-LENgth: 33\n",
1224       33
1225     },
1226     { "HTTP/1.1 200 OK\n"
1227       "Content-Length: 34\r\n",
1228       -1
1229     },
1230   };
1231   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1232     std::string headers(tests[i].headers);
1233     HeadersToRaw(&headers);
1234     scoped_refptr<net::HttpResponseHeaders> parsed(
1235         new net::HttpResponseHeaders(headers));
1236
1237     EXPECT_EQ(tests[i].expected_len, parsed->GetContentLength());
1238   }
1239 }
1240
1241 TEST(HttpResponseHeaders, GetContentRange) {
1242   const struct {
1243     const char* headers;
1244     bool expected_return_value;
1245     int64 expected_first_byte_position;
1246     int64 expected_last_byte_position;
1247     int64 expected_instance_size;
1248   }  tests[] = {
1249     { "HTTP/1.1 206 Partial Content",
1250       false,
1251       -1,
1252       -1,
1253       -1
1254     },
1255     { "HTTP/1.1 206 Partial Content\n"
1256       "Content-Range:",
1257       false,
1258       -1,
1259       -1,
1260       -1
1261     },
1262     { "HTTP/1.1 206 Partial Content\n"
1263       "Content-Range: megabytes 0-10/50",
1264       false,
1265       -1,
1266       -1,
1267       -1
1268     },
1269     { "HTTP/1.1 206 Partial Content\n"
1270       "Content-Range: 0-10/50",
1271       false,
1272       -1,
1273       -1,
1274       -1
1275     },
1276     { "HTTP/1.1 206 Partial Content\n"
1277       "Content-Range: Bytes 0-50/51",
1278       true,
1279       0,
1280       50,
1281       51
1282     },
1283     { "HTTP/1.1 206 Partial Content\n"
1284       "Content-Range: bytes 0-50/51",
1285       true,
1286       0,
1287       50,
1288       51
1289     },
1290     { "HTTP/1.1 206 Partial Content\n"
1291       "Content-Range: bytes\t0-50/51",
1292       false,
1293       -1,
1294       -1,
1295       -1
1296     },
1297     { "HTTP/1.1 206 Partial Content\n"
1298       "Content-Range:     bytes 0-50/51",
1299       true,
1300       0,
1301       50,
1302       51
1303     },
1304     { "HTTP/1.1 206 Partial Content\n"
1305       "Content-Range:     bytes    0    -   50  \t / \t51",
1306       true,
1307       0,
1308       50,
1309       51
1310     },
1311     { "HTTP/1.1 206 Partial Content\n"
1312       "Content-Range: bytes 0\t-\t50\t/\t51\t",
1313       true,
1314       0,
1315       50,
1316       51
1317     },
1318     { "HTTP/1.1 206 Partial Content\n"
1319       "Content-Range:   \tbytes\t\t\t 0\t-\t50\t/\t51\t",
1320       true,
1321       0,
1322       50,
1323       51
1324     },
1325     { "HTTP/1.1 206 Partial Content\n"
1326       "Content-Range: \t   bytes \t  0    -   50   /   5   1",
1327       false,
1328       0,
1329       50,
1330       -1
1331     },
1332     { "HTTP/1.1 206 Partial Content\n"
1333       "Content-Range: \t   bytes \t  0    -   5 0   /   51",
1334       false,
1335       -1,
1336       -1,
1337       -1
1338     },
1339     { "HTTP/1.1 206 Partial Content\n"
1340       "Content-Range: bytes 50-0/51",
1341       false,
1342       50,
1343       0,
1344       -1
1345     },
1346     { "HTTP/1.1 416 Requested range not satisfiable\n"
1347       "Content-Range: bytes * /*",
1348       false,
1349       -1,
1350       -1,
1351       -1
1352     },
1353     { "HTTP/1.1 416 Requested range not satisfiable\n"
1354       "Content-Range: bytes *   /    *   ",
1355       false,
1356       -1,
1357       -1,
1358       -1
1359     },
1360     { "HTTP/1.1 206 Partial Content\n"
1361       "Content-Range: bytes 0-50/*",
1362       false,
1363       0,
1364       50,
1365       -1
1366     },
1367     { "HTTP/1.1 206 Partial Content\n"
1368       "Content-Range: bytes 0-50  /    * ",
1369       false,
1370       0,
1371       50,
1372       -1
1373     },
1374     { "HTTP/1.1 206 Partial Content\n"
1375       "Content-Range: bytes 0-10000000000/10000000001",
1376       true,
1377       0,
1378       10000000000ll,
1379       10000000001ll
1380     },
1381     { "HTTP/1.1 206 Partial Content\n"
1382       "Content-Range: bytes 0-10000000000/10000000000",
1383       false,
1384       0,
1385       10000000000ll,
1386       10000000000ll
1387     },
1388     // 64 bits wraparound.
1389     { "HTTP/1.1 206 Partial Content\n"
1390       "Content-Range: bytes 0 - 9223372036854775807 / 100",
1391       false,
1392       0,
1393       kint64max,
1394       100
1395     },
1396     // 64 bits wraparound.
1397     { "HTTP/1.1 206 Partial Content\n"
1398       "Content-Range: bytes 0 - 100 / -9223372036854775808",
1399       false,
1400       0,
1401       100,
1402       kint64min
1403     },
1404     { "HTTP/1.1 206 Partial Content\n"
1405       "Content-Range: bytes */50",
1406       false,
1407       -1,
1408       -1,
1409       50
1410     },
1411     { "HTTP/1.1 206 Partial Content\n"
1412       "Content-Range: bytes 0-50/10",
1413       false,
1414       0,
1415       50,
1416       10
1417     },
1418     { "HTTP/1.1 206 Partial Content\n"
1419       "Content-Range: bytes 40-50/45",
1420       false,
1421       40,
1422       50,
1423       45
1424     },
1425     { "HTTP/1.1 206 Partial Content\n"
1426       "Content-Range: bytes 0-50/-10",
1427       false,
1428       0,
1429       50,
1430       -10
1431     },
1432     { "HTTP/1.1 206 Partial Content\n"
1433       "Content-Range: bytes 0-0/1",
1434       true,
1435       0,
1436       0,
1437       1
1438     },
1439     { "HTTP/1.1 206 Partial Content\n"
1440       "Content-Range: bytes 0-40000000000000000000/40000000000000000001",
1441       false,
1442       -1,
1443       -1,
1444       -1
1445     },
1446     { "HTTP/1.1 206 Partial Content\n"
1447       "Content-Range: bytes 1-/100",
1448       false,
1449       -1,
1450       -1,
1451       -1
1452     },
1453     { "HTTP/1.1 206 Partial Content\n"
1454       "Content-Range: bytes -/100",
1455       false,
1456       -1,
1457       -1,
1458       -1
1459     },
1460     { "HTTP/1.1 206 Partial Content\n"
1461       "Content-Range: bytes -1/100",
1462       false,
1463       -1,
1464       -1,
1465       -1
1466     },
1467     { "HTTP/1.1 206 Partial Content\n"
1468       "Content-Range: bytes 0-1233/*",
1469       false,
1470       0,
1471       1233,
1472       -1
1473     },
1474     { "HTTP/1.1 206 Partial Content\n"
1475       "Content-Range: bytes -123 - -1/100",
1476       false,
1477       -1,
1478       -1,
1479       -1
1480     },
1481   };
1482   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1483     std::string headers(tests[i].headers);
1484     HeadersToRaw(&headers);
1485     scoped_refptr<net::HttpResponseHeaders> parsed(
1486         new net::HttpResponseHeaders(headers));
1487
1488     int64 first_byte_position;
1489     int64 last_byte_position;
1490     int64 instance_size;
1491     bool return_value = parsed->GetContentRange(&first_byte_position,
1492                                                 &last_byte_position,
1493                                                 &instance_size);
1494     EXPECT_EQ(tests[i].expected_return_value, return_value);
1495     EXPECT_EQ(tests[i].expected_first_byte_position, first_byte_position);
1496     EXPECT_EQ(tests[i].expected_last_byte_position, last_byte_position);
1497     EXPECT_EQ(tests[i].expected_instance_size, instance_size);
1498   }
1499 }
1500
1501 TEST(HttpResponseHeadersTest, IsKeepAlive) {
1502   const struct {
1503     const char* headers;
1504     bool expected_keep_alive;
1505   } tests[] = {
1506     // The status line fabricated by HttpNetworkTransaction for a 0.9 response.
1507     // Treated as 0.9.
1508     { "HTTP/0.9 200 OK",
1509       false
1510     },
1511     // This could come from a broken server.  Treated as 1.0 because it has a
1512     // header.
1513     { "HTTP/0.9 200 OK\n"
1514       "connection: keep-alive\n",
1515       true
1516     },
1517     { "HTTP/1.1 200 OK\n",
1518       true
1519     },
1520     { "HTTP/1.0 200 OK\n",
1521       false
1522     },
1523     { "HTTP/1.0 200 OK\n"
1524       "connection: close\n",
1525       false
1526     },
1527     { "HTTP/1.0 200 OK\n"
1528       "connection: keep-alive\n",
1529       true
1530     },
1531     { "HTTP/1.0 200 OK\n"
1532       "connection: kEeP-AliVe\n",
1533       true
1534     },
1535     { "HTTP/1.0 200 OK\n"
1536       "connection: keep-aliveX\n",
1537       false
1538     },
1539     { "HTTP/1.1 200 OK\n"
1540       "connection: close\n",
1541       false
1542     },
1543     { "HTTP/1.1 200 OK\n"
1544       "connection: keep-alive\n",
1545       true
1546     },
1547     { "HTTP/1.0 200 OK\n"
1548       "proxy-connection: close\n",
1549       false
1550     },
1551     { "HTTP/1.0 200 OK\n"
1552       "proxy-connection: keep-alive\n",
1553       true
1554     },
1555     { "HTTP/1.1 200 OK\n"
1556       "proxy-connection: close\n",
1557       false
1558     },
1559     { "HTTP/1.1 200 OK\n"
1560       "proxy-connection: keep-alive\n",
1561       true
1562     },
1563   };
1564   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1565     std::string headers(tests[i].headers);
1566     HeadersToRaw(&headers);
1567     scoped_refptr<net::HttpResponseHeaders> parsed(
1568         new net::HttpResponseHeaders(headers));
1569
1570     EXPECT_EQ(tests[i].expected_keep_alive, parsed->IsKeepAlive());
1571   }
1572 }
1573
1574 TEST(HttpResponseHeadersTest, HasStrongValidators) {
1575   const struct {
1576     const char* headers;
1577     bool expected_result;
1578   } tests[] = {
1579     { "HTTP/0.9 200 OK",
1580       false
1581     },
1582     { "HTTP/1.0 200 OK\n"
1583       "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1584       "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1585       "ETag: \"foo\"\n",
1586       false
1587     },
1588     { "HTTP/1.1 200 OK\n"
1589       "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1590       "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1591       "ETag: \"foo\"\n",
1592       true
1593     },
1594     { "HTTP/1.1 200 OK\n"
1595       "Date: Wed, 28 Nov 2007 00:41:10 GMT\n"
1596       "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1597       true
1598     },
1599     { "HTTP/1.1 200 OK\n"
1600       "Date: Wed, 28 Nov 2007 00:41:09 GMT\n"
1601       "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1602       false
1603     },
1604     { "HTTP/1.1 200 OK\n"
1605       "ETag: \"foo\"\n",
1606       true
1607     },
1608     // This is not really a weak etag:
1609     { "HTTP/1.1 200 OK\n"
1610       "etag: \"w/foo\"\n",
1611       true
1612     },
1613     // This is a weak etag:
1614     { "HTTP/1.1 200 OK\n"
1615       "etag: w/\"foo\"\n",
1616       false
1617     },
1618     { "HTTP/1.1 200 OK\n"
1619       "etag:    W  /   \"foo\"\n",
1620       false
1621     }
1622   };
1623   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1624     std::string headers(tests[i].headers);
1625     HeadersToRaw(&headers);
1626     scoped_refptr<net::HttpResponseHeaders> parsed(
1627         new net::HttpResponseHeaders(headers));
1628
1629     EXPECT_EQ(tests[i].expected_result, parsed->HasStrongValidators()) <<
1630         "Failed test case " << i;
1631   }
1632 }
1633
1634 TEST(HttpResponseHeadersTest, GetStatusText) {
1635   std::string headers("HTTP/1.1 404 Not Found");
1636   HeadersToRaw(&headers);
1637     scoped_refptr<net::HttpResponseHeaders> parsed(
1638         new net::HttpResponseHeaders(headers));
1639   EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText());
1640 }
1641
1642 TEST(HttpResponseHeadersTest, GetStatusTextMissing) {
1643   std::string headers("HTTP/1.1 404");
1644   HeadersToRaw(&headers);
1645     scoped_refptr<net::HttpResponseHeaders> parsed(
1646         new net::HttpResponseHeaders(headers));
1647   // Since the status line gets normalized, we have OK
1648   EXPECT_EQ(std::string("OK"), parsed->GetStatusText());
1649 }
1650
1651 TEST(HttpResponseHeadersTest, GetStatusTextMultiSpace) {
1652   std::string headers("HTTP/1.0     404     Not   Found");
1653   HeadersToRaw(&headers);
1654     scoped_refptr<net::HttpResponseHeaders> parsed(
1655         new net::HttpResponseHeaders(headers));
1656   EXPECT_EQ(std::string("Not   Found"), parsed->GetStatusText());
1657 }
1658
1659 TEST(HttpResponseHeadersTest, GetStatusBadStatusLine) {
1660   std::string headers("Foo bar.");
1661   HeadersToRaw(&headers);
1662     scoped_refptr<net::HttpResponseHeaders> parsed(
1663         new net::HttpResponseHeaders(headers));
1664   // The bad status line would have gotten rewritten as
1665   // HTTP/1.0 200 OK.
1666   EXPECT_EQ(std::string("OK"), parsed->GetStatusText());
1667 }
1668
1669 TEST(HttpResponseHeadersTest, AddHeader) {
1670   const struct {
1671     const char* orig_headers;
1672     const char* new_header;
1673     const char* expected_headers;
1674   } tests[] = {
1675     { "HTTP/1.1 200 OK\n"
1676       "connection: keep-alive\n"
1677       "Cache-control: max-age=10000\n",
1678
1679       "Content-Length: 450",
1680
1681       "HTTP/1.1 200 OK\n"
1682       "connection: keep-alive\n"
1683       "Cache-control: max-age=10000\n"
1684       "Content-Length: 450\n"
1685     },
1686     { "HTTP/1.1 200 OK\n"
1687       "connection: keep-alive\n"
1688       "Cache-control: max-age=10000    \n",
1689
1690       "Content-Length: 450  ",
1691
1692       "HTTP/1.1 200 OK\n"
1693       "connection: keep-alive\n"
1694       "Cache-control: max-age=10000\n"
1695       "Content-Length: 450\n"
1696     },
1697   };
1698
1699   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1700     std::string orig_headers(tests[i].orig_headers);
1701     HeadersToRaw(&orig_headers);
1702     scoped_refptr<net::HttpResponseHeaders> parsed(
1703         new net::HttpResponseHeaders(orig_headers));
1704
1705     std::string new_header(tests[i].new_header);
1706     parsed->AddHeader(new_header);
1707
1708     std::string resulting_headers;
1709     parsed->GetNormalizedHeaders(&resulting_headers);
1710     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1711   }
1712 }
1713
1714 TEST(HttpResponseHeadersTest, RemoveHeader) {
1715   const struct {
1716     const char* orig_headers;
1717     const char* to_remove;
1718     const char* expected_headers;
1719   } tests[] = {
1720     { "HTTP/1.1 200 OK\n"
1721       "connection: keep-alive\n"
1722       "Cache-control: max-age=10000\n"
1723       "Content-Length: 450\n",
1724
1725       "Content-Length",
1726
1727       "HTTP/1.1 200 OK\n"
1728       "connection: keep-alive\n"
1729       "Cache-control: max-age=10000\n"
1730     },
1731     { "HTTP/1.1 200 OK\n"
1732       "connection: keep-alive  \n"
1733       "Content-Length  : 450  \n"
1734       "Cache-control: max-age=10000\n",
1735
1736       "Content-Length",
1737
1738       "HTTP/1.1 200 OK\n"
1739       "connection: keep-alive\n"
1740       "Cache-control: max-age=10000\n"
1741     },
1742   };
1743
1744   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1745     std::string orig_headers(tests[i].orig_headers);
1746     HeadersToRaw(&orig_headers);
1747     scoped_refptr<net::HttpResponseHeaders> parsed(
1748         new net::HttpResponseHeaders(orig_headers));
1749
1750     std::string name(tests[i].to_remove);
1751     parsed->RemoveHeader(name);
1752
1753     std::string resulting_headers;
1754     parsed->GetNormalizedHeaders(&resulting_headers);
1755     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1756   }
1757 }
1758
1759 TEST(HttpResponseHeadersTest, RemoveIndividualHeader) {
1760   const struct {
1761     const char* orig_headers;
1762     const char* to_remove_name;
1763     const char* to_remove_value;
1764     const char* expected_headers;
1765   } tests[] = {
1766     { "HTTP/1.1 200 OK\n"
1767       "connection: keep-alive\n"
1768       "Cache-control: max-age=10000\n"
1769       "Content-Length: 450\n",
1770
1771       "Content-Length",
1772
1773       "450",
1774
1775       "HTTP/1.1 200 OK\n"
1776       "connection: keep-alive\n"
1777       "Cache-control: max-age=10000\n"
1778     },
1779     { "HTTP/1.1 200 OK\n"
1780       "connection: keep-alive  \n"
1781       "Content-Length  : 450  \n"
1782       "Cache-control: max-age=10000\n",
1783
1784       "Content-Length",
1785
1786       "450",
1787
1788       "HTTP/1.1 200 OK\n"
1789       "connection: keep-alive\n"
1790       "Cache-control: max-age=10000\n"
1791     },
1792     { "HTTP/1.1 200 OK\n"
1793       "connection: keep-alive  \n"
1794       "Content-Length: 450\n"
1795       "Cache-control: max-age=10000\n",
1796
1797       "Content-Length",  // Matching name.
1798
1799       "999",  // Mismatching value.
1800
1801       "HTTP/1.1 200 OK\n"
1802       "connection: keep-alive\n"
1803       "Content-Length: 450\n"
1804       "Cache-control: max-age=10000\n"
1805     },
1806     { "HTTP/1.1 200 OK\n"
1807       "connection: keep-alive  \n"
1808       "Foo: bar, baz\n"
1809       "Foo: bar\n"
1810       "Cache-control: max-age=10000\n",
1811
1812       "Foo",
1813
1814       "bar, baz",  // Space in value.
1815
1816       "HTTP/1.1 200 OK\n"
1817       "connection: keep-alive\n"
1818       "Foo: bar\n"
1819       "Cache-control: max-age=10000\n"
1820     },
1821     { "HTTP/1.1 200 OK\n"
1822       "connection: keep-alive  \n"
1823       "Foo: bar, baz\n"
1824       "Cache-control: max-age=10000\n",
1825
1826       "Foo",
1827
1828       "baz",  // Only partial match -> ignored.
1829
1830       "HTTP/1.1 200 OK\n"
1831       "connection: keep-alive\n"
1832       "Foo: bar, baz\n"
1833       "Cache-control: max-age=10000\n"
1834     },
1835   };
1836
1837   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1838     std::string orig_headers(tests[i].orig_headers);
1839     HeadersToRaw(&orig_headers);
1840     scoped_refptr<net::HttpResponseHeaders> parsed(
1841         new net::HttpResponseHeaders(orig_headers));
1842
1843     std::string name(tests[i].to_remove_name);
1844     std::string value(tests[i].to_remove_value);
1845     parsed->RemoveHeaderLine(name, value);
1846
1847     std::string resulting_headers;
1848     parsed->GetNormalizedHeaders(&resulting_headers);
1849     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1850   }
1851 }
1852
1853 TEST(HttpResponseHeadersTest, ReplaceStatus) {
1854   const struct {
1855     const char* orig_headers;
1856     const char* new_status;
1857     const char* expected_headers;
1858   } tests[] = {
1859     { "HTTP/1.1 206 Partial Content\n"
1860       "connection: keep-alive\n"
1861       "Cache-control: max-age=10000\n"
1862       "Content-Length: 450\n",
1863
1864       "HTTP/1.1 200 OK",
1865
1866       "HTTP/1.1 200 OK\n"
1867       "connection: keep-alive\n"
1868       "Cache-control: max-age=10000\n"
1869       "Content-Length: 450\n"
1870     },
1871     { "HTTP/1.1 200 OK\n"
1872       "connection: keep-alive\n",
1873
1874       "HTTP/1.1 304 Not Modified",
1875
1876       "HTTP/1.1 304 Not Modified\n"
1877       "connection: keep-alive\n"
1878     },
1879     { "HTTP/1.1 200 OK\n"
1880       "connection: keep-alive  \n"
1881       "Content-Length  : 450   \n"
1882       "Cache-control: max-age=10000\n",
1883
1884       "HTTP/1//1 304 Not Modified",
1885
1886       "HTTP/1.0 304 Not Modified\n"
1887       "connection: keep-alive\n"
1888       "Content-Length: 450\n"
1889       "Cache-control: max-age=10000\n"
1890     },
1891   };
1892
1893   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1894     std::string orig_headers(tests[i].orig_headers);
1895     HeadersToRaw(&orig_headers);
1896     scoped_refptr<net::HttpResponseHeaders> parsed(
1897         new net::HttpResponseHeaders(orig_headers));
1898
1899     std::string name(tests[i].new_status);
1900     parsed->ReplaceStatusLine(name);
1901
1902     std::string resulting_headers;
1903     parsed->GetNormalizedHeaders(&resulting_headers);
1904     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1905   }
1906 }
1907
1908 TEST(HttpResponseHeadersTest, UpdateWithNewRange) {
1909   const struct {
1910     const char* orig_headers;
1911     const char* expected_headers;
1912     const char* expected_headers_with_replaced_status;
1913   } tests[] = {
1914     { "HTTP/1.1 200 OK\n"
1915       "Content-Length: 450\n",
1916
1917       "HTTP/1.1 200 OK\n"
1918       "Content-Range: bytes 3-5/450\n"
1919       "Content-Length: 3\n",
1920
1921       "HTTP/1.1 206 Partial Content\n"
1922       "Content-Range: bytes 3-5/450\n"
1923       "Content-Length: 3\n",
1924     },
1925     { "HTTP/1.1 200 OK\n"
1926       "Content-Length: 5\n",
1927
1928       "HTTP/1.1 200 OK\n"
1929       "Content-Range: bytes 3-5/5\n"
1930       "Content-Length: 3\n",
1931
1932       "HTTP/1.1 206 Partial Content\n"
1933       "Content-Range: bytes 3-5/5\n"
1934       "Content-Length: 3\n",
1935     },
1936   };
1937   const net::HttpByteRange range = net::HttpByteRange::Bounded(3, 5);
1938
1939   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1940     std::string orig_headers(tests[i].orig_headers);
1941     std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0');
1942     scoped_refptr<net::HttpResponseHeaders> parsed(
1943         new net::HttpResponseHeaders(orig_headers + '\0'));
1944     int64 content_size = parsed->GetContentLength();
1945     std::string resulting_headers;
1946
1947     // Update headers without replacing status line.
1948     parsed->UpdateWithNewRange(range, content_size, false);
1949     parsed->GetNormalizedHeaders(&resulting_headers);
1950     EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1951
1952     // Replace status line too.
1953     parsed->UpdateWithNewRange(range, content_size, true);
1954     parsed->GetNormalizedHeaders(&resulting_headers);
1955     EXPECT_EQ(std::string(tests[i].expected_headers_with_replaced_status),
1956               resulting_headers);
1957   }
1958 }
1959
1960 TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) {
1961   std::string headers("HTTP/1.1 404\n"
1962                       "Content-Length: 450\n"
1963                       "Connection: keep-alive\n");
1964   HeadersToRaw(&headers);
1965   scoped_refptr<net::HttpResponseHeaders> parsed(
1966       new net::HttpResponseHeaders(headers));
1967
1968   scoped_ptr<base::Value> event_param(
1969       parsed->NetLogCallback(net::NetLog::LOG_ALL_BUT_BYTES));
1970   scoped_refptr<net::HttpResponseHeaders> recreated;
1971
1972   ASSERT_TRUE(net::HttpResponseHeaders::FromNetLogParam(event_param.get(),
1973                                                         &recreated));
1974   ASSERT_TRUE(recreated.get());
1975   EXPECT_EQ(parsed->GetHttpVersion(), recreated->GetHttpVersion());
1976   EXPECT_EQ(parsed->response_code(), recreated->response_code());
1977   EXPECT_EQ(parsed->GetContentLength(), recreated->GetContentLength());
1978   EXPECT_EQ(parsed->IsKeepAlive(), recreated->IsKeepAlive());
1979
1980   std::string normalized_parsed;
1981   parsed->GetNormalizedHeaders(&normalized_parsed);
1982   std::string normalized_recreated;
1983   parsed->GetNormalizedHeaders(&normalized_recreated);
1984   EXPECT_EQ(normalized_parsed, normalized_recreated);
1985 }
1986
1987 TEST_F(HttpResponseHeadersCacheControlTest, AbsentMaxAgeReturnsFalse) {
1988   InitializeHeadersWithCacheControl("nocache");
1989   EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
1990 }
1991
1992 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithNoParameterRejected) {
1993   InitializeHeadersWithCacheControl("max-age=,private");
1994   EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
1995 }
1996
1997 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithSpaceParameterRejected) {
1998   InitializeHeadersWithCacheControl("max-age= ,private");
1999   EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2000 }
2001
2002 TEST_F(HttpResponseHeadersCacheControlTest,
2003        MaxAgeWithSpaceBeforeEqualsIsRejected) {
2004   InitializeHeadersWithCacheControl("max-age = 7");
2005   EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2006 }
2007
2008 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeFirstMatchUsed) {
2009   InitializeHeadersWithCacheControl("max-age=10, max-age=20");
2010   EXPECT_EQ(TimeDelta::FromSeconds(10), GetMaxAgeValue());
2011 }
2012
2013 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeBogusFirstMatchUsed) {
2014   // max-age10 isn't parsed as max-age; max-age=now is parsed as max-age=0 and
2015   // so max-age=20 is not used.
2016   InitializeHeadersWithCacheControl("max-age10, max-age=now, max-age=20");
2017   EXPECT_EQ(TimeDelta::FromSeconds(0), GetMaxAgeValue());
2018 }
2019
2020 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeCaseInsensitive) {
2021   InitializeHeadersWithCacheControl("Max-aGe=15");
2022   EXPECT_EQ(TimeDelta::FromSeconds(15), GetMaxAgeValue());
2023 }
2024
2025 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeEdgeCases) {
2026   // This test doesn't use TEST_P() for consistency with the rest of the tests
2027   // in this file.
2028   // TODO(ricea): Port the tests in this file to use TEST_P().
2029   const struct {
2030     const char* max_age_string;
2031     int64 expected_seconds;
2032   } tests[] = {
2033         {" 1 ", 1},  // Spaces are ignored
2034         {"-1", -1},  // Negative numbers are passed through
2035         {"--1", 0},  // Leading junk gives 0
2036         {"2s", 2},  // trailing junk is ignored
2037         {"3 days", 3},
2038         {"'4'", 0},    // single quotes don't work
2039         {"\"5\"", 0},  // double quotes don't work
2040         {"0x6", 0},    // hex not parsed as hex
2041         {"7F", 7},     // hex without 0x still not parsed as hex
2042         {"010", 10},   // octal not parsed as octal
2043         {"9223372036854", 9223372036854},
2044         //  {"9223372036855", -9223372036854},  // undefined behaviour
2045         //  {"9223372036854775806", -2},        // undefined behaviour
2046         {"9223372036854775807", 9223372036854775807},
2047         {"20000000000000000000",
2048          std::numeric_limits<int64>::max()},  // overflow int64
2049   };
2050   std::string max_age = "max-age=";
2051   for (size_t i = 0; i < arraysize(tests); ++i) {
2052     InitializeHeadersWithCacheControl(
2053         (max_age + tests[i].max_age_string).c_str());
2054     EXPECT_EQ(tests[i].expected_seconds, GetMaxAgeValue().InSeconds())
2055         << " for max-age=" << tests[i].max_age_string;
2056   }
2057 }
2058
2059 TEST_F(HttpResponseHeadersCacheControlTest,
2060        AbsentStaleWhileRevalidateReturnsFalse) {
2061   InitializeHeadersWithCacheControl("max-age=3600");
2062   EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2063 }
2064
2065 TEST_F(HttpResponseHeadersCacheControlTest,
2066        StaleWhileRevalidateWithoutValueRejected) {
2067   InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=");
2068   EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2069 }
2070
2071 TEST_F(HttpResponseHeadersCacheControlTest,
2072        StaleWhileRevalidateWithInvalidValueTreatedAsZero) {
2073   InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=true");
2074   EXPECT_EQ(TimeDelta(), GetStaleWhileRevalidateValue());
2075 }
2076
2077 TEST_F(HttpResponseHeadersCacheControlTest, StaleWhileRevalidateValueReturned) {
2078   InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=7200");
2079   EXPECT_EQ(TimeDelta::FromSeconds(7200), GetStaleWhileRevalidateValue());
2080 }
2081
2082 TEST_F(HttpResponseHeadersCacheControlTest,
2083        FirstStaleWhileRevalidateValueUsed) {
2084   InitializeHeadersWithCacheControl(
2085       "stale-while-revalidate=1,stale-while-revalidate=7200");
2086   EXPECT_EQ(TimeDelta::FromSeconds(1), GetStaleWhileRevalidateValue());
2087 }