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