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