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