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
12using net::HttpUtil;
13
14namespace {
15class HttpUtilTest : public testing::Test {};
16}
17
18TEST(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
103TEST(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
123TEST(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
146TEST(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
166TEST(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
182TEST(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
194TEST(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
207TEST(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
224TEST(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
232TEST(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
253TEST(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
263TEST(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
282TEST(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().
595TEST(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.
643TEST(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
649TEST(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.
658TEST(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
742TEST(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
875namespace {
876void 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
903void 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
920void 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
955TEST(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
990TEST(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
999TEST(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
1029TEST(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.
1047TEST(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.
1064TEST(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