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/strings/string_util.h"
      9 #include "net/http/http_util.h"
     10 #include "testing/gtest/include/gtest/gtest.h"
     11 
     12 using net::HttpUtil;
     13 
     14 namespace {
     15 class HttpUtilTest : public testing::Test {};
     16 }
     17 
     18 TEST(HttpUtilTest, IsSafeHeader) {
     19   static const char* unsafe_headers[] = {
     20     "sec-",
     21     "sEc-",
     22     "sec-foo",
     23     "sEc-FoO",
     24     "proxy-",
     25     "pRoXy-",
     26     "proxy-foo",
     27     "pRoXy-FoO",
     28     "accept-charset",
     29     "accept-encoding",
     30     "access-control-request-headers",
     31     "access-control-request-method",
     32     "connection",
     33     "content-length",
     34     "cookie",
     35     "cookie2",
     36     "content-transfer-encoding",
     37     "date",
     38     "expect",
     39     "host",
     40     "keep-alive",
     41     "origin",
     42     "referer",
     43     "te",
     44     "trailer",
     45     "transfer-encoding",
     46     "upgrade",
     47     "user-agent",
     48     "via",
     49   };
     50   for (size_t i = 0; i < arraysize(unsafe_headers); ++i) {
     51     EXPECT_FALSE(HttpUtil::IsSafeHeader(unsafe_headers[i]))
     52       << unsafe_headers[i];
     53     EXPECT_FALSE(HttpUtil::IsSafeHeader(StringToUpperASCII(std::string(
     54         unsafe_headers[i])))) << unsafe_headers[i];
     55   }
     56   static const char* safe_headers[] = {
     57     "foo",
     58     "x-",
     59     "x-foo",
     60     "content-disposition",
     61     "update",
     62     "accept-charseta",
     63     "accept_charset",
     64     "accept-encodinga",
     65     "accept_encoding",
     66     "access-control-request-headersa",
     67     "access-control-request-header",
     68     "access_control_request_header",
     69     "access-control-request-methoda",
     70     "access_control_request_method",
     71     "connectiona",
     72     "content-lengtha",
     73     "content_length",
     74     "cookiea",
     75     "cookie2a",
     76     "cookie3",
     77     "content-transfer-encodinga",
     78     "content_transfer_encoding",
     79     "datea",
     80     "expecta",
     81     "hosta",
     82     "keep-alivea",
     83     "keep_alive",
     84     "origina",
     85     "referera",
     86     "referrer",
     87     "tea",
     88     "trailera",
     89     "transfer-encodinga",
     90     "transfer_encoding",
     91     "upgradea",
     92     "user-agenta",
     93     "user_agent",
     94     "viaa",
     95   };
     96   for (size_t i = 0; i < arraysize(safe_headers); ++i) {
     97     EXPECT_TRUE(HttpUtil::IsSafeHeader(safe_headers[i])) << safe_headers[i];
     98     EXPECT_TRUE(HttpUtil::IsSafeHeader(StringToUpperASCII(std::string(
     99         safe_headers[i])))) << safe_headers[i];
    100   }
    101 }
    102 
    103 TEST(HttpUtilTest, HasHeader) {
    104   static const struct {
    105     const char* headers;
    106     const char* name;
    107     bool expected_result;
    108   } tests[] = {
    109     { "", "foo", false },
    110     { "foo\r\nbar", "foo", false },
    111     { "ffoo: 1", "foo", false },
    112     { "foo: 1", "foo", true },
    113     { "foo: 1\r\nbar: 2", "foo", true },
    114     { "fOO: 1\r\nbar: 2", "foo", true },
    115     { "g: 0\r\nfoo: 1\r\nbar: 2", "foo", true },
    116   };
    117   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
    118     bool result = HttpUtil::HasHeader(tests[i].headers, tests[i].name);
    119     EXPECT_EQ(tests[i].expected_result, result);
    120   }
    121 }
    122 
    123 TEST(HttpUtilTest, StripHeaders) {
    124   static const char* headers =
    125       "Origin: origin\r\n"
    126       "Content-Type: text/plain\r\n"
    127       "Cookies: foo1\r\n"
    128       "Custom: baz\r\n"
    129       "COOKIES: foo2\r\n"
    130       "Server: Apache\r\n"
    131       "OrIGin: origin2\r\n";
    132 
    133   static const char* header_names[] = {
    134     "origin", "content-type", "cookies"
    135   };
    136 
    137   static const char* expected_stripped_headers =
    138       "Custom: baz\r\n"
    139       "Server: Apache\r\n";
    140 
    141   EXPECT_EQ(expected_stripped_headers,
    142             HttpUtil::StripHeaders(headers, header_names,
    143                                    arraysize(header_names)));
    144 }
    145 
    146 TEST(HttpUtilTest, HeadersIterator) {
    147   std::string headers = "foo: 1\t\r\nbar: hello world\r\nbaz: 3 \r\n";
    148 
    149   HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
    150 
    151   ASSERT_TRUE(it.GetNext());
    152   EXPECT_EQ(std::string("foo"), it.name());
    153   EXPECT_EQ(std::string("1"), it.values());
    154 
    155   ASSERT_TRUE(it.GetNext());
    156   EXPECT_EQ(std::string("bar"), it.name());
    157   EXPECT_EQ(std::string("hello world"), it.values());
    158 
    159   ASSERT_TRUE(it.GetNext());
    160   EXPECT_EQ(std::string("baz"), it.name());
    161   EXPECT_EQ(std::string("3"), it.values());
    162 
    163   EXPECT_FALSE(it.GetNext());
    164 }
    165 
    166 TEST(HttpUtilTest, HeadersIterator_MalformedLine) {
    167   std::string headers = "foo: 1\n: 2\n3\nbar: 4";
    168 
    169   HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n");
    170 
    171   ASSERT_TRUE(it.GetNext());
    172   EXPECT_EQ(std::string("foo"), it.name());
    173   EXPECT_EQ(std::string("1"), it.values());
    174 
    175   ASSERT_TRUE(it.GetNext());
    176   EXPECT_EQ(std::string("bar"), it.name());
    177   EXPECT_EQ(std::string("4"), it.values());
    178 
    179   EXPECT_FALSE(it.GetNext());
    180 }
    181 
    182 TEST(HttpUtilTest, HeadersIterator_AdvanceTo) {
    183   std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4";
    184 
    185   HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
    186   EXPECT_TRUE(it.AdvanceTo("foo"));
    187   EXPECT_EQ("foo", it.name());
    188   EXPECT_TRUE(it.AdvanceTo("bar"));
    189   EXPECT_EQ("bar", it.name());
    190   EXPECT_FALSE(it.AdvanceTo("blat"));
    191   EXPECT_FALSE(it.GetNext());  // should be at end of headers
    192 }
    193 
    194 TEST(HttpUtilTest, HeadersIterator_Reset) {
    195   std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4";
    196   HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
    197   // Search past "foo".
    198   EXPECT_TRUE(it.AdvanceTo("bar"));
    199   // Now try advancing to "foo".  This time it should fail since the iterator
    200   // position is past it.
    201   EXPECT_FALSE(it.AdvanceTo("foo"));
    202   it.Reset();
    203   // Now that we reset the iterator position, we should find 'foo'
    204   EXPECT_TRUE(it.AdvanceTo("foo"));
    205 }
    206 
    207 TEST(HttpUtilTest, ValuesIterator) {
    208   std::string values = " must-revalidate,   no-cache=\"foo, bar\"\t, private ";
    209 
    210   HttpUtil::ValuesIterator it(values.begin(), values.end(), ',');
    211 
    212   ASSERT_TRUE(it.GetNext());
    213   EXPECT_EQ(std::string("must-revalidate"), it.value());
    214 
    215   ASSERT_TRUE(it.GetNext());
    216   EXPECT_EQ(std::string("no-cache=\"foo, bar\""), it.value());
    217 
    218   ASSERT_TRUE(it.GetNext());
    219   EXPECT_EQ(std::string("private"), it.value());
    220 
    221   EXPECT_FALSE(it.GetNext());
    222 }
    223 
    224 TEST(HttpUtilTest, ValuesIterator_Blanks) {
    225   std::string values = " \t ";
    226 
    227   HttpUtil::ValuesIterator it(values.begin(), values.end(), ',');
    228 
    229   EXPECT_FALSE(it.GetNext());
    230 }
    231 
    232 TEST(HttpUtilTest, Unquote) {
    233   // Replace <backslash> " with ".
    234   EXPECT_STREQ("xyz\"abc", HttpUtil::Unquote("\"xyz\\\"abc\"").c_str());
    235 
    236   // Replace <backslash> <backslash> with <backslash>
    237   EXPECT_STREQ("xyz\\abc", HttpUtil::Unquote("\"xyz\\\\abc\"").c_str());
    238   EXPECT_STREQ("xyz\\\\\\abc",
    239                HttpUtil::Unquote("\"xyz\\\\\\\\\\\\abc\"").c_str());
    240 
    241   // Replace <backslash> X with X
    242   EXPECT_STREQ("xyzXabc", HttpUtil::Unquote("\"xyz\\Xabc\"").c_str());
    243 
    244   // Act as identity function on unquoted inputs.
    245   EXPECT_STREQ("X", HttpUtil::Unquote("X").c_str());
    246   EXPECT_STREQ("\"", HttpUtil::Unquote("\"").c_str());
    247 
    248   // Allow single quotes to act as quote marks.
    249   // Not part of RFC 2616.
    250   EXPECT_STREQ("x\"", HttpUtil::Unquote("'x\"'").c_str());
    251 }
    252 
    253 TEST(HttpUtilTest, Quote) {
    254   EXPECT_STREQ("\"xyz\\\"abc\"", HttpUtil::Quote("xyz\"abc").c_str());
    255 
    256   // Replace <backslash> <backslash> with <backslash>
    257   EXPECT_STREQ("\"xyz\\\\abc\"", HttpUtil::Quote("xyz\\abc").c_str());
    258 
    259   // Replace <backslash> X with X
    260   EXPECT_STREQ("\"xyzXabc\"", HttpUtil::Quote("xyzXabc").c_str());
    261 }
    262 
    263 TEST(HttpUtilTest, LocateEndOfHeaders) {
    264   struct {
    265     const char* input;
    266     int expected_result;
    267   } tests[] = {
    268     { "foo\r\nbar\r\n\r\n", 12 },
    269     { "foo\nbar\n\n", 9 },
    270     { "foo\r\nbar\r\n\r\njunk", 12 },
    271     { "foo\nbar\n\njunk", 9 },
    272     { "foo\nbar\n\r\njunk", 10 },
    273     { "foo\nbar\r\n\njunk", 10 },
    274   };
    275   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
    276     int input_len = static_cast<int>(strlen(tests[i].input));
    277     int eoh = HttpUtil::LocateEndOfHeaders(tests[i].input, input_len);
    278     EXPECT_EQ(tests[i].expected_result, eoh);
    279   }
    280 }
    281 
    282 TEST(HttpUtilTest, AssembleRawHeaders) {
    283   struct {
    284     const char* input;  // with '|' representing '\0'
    285     const char* expected_result;  // with '\0' changed to '|'
    286   } tests[] = {
    287     { "HTTP/1.0 200 OK\r\nFoo: 1\r\nBar: 2\r\n\r\n",
    288       "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
    289 
    290     { "HTTP/1.0 200 OK\nFoo: 1\nBar: 2\n\n",
    291       "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
    292 
    293     // Valid line continuation (single SP).
    294     {
    295       "HTTP/1.0 200 OK\n"
    296       "Foo: 1\n"
    297       " continuation\n"
    298       "Bar: 2\n\n",
    299 
    300       "HTTP/1.0 200 OK|"
    301       "Foo: 1 continuation|"
    302       "Bar: 2||"
    303     },
    304 
    305     // Valid line continuation (single HT).
    306     {
    307       "HTTP/1.0 200 OK\n"
    308       "Foo: 1\n"
    309       "\tcontinuation\n"
    310       "Bar: 2\n\n",
    311 
    312       "HTTP/1.0 200 OK|"
    313       "Foo: 1 continuation|"
    314       "Bar: 2||"
    315     },
    316 
    317     // Valid line continuation (multiple SP).
    318     {
    319       "HTTP/1.0 200 OK\n"
    320       "Foo: 1\n"
    321       "   continuation\n"
    322       "Bar: 2\n\n",
    323 
    324       "HTTP/1.0 200 OK|"
    325       "Foo: 1 continuation|"
    326       "Bar: 2||"
    327     },
    328 
    329     // Valid line continuation (multiple HT).
    330     {
    331       "HTTP/1.0 200 OK\n"
    332       "Foo: 1\n"
    333       "\t\t\tcontinuation\n"
    334       "Bar: 2\n\n",
    335 
    336       "HTTP/1.0 200 OK|"
    337       "Foo: 1 continuation|"
    338       "Bar: 2||"
    339     },
    340 
    341     // Valid line continuation (mixed HT, SP).
    342     {
    343       "HTTP/1.0 200 OK\n"
    344       "Foo: 1\n"
    345       " \t \t continuation\n"
    346       "Bar: 2\n\n",
    347 
    348       "HTTP/1.0 200 OK|"
    349       "Foo: 1 continuation|"
    350       "Bar: 2||"
    351     },
    352 
    353     // Valid multi-line continuation
    354     {
    355       "HTTP/1.0 200 OK\n"
    356       "Foo: 1\n"
    357       " continuation1\n"
    358       "\tcontinuation2\n"
    359       "  continuation3\n"
    360       "Bar: 2\n\n",
    361 
    362       "HTTP/1.0 200 OK|"
    363       "Foo: 1 continuation1 continuation2 continuation3|"
    364       "Bar: 2||"
    365     },
    366 
    367     // Continuation of quoted value.
    368     // This is different from what Firefox does, since it
    369     // will preserve the LWS.
    370     {
    371       "HTTP/1.0 200 OK\n"
    372       "Etag: \"34534-d3\n"
    373       "    134q\"\n"
    374       "Bar: 2\n\n",
    375 
    376       "HTTP/1.0 200 OK|"
    377       "Etag: \"34534-d3 134q\"|"
    378       "Bar: 2||"
    379     },
    380 
    381     // Valid multi-line continuation, full LWS lines
    382     {
    383       "HTTP/1.0 200 OK\n"
    384       "Foo: 1\n"
    385       "         \n"
    386       "\t\t\t\t\n"
    387       "\t  continuation\n"
    388       "Bar: 2\n\n",
    389 
    390       // One SP per continued line = 3.
    391       "HTTP/1.0 200 OK|"
    392       "Foo: 1   continuation|"
    393       "Bar: 2||"
    394     },
    395 
    396     // Valid multi-line continuation, all LWS
    397     {
    398       "HTTP/1.0 200 OK\n"
    399       "Foo: 1\n"
    400       "         \n"
    401       "\t\t\t\t\n"
    402       "\t  \n"
    403       "Bar: 2\n\n",
    404 
    405       // One SP per continued line = 3.
    406       "HTTP/1.0 200 OK|"
    407       "Foo: 1   |"
    408       "Bar: 2||"
    409     },
    410 
    411     // Valid line continuation (No value bytes in first line).
    412     {
    413       "HTTP/1.0 200 OK\n"
    414       "Foo:\n"
    415       " value\n"
    416       "Bar: 2\n\n",
    417 
    418       "HTTP/1.0 200 OK|"
    419       "Foo: value|"
    420       "Bar: 2||"
    421     },
    422 
    423     // Not a line continuation (can't continue status line).
    424     {
    425       "HTTP/1.0 200 OK\n"
    426       " Foo: 1\n"
    427       "Bar: 2\n\n",
    428 
    429       "HTTP/1.0 200 OK|"
    430       " Foo: 1|"
    431       "Bar: 2||"
    432     },
    433 
    434     // Not a line continuation (can't continue status line).
    435     {
    436       "HTTP/1.0\n"
    437       " 200 OK\n"
    438       "Foo: 1\n"
    439       "Bar: 2\n\n",
    440 
    441       "HTTP/1.0|"
    442       " 200 OK|"
    443       "Foo: 1|"
    444       "Bar: 2||"
    445     },
    446 
    447     // Not a line continuation (can't continue status line).
    448     {
    449       "HTTP/1.0 404\n"
    450       " Not Found\n"
    451       "Foo: 1\n"
    452       "Bar: 2\n\n",
    453 
    454       "HTTP/1.0 404|"
    455       " Not Found|"
    456       "Foo: 1|"
    457       "Bar: 2||"
    458     },
    459 
    460     // Unterminated status line.
    461     {
    462       "HTTP/1.0 200 OK",
    463 
    464       "HTTP/1.0 200 OK||"
    465     },
    466 
    467     // Single terminated, with headers
    468     {
    469       "HTTP/1.0 200 OK\n"
    470       "Foo: 1\n"
    471       "Bar: 2\n",
    472 
    473       "HTTP/1.0 200 OK|"
    474       "Foo: 1|"
    475       "Bar: 2||"
    476     },
    477 
    478     // Not terminated, with headers
    479     {
    480       "HTTP/1.0 200 OK\n"
    481       "Foo: 1\n"
    482       "Bar: 2",
    483 
    484       "HTTP/1.0 200 OK|"
    485       "Foo: 1|"
    486       "Bar: 2||"
    487     },
    488 
    489     // Not a line continuation (VT)
    490     {
    491       "HTTP/1.0 200 OK\n"
    492       "Foo: 1\n"
    493       "\vInvalidContinuation\n"
    494       "Bar: 2\n\n",
    495 
    496       "HTTP/1.0 200 OK|"
    497       "Foo: 1|"
    498       "\vInvalidContinuation|"
    499       "Bar: 2||"
    500     },
    501 
    502     // Not a line continuation (formfeed)
    503     {
    504       "HTTP/1.0 200 OK\n"
    505       "Foo: 1\n"
    506       "\fInvalidContinuation\n"
    507       "Bar: 2\n\n",
    508 
    509       "HTTP/1.0 200 OK|"
    510       "Foo: 1|"
    511       "\fInvalidContinuation|"
    512       "Bar: 2||"
    513     },
    514 
    515     // Not a line continuation -- can't continue header names.
    516     {
    517       "HTTP/1.0 200 OK\n"
    518       "Serv\n"
    519       " er: Apache\n"
    520       "\tInvalidContinuation\n"
    521       "Bar: 2\n\n",
    522 
    523       "HTTP/1.0 200 OK|"
    524       "Serv|"
    525       " er: Apache|"
    526       "\tInvalidContinuation|"
    527       "Bar: 2||"
    528     },
    529 
    530     // Not a line continuation -- no value to continue.
    531     {
    532       "HTTP/1.0 200 OK\n"
    533       "Foo: 1\n"
    534       "garbage\n"
    535       "  not-a-continuation\n"
    536       "Bar: 2\n\n",
    537 
    538       "HTTP/1.0 200 OK|"
    539       "Foo: 1|"
    540       "garbage|"
    541       "  not-a-continuation|"
    542       "Bar: 2||",
    543     },
    544 
    545     // Not a line continuation -- no valid name.
    546     {
    547       "HTTP/1.0 200 OK\n"
    548       ": 1\n"
    549       "  garbage\n"
    550       "Bar: 2\n\n",
    551 
    552       "HTTP/1.0 200 OK|"
    553       ": 1|"
    554       "  garbage|"
    555       "Bar: 2||",
    556     },
    557 
    558     // Not a line continuation -- no valid name (whitespace)
    559     {
    560       "HTTP/1.0 200 OK\n"
    561       "   : 1\n"
    562       "  garbage\n"
    563       "Bar: 2\n\n",
    564 
    565       "HTTP/1.0 200 OK|"
    566       "   : 1|"
    567       "  garbage|"
    568       "Bar: 2||",
    569     },
    570 
    571     // Embed NULLs in the status line. They should not be understood
    572     // as line separators.
    573     {
    574       "HTTP/1.0 200 OK|Bar2:0|Baz2:1\r\nFoo: 1\r\nBar: 2\r\n\r\n",
    575       "HTTP/1.0 200 OKBar2:0Baz2:1|Foo: 1|Bar: 2||"
    576     },
    577 
    578     // Embed NULLs in a header line. They should not be understood as
    579     // line separators.
    580     {
    581       "HTTP/1.0 200 OK\nFoo: 1|Foo2: 3\nBar: 2\n\n",
    582       "HTTP/1.0 200 OK|Foo: 1Foo2: 3|Bar: 2||"
    583     },
    584   };
    585   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
    586     std::string input = tests[i].input;
    587     std::replace(input.begin(), input.end(), '|', '\0');
    588     std::string raw = HttpUtil::AssembleRawHeaders(input.data(), input.size());
    589     std::replace(raw.begin(), raw.end(), '\0', '|');
    590     EXPECT_EQ(tests[i].expected_result, raw);
    591   }
    592 }
    593 
    594 // Test SpecForRequest() and PathForRequest().
    595 TEST(HttpUtilTest, RequestUrlSanitize) {
    596   struct {
    597     const char* url;
    598     const char* expected_spec;
    599     const char* expected_path;
    600   } tests[] = {
    601     { // Check that #hash is removed.
    602       "http://www.google.com:78/foobar?query=1#hash",
    603       "http://www.google.com:78/foobar?query=1",
    604       "/foobar?query=1"
    605     },
    606     { // The reference may itself contain # -- strip all of it.
    607       "http://192.168.0.1?query=1#hash#10#11#13#14",
    608       "http://192.168.0.1/?query=1",
    609       "/?query=1"
    610     },
    611     { // Strip username/password.
    612       "http://user:pass@google.com",
    613       "http://google.com/",
    614       "/"
    615     },
    616     { // https scheme
    617       "https://www.google.com:78/foobar?query=1#hash",
    618       "https://www.google.com:78/foobar?query=1",
    619       "/foobar?query=1"
    620     },
    621     { // WebSocket's ws scheme
    622       "ws://www.google.com:78/foobar?query=1#hash",
    623       "ws://www.google.com:78/foobar?query=1",
    624       "/foobar?query=1"
    625     },
    626     { // WebSocket's wss scheme
    627       "wss://www.google.com:78/foobar?query=1#hash",
    628       "wss://www.google.com:78/foobar?query=1",
    629       "/foobar?query=1"
    630     }
    631   };
    632   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
    633     GURL url(GURL(tests[i].url));
    634     std::string expected_spec(tests[i].expected_spec);
    635     std::string expected_path(tests[i].expected_path);
    636 
    637     EXPECT_EQ(expected_spec, HttpUtil::SpecForRequest(url));
    638     EXPECT_EQ(expected_path, HttpUtil::PathForRequest(url));
    639   }
    640 }
    641 
    642 // Test SpecForRequest() for "ftp" scheme.
    643 TEST(HttpUtilTest, SpecForRequestForUrlWithFtpScheme) {
    644   GURL ftp_url("ftp://user:pass@google.com/pub/chromium/");
    645   EXPECT_EQ("ftp://google.com/pub/chromium/",
    646             HttpUtil::SpecForRequest(ftp_url));
    647 }
    648 
    649 TEST(HttpUtilTest, GenerateAcceptLanguageHeader) {
    650   EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6"),
    651             HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de"));
    652   EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6,ko;q=0.4,zh-CN;q=0.2,"
    653                         "ja;q=0.2"),
    654             HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de,ko,zh-CN,ja"));
    655 }
    656 
    657 // HttpResponseHeadersTest.GetMimeType also tests ParseContentType.
    658 TEST(HttpUtilTest, ParseContentType) {
    659   const struct {
    660     const char* content_type;
    661     const char* expected_mime_type;
    662     const char* expected_charset;
    663     const bool expected_had_charset;
    664     const char* expected_boundary;
    665   } tests[] = {
    666     { "text/html; charset=utf-8",
    667       "text/html",
    668       "utf-8",
    669       true,
    670       ""
    671     },
    672     { "text/html; charset =utf-8",
    673       "text/html",
    674       "utf-8",
    675       true,
    676       ""
    677     },
    678     { "text/html; charset= utf-8",
    679       "text/html",
    680       "utf-8",
    681       true,
    682       ""
    683     },
    684     { "text/html; charset=utf-8 ",
    685       "text/html",
    686       "utf-8",
    687       true,
    688       ""
    689     },
    690     { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs\"",
    691       "text/html",
    692       "",
    693       false,
    694       "\"WebKit-ada-df-dsf-adsfadsfs\""
    695     },
    696     { "text/html; boundary =\"WebKit-ada-df-dsf-adsfadsfs\"",
    697       "text/html",
    698       "",
    699       false,
    700       "\"WebKit-ada-df-dsf-adsfadsfs\""
    701     },
    702     { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\"",
    703       "text/html",
    704       "",
    705       false,
    706       "\"WebKit-ada-df-dsf-adsfadsfs\""
    707     },
    708     { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\"   ",
    709       "text/html",
    710       "",
    711       false,
    712       "\"WebKit-ada-df-dsf-adsfadsfs\""
    713     },
    714     { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs  \"",
    715       "text/html",
    716       "",
    717       false,
    718       "\"WebKit-ada-df-dsf-adsfadsfs  \""
    719     },
    720     { "text/html; boundary=WebKit-ada-df-dsf-adsfadsfs",
    721       "text/html",
    722       "",
    723       false,
    724       "WebKit-ada-df-dsf-adsfadsfs"
    725     },
    726     // TODO(abarth): Add more interesting test cases.
    727   };
    728   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
    729     std::string mime_type;
    730     std::string charset;
    731     bool had_charset = false;
    732     std::string boundary;
    733     net::HttpUtil::ParseContentType(tests[i].content_type, &mime_type,
    734                                     &charset, &had_charset, &boundary);
    735     EXPECT_EQ(tests[i].expected_mime_type, mime_type) << "i=" << i;
    736     EXPECT_EQ(tests[i].expected_charset, charset) << "i=" << i;
    737     EXPECT_EQ(tests[i].expected_had_charset, had_charset) << "i=" << i;
    738     EXPECT_EQ(tests[i].expected_boundary, boundary) << "i=" << i;
    739   }
    740 }
    741 
    742 TEST(HttpUtilTest, ParseRanges) {
    743   const struct {
    744     const char* headers;
    745     bool expected_return_value;
    746     size_t expected_ranges_size;
    747     const struct {
    748       int64 expected_first_byte_position;
    749       int64 expected_last_byte_position;
    750       int64 expected_suffix_length;
    751     } expected_ranges[10];
    752   } tests[] = {
    753     { "Range: bytes=0-10",
    754       true,
    755       1,
    756       { {0, 10, -1}, }
    757     },
    758     { "Range: bytes=10-0",
    759       false,
    760       0,
    761       {}
    762     },
    763     { "Range: BytES=0-10",
    764       true,
    765       1,
    766       { {0, 10, -1}, }
    767     },
    768     { "Range: megabytes=0-10",
    769       false,
    770       0,
    771       {}
    772     },
    773     { "Range: bytes0-10",
    774       false,
    775       0,
    776       {}
    777     },
    778     { "Range: bytes=0-0,0-10,10-20,100-200,100-,-200",
    779       true,
    780       6,
    781       { {0, 0, -1},
    782         {0, 10, -1},
    783         {10, 20, -1},
    784         {100, 200, -1},
    785         {100, -1, -1},
    786         {-1, -1, 200},
    787       }
    788     },
    789     { "Range: bytes=0-10\r\n"
    790       "Range: bytes=0-10,10-20,100-200,100-,-200",
    791       true,
    792       1,
    793       { {0, 10, -1}
    794       }
    795     },
    796     { "Range: bytes=",
    797       false,
    798       0,
    799       {}
    800     },
    801     { "Range: bytes=-",
    802       false,
    803       0,
    804       {}
    805     },
    806     { "Range: bytes=0-10-",
    807       false,
    808       0,
    809       {}
    810     },
    811     { "Range: bytes=-0-10",
    812       false,
    813       0,
    814       {}
    815     },
    816     { "Range: bytes =0-10\r\n",
    817       true,
    818       1,
    819       { {0, 10, -1}
    820       }
    821     },
    822     { "Range: bytes=  0-10      \r\n",
    823       true,
    824       1,
    825       { {0, 10, -1}
    826       }
    827     },
    828     { "Range: bytes  =   0  -   10      \r\n",
    829       true,
    830       1,
    831       { {0, 10, -1}
    832       }
    833     },
    834     { "Range: bytes=   0-1   0\r\n",
    835       false,
    836       0,
    837       {}
    838     },
    839     { "Range: bytes=   0-     -10\r\n",
    840       false,
    841       0,
    842       {}
    843     },
    844     { "Range: bytes=   0  -  1   ,   10 -20,   100- 200 ,  100-,  -200 \r\n",
    845       true,
    846       5,
    847       { {0, 1, -1},
    848         {10, 20, -1},
    849         {100, 200, -1},
    850         {100, -1, -1},
    851         {-1, -1, 200},
    852       }
    853     },
    854   };
    855 
    856   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
    857     std::vector<net::HttpByteRange> ranges;
    858     bool return_value = HttpUtil::ParseRanges(std::string(tests[i].headers),
    859                                               &ranges);
    860     EXPECT_EQ(tests[i].expected_return_value, return_value);
    861     if (return_value) {
    862       EXPECT_EQ(tests[i].expected_ranges_size, ranges.size());
    863       for (size_t j = 0; j < ranges.size(); ++j) {
    864         EXPECT_EQ(tests[i].expected_ranges[j].expected_first_byte_position,
    865                   ranges[j].first_byte_position());
    866         EXPECT_EQ(tests[i].expected_ranges[j].expected_last_byte_position,
    867                   ranges[j].last_byte_position());
    868         EXPECT_EQ(tests[i].expected_ranges[j].expected_suffix_length,
    869                   ranges[j].suffix_length());
    870       }
    871     }
    872   }
    873 }
    874 
    875 namespace {
    876 void CheckCurrentNameValuePair(HttpUtil::NameValuePairsIterator* parser,
    877                                bool expect_valid,
    878                                std::string expected_name,
    879                                std::string expected_value) {
    880   ASSERT_EQ(expect_valid, parser->valid());
    881   if (!expect_valid) {
    882     return;
    883   }
    884 
    885   // Let's make sure that these never change (i.e., when a quoted value is
    886   // unquoted, it should be cached on the first calls and not regenerated
    887   // later).
    888   std::string::const_iterator first_value_begin = parser->value_begin();
    889   std::string::const_iterator first_value_end = parser->value_end();
    890 
    891   ASSERT_EQ(expected_name, std::string(parser->name_begin(),
    892                                        parser->name_end()));
    893   ASSERT_EQ(expected_name, parser->name());
    894   ASSERT_EQ(expected_value, std::string(parser->value_begin(),
    895                                         parser->value_end()));
    896   ASSERT_EQ(expected_value, parser->value());
    897 
    898   // Make sure they didn't/don't change.
    899   ASSERT_TRUE(first_value_begin == parser->value_begin());
    900   ASSERT_TRUE(first_value_end == parser->value_end());
    901 }
    902 
    903 void CheckNextNameValuePair(HttpUtil::NameValuePairsIterator* parser,
    904                             bool expect_next,
    905                             bool expect_valid,
    906                             std::string expected_name,
    907                             std::string expected_value) {
    908   ASSERT_EQ(expect_next, parser->GetNext());
    909   ASSERT_EQ(expect_valid, parser->valid());
    910   if (!expect_next || !expect_valid) {
    911     return;
    912   }
    913 
    914   CheckCurrentNameValuePair(parser,
    915                             expect_valid,
    916                             expected_name,
    917                             expected_value);
    918 }
    919 
    920 void CheckInvalidNameValuePair(std::string valid_part,
    921                                std::string invalid_part) {
    922   std::string whole_string = valid_part + invalid_part;
    923 
    924   HttpUtil::NameValuePairsIterator valid_parser(valid_part.begin(),
    925                                                 valid_part.end(),
    926                                                 ';');
    927   HttpUtil::NameValuePairsIterator invalid_parser(whole_string.begin(),
    928                                                   whole_string.end(),
    929                                                   ';');
    930 
    931   ASSERT_TRUE(valid_parser.valid());
    932   ASSERT_TRUE(invalid_parser.valid());
    933 
    934   // Both parsers should return all the same values until "valid_parser" is
    935   // exhausted.
    936   while (valid_parser.GetNext()) {
    937     ASSERT_TRUE(invalid_parser.GetNext());
    938     ASSERT_TRUE(valid_parser.valid());
    939     ASSERT_TRUE(invalid_parser.valid());
    940     ASSERT_EQ(valid_parser.name(), invalid_parser.name());
    941     ASSERT_EQ(valid_parser.value(), invalid_parser.value());
    942   }
    943 
    944   // valid_parser is exhausted and remains 'valid'
    945   ASSERT_TRUE(valid_parser.valid());
    946 
    947   // invalid_parser's corresponding call to GetNext also returns false...
    948   ASSERT_FALSE(invalid_parser.GetNext());
    949   // ...but the parser is in an invalid state.
    950   ASSERT_FALSE(invalid_parser.valid());
    951 }
    952 
    953 }  // anonymous namespace
    954 
    955 TEST(HttpUtilTest, NameValuePairsIteratorCopyAndAssign) {
    956   std::string data = "alpha='\\'a\\''; beta=\" b \"; cappa='c;'; delta=\"d\"";
    957   HttpUtil::NameValuePairsIterator parser_a(data.begin(), data.end(), ';');
    958 
    959   EXPECT_TRUE(parser_a.valid());
    960   ASSERT_NO_FATAL_FAILURE(
    961       CheckNextNameValuePair(&parser_a, true, true, "alpha", "'a'"));
    962 
    963   HttpUtil::NameValuePairsIterator parser_b(parser_a);
    964   // a and b now point to same location
    965   ASSERT_NO_FATAL_FAILURE(
    966       CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'"));
    967   ASSERT_NO_FATAL_FAILURE(
    968       CheckCurrentNameValuePair(&parser_a, true, "alpha", "'a'"));
    969 
    970   // advance a, no effect on b
    971   ASSERT_NO_FATAL_FAILURE(
    972       CheckNextNameValuePair(&parser_a, true, true, "beta", " b "));
    973   ASSERT_NO_FATAL_FAILURE(
    974       CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'"));
    975 
    976   // assign b the current state of a, no effect on a
    977   parser_b = parser_a;
    978   ASSERT_NO_FATAL_FAILURE(
    979       CheckCurrentNameValuePair(&parser_b, true, "beta", " b "));
    980   ASSERT_NO_FATAL_FAILURE(
    981       CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
    982 
    983   // advance b, no effect on a
    984   ASSERT_NO_FATAL_FAILURE(
    985       CheckNextNameValuePair(&parser_b, true, true, "cappa", "c;"));
    986   ASSERT_NO_FATAL_FAILURE(
    987       CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
    988 }
    989 
    990 TEST(HttpUtilTest, NameValuePairsIteratorEmptyInput) {
    991   std::string data;
    992   HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
    993 
    994   EXPECT_TRUE(parser.valid());
    995   ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
    996       &parser, false, true, std::string(), std::string()));
    997 }
    998 
    999 TEST(HttpUtilTest, NameValuePairsIterator) {
   1000   std::string data = "alpha=1; beta= 2 ;cappa =' 3; ';"
   1001                      "delta= \" \\\"4\\\" \"; e= \" '5'\"; e=6;"
   1002                      "f='\\'\\h\\e\\l\\l\\o\\ \\w\\o\\r\\l\\d\\'';"
   1003                      "g=''; h='hello'";
   1004   HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
   1005   EXPECT_TRUE(parser.valid());
   1006 
   1007   ASSERT_NO_FATAL_FAILURE(
   1008       CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
   1009   ASSERT_NO_FATAL_FAILURE(
   1010       CheckNextNameValuePair(&parser, true, true, "beta", "2"));
   1011   ASSERT_NO_FATAL_FAILURE(
   1012       CheckNextNameValuePair(&parser, true, true, "cappa", " 3; "));
   1013   ASSERT_NO_FATAL_FAILURE(
   1014       CheckNextNameValuePair(&parser, true, true, "delta", " \"4\" "));
   1015   ASSERT_NO_FATAL_FAILURE(
   1016       CheckNextNameValuePair(&parser, true, true, "e", " '5'"));
   1017   ASSERT_NO_FATAL_FAILURE(
   1018       CheckNextNameValuePair(&parser, true, true, "e", "6"));
   1019   ASSERT_NO_FATAL_FAILURE(
   1020       CheckNextNameValuePair(&parser, true, true, "f", "'hello world'"));
   1021   ASSERT_NO_FATAL_FAILURE(
   1022       CheckNextNameValuePair(&parser, true, true, "g", std::string()));
   1023   ASSERT_NO_FATAL_FAILURE(
   1024       CheckNextNameValuePair(&parser, true, true, "h", "hello"));
   1025   ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
   1026       &parser, false, true, std::string(), std::string()));
   1027 }
   1028 
   1029 TEST(HttpUtilTest, NameValuePairsIteratorIllegalInputs) {
   1030   ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; beta"));
   1031   ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "beta"));
   1032 
   1033   ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; 'beta'=2"));
   1034   ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "'beta'=2"));
   1035   ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";beta="));
   1036   ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1",
   1037                                                     ";beta=;cappa=2"));
   1038 
   1039   // According to the spec this is an error, but it doesn't seem appropriate to
   1040   // change our behaviour to be less permissive at this time.
   1041   // See NameValuePairsIteratorExtraSeparators test
   1042   // ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";; beta=2"));
   1043 }
   1044 
   1045 // If we are going to support extra separators against the spec, let's just make
   1046 // sure they work rationally.
   1047 TEST(HttpUtilTest, NameValuePairsIteratorExtraSeparators) {
   1048   std::string data = " ; ;;alpha=1; ;; ; beta= 2;cappa=3;;; ; ";
   1049   HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
   1050   EXPECT_TRUE(parser.valid());
   1051 
   1052   ASSERT_NO_FATAL_FAILURE(
   1053       CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
   1054   ASSERT_NO_FATAL_FAILURE(
   1055       CheckNextNameValuePair(&parser, true, true, "beta", "2"));
   1056   ASSERT_NO_FATAL_FAILURE(
   1057       CheckNextNameValuePair(&parser, true, true, "cappa", "3"));
   1058   ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
   1059       &parser, false, true, std::string(), std::string()));
   1060 }
   1061 
   1062 // See comments on the implementation of NameValuePairsIterator::GetNext
   1063 // regarding this derogation from the spec.
   1064 TEST(HttpUtilTest, NameValuePairsIteratorMissingEndQuote) {
   1065   std::string data = "name='value";
   1066   HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
   1067   EXPECT_TRUE(parser.valid());
   1068 
   1069   ASSERT_NO_FATAL_FAILURE(
   1070       CheckNextNameValuePair(&parser, true, true, "name", "value"));
   1071   ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
   1072       &parser, false, true, std::string(), std::string()));
   1073 }
   1074