- add sources.
[platform/framework/web/crosswalk.git] / src / net / http / http_util_unittest.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <algorithm>
6
7 #include "base/basictypes.h"
8 #include "base/strings/string_util.h"
9 #include "net/http/http_util.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11
12 using net::HttpUtil;
13
14 namespace {
15 class HttpUtilTest : public testing::Test {};
16 }
17
18 TEST(HttpUtilTest, IsSafeHeader) {
19   static const char* unsafe_headers[] = {
20     "sec-",
21     "sEc-",
22     "sec-foo",
23     "sEc-FoO",
24     "proxy-",
25     "pRoXy-",
26     "proxy-foo",
27     "pRoXy-FoO",
28     "accept-charset",
29     "accept-encoding",
30     "access-control-request-headers",
31     "access-control-request-method",
32     "connection",
33     "content-length",
34     "cookie",
35     "cookie2",
36     "content-transfer-encoding",
37     "date",
38     "expect",
39     "host",
40     "keep-alive",
41     "origin",
42     "referer",
43     "te",
44     "trailer",
45     "transfer-encoding",
46     "upgrade",
47     "user-agent",
48     "via",
49   };
50   for (size_t i = 0; i < arraysize(unsafe_headers); ++i) {
51     EXPECT_FALSE(HttpUtil::IsSafeHeader(unsafe_headers[i]))
52       << unsafe_headers[i];
53     EXPECT_FALSE(HttpUtil::IsSafeHeader(StringToUpperASCII(std::string(
54         unsafe_headers[i])))) << unsafe_headers[i];
55   }
56   static const char* safe_headers[] = {
57     "foo",
58     "x-",
59     "x-foo",
60     "content-disposition",
61     "update",
62     "accept-charseta",
63     "accept_charset",
64     "accept-encodinga",
65     "accept_encoding",
66     "access-control-request-headersa",
67     "access-control-request-header",
68     "access_control_request_header",
69     "access-control-request-methoda",
70     "access_control_request_method",
71     "connectiona",
72     "content-lengtha",
73     "content_length",
74     "cookiea",
75     "cookie2a",
76     "cookie3",
77     "content-transfer-encodinga",
78     "content_transfer_encoding",
79     "datea",
80     "expecta",
81     "hosta",
82     "keep-alivea",
83     "keep_alive",
84     "origina",
85     "referera",
86     "referrer",
87     "tea",
88     "trailera",
89     "transfer-encodinga",
90     "transfer_encoding",
91     "upgradea",
92     "user-agenta",
93     "user_agent",
94     "viaa",
95   };
96   for (size_t i = 0; i < arraysize(safe_headers); ++i) {
97     EXPECT_TRUE(HttpUtil::IsSafeHeader(safe_headers[i])) << safe_headers[i];
98     EXPECT_TRUE(HttpUtil::IsSafeHeader(StringToUpperASCII(std::string(
99         safe_headers[i])))) << safe_headers[i];
100   }
101 }
102
103 TEST(HttpUtilTest, HasHeader) {
104   static const struct {
105     const char* headers;
106     const char* name;
107     bool expected_result;
108   } tests[] = {
109     { "", "foo", false },
110     { "foo\r\nbar", "foo", false },
111     { "ffoo: 1", "foo", false },
112     { "foo: 1", "foo", true },
113     { "foo: 1\r\nbar: 2", "foo", true },
114     { "fOO: 1\r\nbar: 2", "foo", true },
115     { "g: 0\r\nfoo: 1\r\nbar: 2", "foo", true },
116   };
117   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
118     bool result = HttpUtil::HasHeader(tests[i].headers, tests[i].name);
119     EXPECT_EQ(tests[i].expected_result, result);
120   }
121 }
122
123 TEST(HttpUtilTest, StripHeaders) {
124   static const char* headers =
125       "Origin: origin\r\n"
126       "Content-Type: text/plain\r\n"
127       "Cookies: foo1\r\n"
128       "Custom: baz\r\n"
129       "COOKIES: foo2\r\n"
130       "Server: Apache\r\n"
131       "OrIGin: origin2\r\n";
132
133   static const char* header_names[] = {
134     "origin", "content-type", "cookies"
135   };
136
137   static const char* expected_stripped_headers =
138       "Custom: baz\r\n"
139       "Server: Apache\r\n";
140
141   EXPECT_EQ(expected_stripped_headers,
142             HttpUtil::StripHeaders(headers, header_names,
143                                    arraysize(header_names)));
144 }
145
146 TEST(HttpUtilTest, HeadersIterator) {
147   std::string headers = "foo: 1\t\r\nbar: hello world\r\nbaz: 3 \r\n";
148
149   HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
150
151   ASSERT_TRUE(it.GetNext());
152   EXPECT_EQ(std::string("foo"), it.name());
153   EXPECT_EQ(std::string("1"), it.values());
154
155   ASSERT_TRUE(it.GetNext());
156   EXPECT_EQ(std::string("bar"), it.name());
157   EXPECT_EQ(std::string("hello world"), it.values());
158
159   ASSERT_TRUE(it.GetNext());
160   EXPECT_EQ(std::string("baz"), it.name());
161   EXPECT_EQ(std::string("3"), it.values());
162
163   EXPECT_FALSE(it.GetNext());
164 }
165
166 TEST(HttpUtilTest, HeadersIterator_MalformedLine) {
167   std::string headers = "foo: 1\n: 2\n3\nbar: 4";
168
169   HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n");
170
171   ASSERT_TRUE(it.GetNext());
172   EXPECT_EQ(std::string("foo"), it.name());
173   EXPECT_EQ(std::string("1"), it.values());
174
175   ASSERT_TRUE(it.GetNext());
176   EXPECT_EQ(std::string("bar"), it.name());
177   EXPECT_EQ(std::string("4"), it.values());
178
179   EXPECT_FALSE(it.GetNext());
180 }
181
182 TEST(HttpUtilTest, HeadersIterator_AdvanceTo) {
183   std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4";
184
185   HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
186   EXPECT_TRUE(it.AdvanceTo("foo"));
187   EXPECT_EQ("foo", it.name());
188   EXPECT_TRUE(it.AdvanceTo("bar"));
189   EXPECT_EQ("bar", it.name());
190   EXPECT_FALSE(it.AdvanceTo("blat"));
191   EXPECT_FALSE(it.GetNext());  // should be at end of headers
192 }
193
194 TEST(HttpUtilTest, HeadersIterator_Reset) {
195   std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4";
196   HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
197   // Search past "foo".
198   EXPECT_TRUE(it.AdvanceTo("bar"));
199   // Now try advancing to "foo".  This time it should fail since the iterator
200   // position is past it.
201   EXPECT_FALSE(it.AdvanceTo("foo"));
202   it.Reset();
203   // Now that we reset the iterator position, we should find 'foo'
204   EXPECT_TRUE(it.AdvanceTo("foo"));
205 }
206
207 TEST(HttpUtilTest, ValuesIterator) {
208   std::string values = " must-revalidate,   no-cache=\"foo, bar\"\t, private ";
209
210   HttpUtil::ValuesIterator it(values.begin(), values.end(), ',');
211
212   ASSERT_TRUE(it.GetNext());
213   EXPECT_EQ(std::string("must-revalidate"), it.value());
214
215   ASSERT_TRUE(it.GetNext());
216   EXPECT_EQ(std::string("no-cache=\"foo, bar\""), it.value());
217
218   ASSERT_TRUE(it.GetNext());
219   EXPECT_EQ(std::string("private"), it.value());
220
221   EXPECT_FALSE(it.GetNext());
222 }
223
224 TEST(HttpUtilTest, ValuesIterator_Blanks) {
225   std::string values = " \t ";
226
227   HttpUtil::ValuesIterator it(values.begin(), values.end(), ',');
228
229   EXPECT_FALSE(it.GetNext());
230 }
231
232 TEST(HttpUtilTest, Unquote) {
233   // Replace <backslash> " with ".
234   EXPECT_STREQ("xyz\"abc", HttpUtil::Unquote("\"xyz\\\"abc\"").c_str());
235
236   // Replace <backslash> <backslash> with <backslash>
237   EXPECT_STREQ("xyz\\abc", HttpUtil::Unquote("\"xyz\\\\abc\"").c_str());
238   EXPECT_STREQ("xyz\\\\\\abc",
239                HttpUtil::Unquote("\"xyz\\\\\\\\\\\\abc\"").c_str());
240
241   // Replace <backslash> X with X
242   EXPECT_STREQ("xyzXabc", HttpUtil::Unquote("\"xyz\\Xabc\"").c_str());
243
244   // Act as identity function on unquoted inputs.
245   EXPECT_STREQ("X", HttpUtil::Unquote("X").c_str());
246   EXPECT_STREQ("\"", HttpUtil::Unquote("\"").c_str());
247
248   // Allow single quotes to act as quote marks.
249   // Not part of RFC 2616.
250   EXPECT_STREQ("x\"", HttpUtil::Unquote("'x\"'").c_str());
251 }
252
253 TEST(HttpUtilTest, Quote) {
254   EXPECT_STREQ("\"xyz\\\"abc\"", HttpUtil::Quote("xyz\"abc").c_str());
255
256   // Replace <backslash> <backslash> with <backslash>
257   EXPECT_STREQ("\"xyz\\\\abc\"", HttpUtil::Quote("xyz\\abc").c_str());
258
259   // Replace <backslash> X with X
260   EXPECT_STREQ("\"xyzXabc\"", HttpUtil::Quote("xyzXabc").c_str());
261 }
262
263 TEST(HttpUtilTest, LocateEndOfHeaders) {
264   struct {
265     const char* input;
266     int expected_result;
267   } tests[] = {
268     { "foo\r\nbar\r\n\r\n", 12 },
269     { "foo\nbar\n\n", 9 },
270     { "foo\r\nbar\r\n\r\njunk", 12 },
271     { "foo\nbar\n\njunk", 9 },
272     { "foo\nbar\n\r\njunk", 10 },
273     { "foo\nbar\r\n\njunk", 10 },
274   };
275   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
276     int input_len = static_cast<int>(strlen(tests[i].input));
277     int eoh = HttpUtil::LocateEndOfHeaders(tests[i].input, input_len);
278     EXPECT_EQ(tests[i].expected_result, eoh);
279   }
280 }
281
282 TEST(HttpUtilTest, AssembleRawHeaders) {
283   struct {
284     const char* input;  // with '|' representing '\0'
285     const char* expected_result;  // with '\0' changed to '|'
286   } tests[] = {
287     { "HTTP/1.0 200 OK\r\nFoo: 1\r\nBar: 2\r\n\r\n",
288       "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
289
290     { "HTTP/1.0 200 OK\nFoo: 1\nBar: 2\n\n",
291       "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
292
293     // Valid line continuation (single SP).
294     {
295       "HTTP/1.0 200 OK\n"
296       "Foo: 1\n"
297       " continuation\n"
298       "Bar: 2\n\n",
299
300       "HTTP/1.0 200 OK|"
301       "Foo: 1 continuation|"
302       "Bar: 2||"
303     },
304
305     // Valid line continuation (single HT).
306     {
307       "HTTP/1.0 200 OK\n"
308       "Foo: 1\n"
309       "\tcontinuation\n"
310       "Bar: 2\n\n",
311
312       "HTTP/1.0 200 OK|"
313       "Foo: 1 continuation|"
314       "Bar: 2||"
315     },
316
317     // Valid line continuation (multiple SP).
318     {
319       "HTTP/1.0 200 OK\n"
320       "Foo: 1\n"
321       "   continuation\n"
322       "Bar: 2\n\n",
323
324       "HTTP/1.0 200 OK|"
325       "Foo: 1 continuation|"
326       "Bar: 2||"
327     },
328
329     // Valid line continuation (multiple HT).
330     {
331       "HTTP/1.0 200 OK\n"
332       "Foo: 1\n"
333       "\t\t\tcontinuation\n"
334       "Bar: 2\n\n",
335
336       "HTTP/1.0 200 OK|"
337       "Foo: 1 continuation|"
338       "Bar: 2||"
339     },
340
341     // Valid line continuation (mixed HT, SP).
342     {
343       "HTTP/1.0 200 OK\n"
344       "Foo: 1\n"
345       " \t \t continuation\n"
346       "Bar: 2\n\n",
347
348       "HTTP/1.0 200 OK|"
349       "Foo: 1 continuation|"
350       "Bar: 2||"
351     },
352
353     // Valid multi-line continuation
354     {
355       "HTTP/1.0 200 OK\n"
356       "Foo: 1\n"
357       " continuation1\n"
358       "\tcontinuation2\n"
359       "  continuation3\n"
360       "Bar: 2\n\n",
361
362       "HTTP/1.0 200 OK|"
363       "Foo: 1 continuation1 continuation2 continuation3|"
364       "Bar: 2||"
365     },
366
367     // Continuation of quoted value.
368     // This is different from what Firefox does, since it
369     // will preserve the LWS.
370     {
371       "HTTP/1.0 200 OK\n"
372       "Etag: \"34534-d3\n"
373       "    134q\"\n"
374       "Bar: 2\n\n",
375
376       "HTTP/1.0 200 OK|"
377       "Etag: \"34534-d3 134q\"|"
378       "Bar: 2||"
379     },
380
381     // Valid multi-line continuation, full LWS lines
382     {
383       "HTTP/1.0 200 OK\n"
384       "Foo: 1\n"
385       "         \n"
386       "\t\t\t\t\n"
387       "\t  continuation\n"
388       "Bar: 2\n\n",
389
390       // One SP per continued line = 3.
391       "HTTP/1.0 200 OK|"
392       "Foo: 1   continuation|"
393       "Bar: 2||"
394     },
395
396     // Valid multi-line continuation, all LWS
397     {
398       "HTTP/1.0 200 OK\n"
399       "Foo: 1\n"
400       "         \n"
401       "\t\t\t\t\n"
402       "\t  \n"
403       "Bar: 2\n\n",
404
405       // One SP per continued line = 3.
406       "HTTP/1.0 200 OK|"
407       "Foo: 1   |"
408       "Bar: 2||"
409     },
410
411     // Valid line continuation (No value bytes in first line).
412     {
413       "HTTP/1.0 200 OK\n"
414       "Foo:\n"
415       " value\n"
416       "Bar: 2\n\n",
417
418       "HTTP/1.0 200 OK|"
419       "Foo: value|"
420       "Bar: 2||"
421     },
422
423     // Not a line continuation (can't continue status line).
424     {
425       "HTTP/1.0 200 OK\n"
426       " Foo: 1\n"
427       "Bar: 2\n\n",
428
429       "HTTP/1.0 200 OK|"
430       " Foo: 1|"
431       "Bar: 2||"
432     },
433
434     // Not a line continuation (can't continue status line).
435     {
436       "HTTP/1.0\n"
437       " 200 OK\n"
438       "Foo: 1\n"
439       "Bar: 2\n\n",
440
441       "HTTP/1.0|"
442       " 200 OK|"
443       "Foo: 1|"
444       "Bar: 2||"
445     },
446
447     // Not a line continuation (can't continue status line).
448     {
449       "HTTP/1.0 404\n"
450       " Not Found\n"
451       "Foo: 1\n"
452       "Bar: 2\n\n",
453
454       "HTTP/1.0 404|"
455       " Not Found|"
456       "Foo: 1|"
457       "Bar: 2||"
458     },
459
460     // Unterminated status line.
461     {
462       "HTTP/1.0 200 OK",
463
464       "HTTP/1.0 200 OK||"
465     },
466
467     // Single terminated, with headers
468     {
469       "HTTP/1.0 200 OK\n"
470       "Foo: 1\n"
471       "Bar: 2\n",
472
473       "HTTP/1.0 200 OK|"
474       "Foo: 1|"
475       "Bar: 2||"
476     },
477
478     // Not terminated, with headers
479     {
480       "HTTP/1.0 200 OK\n"
481       "Foo: 1\n"
482       "Bar: 2",
483
484       "HTTP/1.0 200 OK|"
485       "Foo: 1|"
486       "Bar: 2||"
487     },
488
489     // Not a line continuation (VT)
490     {
491       "HTTP/1.0 200 OK\n"
492       "Foo: 1\n"
493       "\vInvalidContinuation\n"
494       "Bar: 2\n\n",
495
496       "HTTP/1.0 200 OK|"
497       "Foo: 1|"
498       "\vInvalidContinuation|"
499       "Bar: 2||"
500     },
501
502     // Not a line continuation (formfeed)
503     {
504       "HTTP/1.0 200 OK\n"
505       "Foo: 1\n"
506       "\fInvalidContinuation\n"
507       "Bar: 2\n\n",
508
509       "HTTP/1.0 200 OK|"
510       "Foo: 1|"
511       "\fInvalidContinuation|"
512       "Bar: 2||"
513     },
514
515     // Not a line continuation -- can't continue header names.
516     {
517       "HTTP/1.0 200 OK\n"
518       "Serv\n"
519       " er: Apache\n"
520       "\tInvalidContinuation\n"
521       "Bar: 2\n\n",
522
523       "HTTP/1.0 200 OK|"
524       "Serv|"
525       " er: Apache|"
526       "\tInvalidContinuation|"
527       "Bar: 2||"
528     },
529
530     // Not a line continuation -- no value to continue.
531     {
532       "HTTP/1.0 200 OK\n"
533       "Foo: 1\n"
534       "garbage\n"
535       "  not-a-continuation\n"
536       "Bar: 2\n\n",
537
538       "HTTP/1.0 200 OK|"
539       "Foo: 1|"
540       "garbage|"
541       "  not-a-continuation|"
542       "Bar: 2||",
543     },
544
545     // Not a line continuation -- no valid name.
546     {
547       "HTTP/1.0 200 OK\n"
548       ": 1\n"
549       "  garbage\n"
550       "Bar: 2\n\n",
551
552       "HTTP/1.0 200 OK|"
553       ": 1|"
554       "  garbage|"
555       "Bar: 2||",
556     },
557
558     // Not a line continuation -- no valid name (whitespace)
559     {
560       "HTTP/1.0 200 OK\n"
561       "   : 1\n"
562       "  garbage\n"
563       "Bar: 2\n\n",
564
565       "HTTP/1.0 200 OK|"
566       "   : 1|"
567       "  garbage|"
568       "Bar: 2||",
569     },
570
571     // Embed NULLs in the status line. They should not be understood
572     // as line separators.
573     {
574       "HTTP/1.0 200 OK|Bar2:0|Baz2:1\r\nFoo: 1\r\nBar: 2\r\n\r\n",
575       "HTTP/1.0 200 OKBar2:0Baz2:1|Foo: 1|Bar: 2||"
576     },
577
578     // Embed NULLs in a header line. They should not be understood as
579     // line separators.
580     {
581       "HTTP/1.0 200 OK\nFoo: 1|Foo2: 3\nBar: 2\n\n",
582       "HTTP/1.0 200 OK|Foo: 1Foo2: 3|Bar: 2||"
583     },
584   };
585   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
586     std::string input = tests[i].input;
587     std::replace(input.begin(), input.end(), '|', '\0');
588     std::string raw = HttpUtil::AssembleRawHeaders(input.data(), input.size());
589     std::replace(raw.begin(), raw.end(), '\0', '|');
590     EXPECT_EQ(tests[i].expected_result, raw);
591   }
592 }
593
594 // Test SpecForRequest() and PathForRequest().
595 TEST(HttpUtilTest, RequestUrlSanitize) {
596   struct {
597     const char* url;
598     const char* expected_spec;
599     const char* expected_path;
600   } tests[] = {
601     { // Check that #hash is removed.
602       "http://www.google.com:78/foobar?query=1#hash",
603       "http://www.google.com:78/foobar?query=1",
604       "/foobar?query=1"
605     },
606     { // The reference may itself contain # -- strip all of it.
607       "http://192.168.0.1?query=1#hash#10#11#13#14",
608       "http://192.168.0.1/?query=1",
609       "/?query=1"
610     },
611     { // Strip username/password.
612       "http://user:pass@google.com",
613       "http://google.com/",
614       "/"
615     }
616   };
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
627 TEST(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.
636 TEST(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
720 TEST(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
853 namespace {
854 void 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
881 void 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
898 void 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
933 TEST(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
968 TEST(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
977 TEST(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
1007 TEST(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.
1025 TEST(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.
1042 TEST(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 }