http_util_unittest.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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  };
617  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
618    GURL url(GURL(tests[i].url));
619    std::string expected_spec(tests[i].expected_spec);
620    std::string expected_path(tests[i].expected_path);
621
622    EXPECT_EQ(expected_spec, HttpUtil::SpecForRequest(url));
623    EXPECT_EQ(expected_path, HttpUtil::PathForRequest(url));
624  }
625}
626
627TEST(HttpUtilTest, GenerateAcceptLanguageHeader) {
628  EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6"),
629            HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de"));
630  EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6,ko;q=0.4,zh-CN;q=0.2,"
631                        "ja;q=0.2"),
632            HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de,ko,zh-CN,ja"));
633}
634
635// HttpResponseHeadersTest.GetMimeType also tests ParseContentType.
636TEST(HttpUtilTest, ParseContentType) {
637  const struct {
638    const char* content_type;
639    const char* expected_mime_type;
640    const char* expected_charset;
641    const bool expected_had_charset;
642    const char* expected_boundary;
643  } tests[] = {
644    { "text/html; charset=utf-8",
645      "text/html",
646      "utf-8",
647      true,
648      ""
649    },
650    { "text/html; charset =utf-8",
651      "text/html",
652      "utf-8",
653      true,
654      ""
655    },
656    { "text/html; charset= utf-8",
657      "text/html",
658      "utf-8",
659      true,
660      ""
661    },
662    { "text/html; charset=utf-8 ",
663      "text/html",
664      "utf-8",
665      true,
666      ""
667    },
668    { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs\"",
669      "text/html",
670      "",
671      false,
672      "\"WebKit-ada-df-dsf-adsfadsfs\""
673    },
674    { "text/html; boundary =\"WebKit-ada-df-dsf-adsfadsfs\"",
675      "text/html",
676      "",
677      false,
678      "\"WebKit-ada-df-dsf-adsfadsfs\""
679    },
680    { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\"",
681      "text/html",
682      "",
683      false,
684      "\"WebKit-ada-df-dsf-adsfadsfs\""
685    },
686    { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\"   ",
687      "text/html",
688      "",
689      false,
690      "\"WebKit-ada-df-dsf-adsfadsfs\""
691    },
692    { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs  \"",
693      "text/html",
694      "",
695      false,
696      "\"WebKit-ada-df-dsf-adsfadsfs  \""
697    },
698    { "text/html; boundary=WebKit-ada-df-dsf-adsfadsfs",
699      "text/html",
700      "",
701      false,
702      "WebKit-ada-df-dsf-adsfadsfs"
703    },
704    // TODO(abarth): Add more interesting test cases.
705  };
706  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
707    std::string mime_type;
708    std::string charset;
709    bool had_charset = false;
710    std::string boundary;
711    net::HttpUtil::ParseContentType(tests[i].content_type, &mime_type,
712                                    &charset, &had_charset, &boundary);
713    EXPECT_EQ(tests[i].expected_mime_type, mime_type) << "i=" << i;
714    EXPECT_EQ(tests[i].expected_charset, charset) << "i=" << i;
715    EXPECT_EQ(tests[i].expected_had_charset, had_charset) << "i=" << i;
716    EXPECT_EQ(tests[i].expected_boundary, boundary) << "i=" << i;
717  }
718}
719
720TEST(HttpUtilTest, ParseRanges) {
721  const struct {
722    const char* headers;
723    bool expected_return_value;
724    size_t expected_ranges_size;
725    const struct {
726      int64 expected_first_byte_position;
727      int64 expected_last_byte_position;
728      int64 expected_suffix_length;
729    } expected_ranges[10];
730  } tests[] = {
731    { "Range: bytes=0-10",
732      true,
733      1,
734      { {0, 10, -1}, }
735    },
736    { "Range: bytes=10-0",
737      false,
738      0,
739      {}
740    },
741    { "Range: BytES=0-10",
742      true,
743      1,
744      { {0, 10, -1}, }
745    },
746    { "Range: megabytes=0-10",
747      false,
748      0,
749      {}
750    },
751    { "Range: bytes0-10",
752      false,
753      0,
754      {}
755    },
756    { "Range: bytes=0-0,0-10,10-20,100-200,100-,-200",
757      true,
758      6,
759      { {0, 0, -1},
760        {0, 10, -1},
761        {10, 20, -1},
762        {100, 200, -1},
763        {100, -1, -1},
764        {-1, -1, 200},
765      }
766    },
767    { "Range: bytes=0-10\r\n"
768      "Range: bytes=0-10,10-20,100-200,100-,-200",
769      true,
770      1,
771      { {0, 10, -1}
772      }
773    },
774    { "Range: bytes=",
775      false,
776      0,
777      {}
778    },
779    { "Range: bytes=-",
780      false,
781      0,
782      {}
783    },
784    { "Range: bytes=0-10-",
785      false,
786      0,
787      {}
788    },
789    { "Range: bytes=-0-10",
790      false,
791      0,
792      {}
793    },
794    { "Range: bytes =0-10\r\n",
795      true,
796      1,
797      { {0, 10, -1}
798      }
799    },
800    { "Range: bytes=  0-10      \r\n",
801      true,
802      1,
803      { {0, 10, -1}
804      }
805    },
806    { "Range: bytes  =   0  -   10      \r\n",
807      true,
808      1,
809      { {0, 10, -1}
810      }
811    },
812    { "Range: bytes=   0-1   0\r\n",
813      false,
814      0,
815      {}
816    },
817    { "Range: bytes=   0-     -10\r\n",
818      false,
819      0,
820      {}
821    },
822    { "Range: bytes=   0  -  1   ,   10 -20,   100- 200 ,  100-,  -200 \r\n",
823      true,
824      5,
825      { {0, 1, -1},
826        {10, 20, -1},
827        {100, 200, -1},
828        {100, -1, -1},
829        {-1, -1, 200},
830      }
831    },
832  };
833
834  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
835    std::vector<net::HttpByteRange> ranges;
836    bool return_value = HttpUtil::ParseRanges(std::string(tests[i].headers),
837                                              &ranges);
838    EXPECT_EQ(tests[i].expected_return_value, return_value);
839    if (return_value) {
840      EXPECT_EQ(tests[i].expected_ranges_size, ranges.size());
841      for (size_t j = 0; j < ranges.size(); ++j) {
842        EXPECT_EQ(tests[i].expected_ranges[j].expected_first_byte_position,
843                  ranges[j].first_byte_position());
844        EXPECT_EQ(tests[i].expected_ranges[j].expected_last_byte_position,
845                  ranges[j].last_byte_position());
846        EXPECT_EQ(tests[i].expected_ranges[j].expected_suffix_length,
847                  ranges[j].suffix_length());
848      }
849    }
850  }
851}
852
853namespace {
854void CheckCurrentNameValuePair(HttpUtil::NameValuePairsIterator* parser,
855                               bool expect_valid,
856                               std::string expected_name,
857                               std::string expected_value) {
858  ASSERT_EQ(expect_valid, parser->valid());
859  if (!expect_valid) {
860    return;
861  }
862
863  // Let's make sure that these never change (i.e., when a quoted value is
864  // unquoted, it should be cached on the first calls and not regenerated
865  // later).
866  std::string::const_iterator first_value_begin = parser->value_begin();
867  std::string::const_iterator first_value_end = parser->value_end();
868
869  ASSERT_EQ(expected_name, std::string(parser->name_begin(),
870                                       parser->name_end()));
871  ASSERT_EQ(expected_name, parser->name());
872  ASSERT_EQ(expected_value, std::string(parser->value_begin(),
873                                        parser->value_end()));
874  ASSERT_EQ(expected_value, parser->value());
875
876  // Make sure they didn't/don't change.
877  ASSERT_TRUE(first_value_begin == parser->value_begin());
878  ASSERT_TRUE(first_value_end == parser->value_end());
879}
880
881void CheckNextNameValuePair(HttpUtil::NameValuePairsIterator* parser,
882                            bool expect_next,
883                            bool expect_valid,
884                            std::string expected_name,
885                            std::string expected_value) {
886  ASSERT_EQ(expect_next, parser->GetNext());
887  ASSERT_EQ(expect_valid, parser->valid());
888  if (!expect_next || !expect_valid) {
889    return;
890  }
891
892  CheckCurrentNameValuePair(parser,
893                            expect_valid,
894                            expected_name,
895                            expected_value);
896}
897
898void CheckInvalidNameValuePair(std::string valid_part,
899                               std::string invalid_part) {
900  std::string whole_string = valid_part + invalid_part;
901
902  HttpUtil::NameValuePairsIterator valid_parser(valid_part.begin(),
903                                                valid_part.end(),
904                                                ';');
905  HttpUtil::NameValuePairsIterator invalid_parser(whole_string.begin(),
906                                                  whole_string.end(),
907                                                  ';');
908
909  ASSERT_TRUE(valid_parser.valid());
910  ASSERT_TRUE(invalid_parser.valid());
911
912  // Both parsers should return all the same values until "valid_parser" is
913  // exhausted.
914  while (valid_parser.GetNext()) {
915    ASSERT_TRUE(invalid_parser.GetNext());
916    ASSERT_TRUE(valid_parser.valid());
917    ASSERT_TRUE(invalid_parser.valid());
918    ASSERT_EQ(valid_parser.name(), invalid_parser.name());
919    ASSERT_EQ(valid_parser.value(), invalid_parser.value());
920  }
921
922  // valid_parser is exhausted and remains 'valid'
923  ASSERT_TRUE(valid_parser.valid());
924
925  // invalid_parser's corresponding call to GetNext also returns false...
926  ASSERT_FALSE(invalid_parser.GetNext());
927  // ...but the parser is in an invalid state.
928  ASSERT_FALSE(invalid_parser.valid());
929}
930
931}  // anonymous namespace
932
933TEST(HttpUtilTest, NameValuePairsIteratorCopyAndAssign) {
934  std::string data = "alpha='\\'a\\''; beta=\" b \"; cappa='c;'; delta=\"d\"";
935  HttpUtil::NameValuePairsIterator parser_a(data.begin(), data.end(), ';');
936
937  EXPECT_TRUE(parser_a.valid());
938  ASSERT_NO_FATAL_FAILURE(
939      CheckNextNameValuePair(&parser_a, true, true, "alpha", "'a'"));
940
941  HttpUtil::NameValuePairsIterator parser_b(parser_a);
942  // a and b now point to same location
943  ASSERT_NO_FATAL_FAILURE(
944      CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'"));
945  ASSERT_NO_FATAL_FAILURE(
946      CheckCurrentNameValuePair(&parser_a, true, "alpha", "'a'"));
947
948  // advance a, no effect on b
949  ASSERT_NO_FATAL_FAILURE(
950      CheckNextNameValuePair(&parser_a, true, true, "beta", " b "));
951  ASSERT_NO_FATAL_FAILURE(
952      CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'"));
953
954  // assign b the current state of a, no effect on a
955  parser_b = parser_a;
956  ASSERT_NO_FATAL_FAILURE(
957      CheckCurrentNameValuePair(&parser_b, true, "beta", " b "));
958  ASSERT_NO_FATAL_FAILURE(
959      CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
960
961  // advance b, no effect on a
962  ASSERT_NO_FATAL_FAILURE(
963      CheckNextNameValuePair(&parser_b, true, true, "cappa", "c;"));
964  ASSERT_NO_FATAL_FAILURE(
965      CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
966}
967
968TEST(HttpUtilTest, NameValuePairsIteratorEmptyInput) {
969  std::string data;
970  HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
971
972  EXPECT_TRUE(parser.valid());
973  ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
974      &parser, false, true, std::string(), std::string()));
975}
976
977TEST(HttpUtilTest, NameValuePairsIterator) {
978  std::string data = "alpha=1; beta= 2 ;cappa =' 3; ';"
979                     "delta= \" \\\"4\\\" \"; e= \" '5'\"; e=6;"
980                     "f='\\'\\h\\e\\l\\l\\o\\ \\w\\o\\r\\l\\d\\'';"
981                     "g=''; h='hello'";
982  HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
983  EXPECT_TRUE(parser.valid());
984
985  ASSERT_NO_FATAL_FAILURE(
986      CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
987  ASSERT_NO_FATAL_FAILURE(
988      CheckNextNameValuePair(&parser, true, true, "beta", "2"));
989  ASSERT_NO_FATAL_FAILURE(
990      CheckNextNameValuePair(&parser, true, true, "cappa", " 3; "));
991  ASSERT_NO_FATAL_FAILURE(
992      CheckNextNameValuePair(&parser, true, true, "delta", " \"4\" "));
993  ASSERT_NO_FATAL_FAILURE(
994      CheckNextNameValuePair(&parser, true, true, "e", " '5'"));
995  ASSERT_NO_FATAL_FAILURE(
996      CheckNextNameValuePair(&parser, true, true, "e", "6"));
997  ASSERT_NO_FATAL_FAILURE(
998      CheckNextNameValuePair(&parser, true, true, "f", "'hello world'"));
999  ASSERT_NO_FATAL_FAILURE(
1000      CheckNextNameValuePair(&parser, true, true, "g", std::string()));
1001  ASSERT_NO_FATAL_FAILURE(
1002      CheckNextNameValuePair(&parser, true, true, "h", "hello"));
1003  ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1004      &parser, false, true, std::string(), std::string()));
1005}
1006
1007TEST(HttpUtilTest, NameValuePairsIteratorIllegalInputs) {
1008  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; beta"));
1009  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "beta"));
1010
1011  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; 'beta'=2"));
1012  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "'beta'=2"));
1013  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";beta="));
1014  ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1",
1015                                                    ";beta=;cappa=2"));
1016
1017  // According to the spec this is an error, but it doesn't seem appropriate to
1018  // change our behaviour to be less permissive at this time.
1019  // See NameValuePairsIteratorExtraSeparators test
1020  // ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";; beta=2"));
1021}
1022
1023// If we are going to support extra separators against the spec, let's just make
1024// sure they work rationally.
1025TEST(HttpUtilTest, NameValuePairsIteratorExtraSeparators) {
1026  std::string data = " ; ;;alpha=1; ;; ; beta= 2;cappa=3;;; ; ";
1027  HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
1028  EXPECT_TRUE(parser.valid());
1029
1030  ASSERT_NO_FATAL_FAILURE(
1031      CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
1032  ASSERT_NO_FATAL_FAILURE(
1033      CheckNextNameValuePair(&parser, true, true, "beta", "2"));
1034  ASSERT_NO_FATAL_FAILURE(
1035      CheckNextNameValuePair(&parser, true, true, "cappa", "3"));
1036  ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1037      &parser, false, true, std::string(), std::string()));
1038}
1039
1040// See comments on the implementation of NameValuePairsIterator::GetNext
1041// regarding this derogation from the spec.
1042TEST(HttpUtilTest, NameValuePairsIteratorMissingEndQuote) {
1043  std::string data = "name='value";
1044  HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
1045  EXPECT_TRUE(parser.valid());
1046
1047  ASSERT_NO_FATAL_FAILURE(
1048      CheckNextNameValuePair(&parser, true, true, "name", "value"));
1049  ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1050      &parser, false, true, std::string(), std::string()));
1051}
1052