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