Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / http / http_util_unittest.cc
bloba5c984db66b1f5e46798036e8c1ac3758fab9f81
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.
5 #include <algorithm>
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"
12 namespace net {
14 namespace {
15 class HttpUtilTest : public testing::Test {};
18 TEST(HttpUtilTest, IsSafeHeader) {
19 static const char* const 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",
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(base::ToUpperASCII(unsafe_headers[i])))
54 << unsafe_headers[i];
56 static const char* const 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",
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(base::ToUpperASCII(safe_headers[i])))
99 << safe_headers[i];
103 TEST(HttpUtilTest, HasHeader) {
104 static const struct {
105 const char* const headers;
106 const char* const 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 },
117 for (size_t i = 0; i < arraysize(tests); ++i) {
118 bool result = HttpUtil::HasHeader(tests[i].headers, tests[i].name);
119 EXPECT_EQ(tests[i].expected_result, result);
123 TEST(HttpUtilTest, StripHeaders) {
124 static const char* const 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";
133 static const char* const header_names[] = {
134 "origin", "content-type", "cookies"
137 static const char* const expected_stripped_headers =
138 "Custom: baz\r\n"
139 "Server: Apache\r\n";
141 EXPECT_EQ(expected_stripped_headers,
142 HttpUtil::StripHeaders(headers, header_names,
143 arraysize(header_names)));
146 TEST(HttpUtilTest, HeadersIterator) {
147 std::string headers = "foo: 1\t\r\nbar: hello world\r\nbaz: 3 \r\n";
149 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n");
151 ASSERT_TRUE(it.GetNext());
152 EXPECT_EQ(std::string("foo"), it.name());
153 EXPECT_EQ(std::string("1"), it.values());
155 ASSERT_TRUE(it.GetNext());
156 EXPECT_EQ(std::string("bar"), it.name());
157 EXPECT_EQ(std::string("hello world"), it.values());
159 ASSERT_TRUE(it.GetNext());
160 EXPECT_EQ(std::string("baz"), it.name());
161 EXPECT_EQ(std::string("3"), it.values());
163 EXPECT_FALSE(it.GetNext());
166 TEST(HttpUtilTest, HeadersIterator_MalformedLine) {
167 std::string headers = "foo: 1\n: 2\n3\nbar: 4";
169 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n");
171 ASSERT_TRUE(it.GetNext());
172 EXPECT_EQ(std::string("foo"), it.name());
173 EXPECT_EQ(std::string("1"), it.values());
175 ASSERT_TRUE(it.GetNext());
176 EXPECT_EQ(std::string("bar"), it.name());
177 EXPECT_EQ(std::string("4"), it.values());
179 EXPECT_FALSE(it.GetNext());
182 TEST(HttpUtilTest, HeadersIterator_AdvanceTo) {
183 std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4";
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
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"));
207 TEST(HttpUtilTest, ValuesIterator) {
208 std::string values = " must-revalidate, no-cache=\"foo, bar\"\t, private ";
210 HttpUtil::ValuesIterator it(values.begin(), values.end(), ',');
212 ASSERT_TRUE(it.GetNext());
213 EXPECT_EQ(std::string("must-revalidate"), it.value());
215 ASSERT_TRUE(it.GetNext());
216 EXPECT_EQ(std::string("no-cache=\"foo, bar\""), it.value());
218 ASSERT_TRUE(it.GetNext());
219 EXPECT_EQ(std::string("private"), it.value());
221 EXPECT_FALSE(it.GetNext());
224 TEST(HttpUtilTest, ValuesIterator_Blanks) {
225 std::string values = " \t ";
227 HttpUtil::ValuesIterator it(values.begin(), values.end(), ',');
229 EXPECT_FALSE(it.GetNext());
232 TEST(HttpUtilTest, Unquote) {
233 // Replace <backslash> " with ".
234 EXPECT_STREQ("xyz\"abc", HttpUtil::Unquote("\"xyz\\\"abc\"").c_str());
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());
241 // Replace <backslash> X with X
242 EXPECT_STREQ("xyzXabc", HttpUtil::Unquote("\"xyz\\Xabc\"").c_str());
244 // Act as identity function on unquoted inputs.
245 EXPECT_STREQ("X", HttpUtil::Unquote("X").c_str());
246 EXPECT_STREQ("\"", HttpUtil::Unquote("\"").c_str());
248 // Allow single quotes to act as quote marks.
249 // Not part of RFC 2616.
250 EXPECT_STREQ("x\"", HttpUtil::Unquote("'x\"'").c_str());
253 TEST(HttpUtilTest, Quote) {
254 EXPECT_STREQ("\"xyz\\\"abc\"", HttpUtil::Quote("xyz\"abc").c_str());
256 // Replace <backslash> <backslash> with <backslash>
257 EXPECT_STREQ("\"xyz\\\\abc\"", HttpUtil::Quote("xyz\\abc").c_str());
259 // Replace <backslash> X with X
260 EXPECT_STREQ("\"xyzXabc\"", HttpUtil::Quote("xyzXabc").c_str());
263 TEST(HttpUtilTest, LocateEndOfHeaders) {
264 struct {
265 const char* const input;
266 int expected_result;
267 } tests[] = {
268 {"\r\n", -1},
269 {"\n", -1},
270 {"\r", -1},
271 {"foo", -1},
272 {"\r\n\r\n", 4},
273 {"foo\r\nbar\r\n\r\n", 12},
274 {"foo\nbar\n\n", 9},
275 {"foo\r\nbar\r\n\r\njunk", 12},
276 {"foo\nbar\n\njunk", 9},
277 {"foo\nbar\n\r\njunk", 10},
278 {"foo\nbar\r\n\njunk", 10},
280 for (size_t i = 0; i < arraysize(tests); ++i) {
281 int input_len = static_cast<int>(strlen(tests[i].input));
282 int eoh = HttpUtil::LocateEndOfHeaders(tests[i].input, input_len);
283 EXPECT_EQ(tests[i].expected_result, eoh);
287 TEST(HttpUtilTest, LocateEndOfAdditionalHeaders) {
288 struct {
289 const char* const input;
290 int expected_result;
291 } tests[] = {
292 {"\r\n", 2},
293 {"\n", 1},
294 {"\r", -1},
295 {"foo", -1},
296 {"\r\n\r\n", 2},
297 {"foo\r\nbar\r\n\r\n", 12},
298 {"foo\nbar\n\n", 9},
299 {"foo\r\nbar\r\n\r\njunk", 12},
300 {"foo\nbar\n\njunk", 9},
301 {"foo\nbar\n\r\njunk", 10},
302 {"foo\nbar\r\n\njunk", 10},
304 for (size_t i = 0; i < arraysize(tests); ++i) {
305 int input_len = static_cast<int>(strlen(tests[i].input));
306 int eoh = HttpUtil::LocateEndOfAdditionalHeaders(tests[i].input, input_len);
307 EXPECT_EQ(tests[i].expected_result, eoh);
310 TEST(HttpUtilTest, AssembleRawHeaders) {
311 struct {
312 const char* const input; // with '|' representing '\0'
313 const char* const expected_result; // with '\0' changed to '|'
314 } tests[] = {
315 { "HTTP/1.0 200 OK\r\nFoo: 1\r\nBar: 2\r\n\r\n",
316 "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
318 { "HTTP/1.0 200 OK\nFoo: 1\nBar: 2\n\n",
319 "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" },
321 // Valid line continuation (single SP).
323 "HTTP/1.0 200 OK\n"
324 "Foo: 1\n"
325 " continuation\n"
326 "Bar: 2\n\n",
328 "HTTP/1.0 200 OK|"
329 "Foo: 1 continuation|"
330 "Bar: 2||"
333 // Valid line continuation (single HT).
335 "HTTP/1.0 200 OK\n"
336 "Foo: 1\n"
337 "\tcontinuation\n"
338 "Bar: 2\n\n",
340 "HTTP/1.0 200 OK|"
341 "Foo: 1 continuation|"
342 "Bar: 2||"
345 // Valid line continuation (multiple SP).
347 "HTTP/1.0 200 OK\n"
348 "Foo: 1\n"
349 " continuation\n"
350 "Bar: 2\n\n",
352 "HTTP/1.0 200 OK|"
353 "Foo: 1 continuation|"
354 "Bar: 2||"
357 // Valid line continuation (multiple HT).
359 "HTTP/1.0 200 OK\n"
360 "Foo: 1\n"
361 "\t\t\tcontinuation\n"
362 "Bar: 2\n\n",
364 "HTTP/1.0 200 OK|"
365 "Foo: 1 continuation|"
366 "Bar: 2||"
369 // Valid line continuation (mixed HT, SP).
371 "HTTP/1.0 200 OK\n"
372 "Foo: 1\n"
373 " \t \t continuation\n"
374 "Bar: 2\n\n",
376 "HTTP/1.0 200 OK|"
377 "Foo: 1 continuation|"
378 "Bar: 2||"
381 // Valid multi-line continuation
383 "HTTP/1.0 200 OK\n"
384 "Foo: 1\n"
385 " continuation1\n"
386 "\tcontinuation2\n"
387 " continuation3\n"
388 "Bar: 2\n\n",
390 "HTTP/1.0 200 OK|"
391 "Foo: 1 continuation1 continuation2 continuation3|"
392 "Bar: 2||"
395 // Continuation of quoted value.
396 // This is different from what Firefox does, since it
397 // will preserve the LWS.
399 "HTTP/1.0 200 OK\n"
400 "Etag: \"34534-d3\n"
401 " 134q\"\n"
402 "Bar: 2\n\n",
404 "HTTP/1.0 200 OK|"
405 "Etag: \"34534-d3 134q\"|"
406 "Bar: 2||"
409 // Valid multi-line continuation, full LWS lines
411 "HTTP/1.0 200 OK\n"
412 "Foo: 1\n"
413 " \n"
414 "\t\t\t\t\n"
415 "\t continuation\n"
416 "Bar: 2\n\n",
418 // One SP per continued line = 3.
419 "HTTP/1.0 200 OK|"
420 "Foo: 1 continuation|"
421 "Bar: 2||"
424 // Valid multi-line continuation, all LWS
426 "HTTP/1.0 200 OK\n"
427 "Foo: 1\n"
428 " \n"
429 "\t\t\t\t\n"
430 "\t \n"
431 "Bar: 2\n\n",
433 // One SP per continued line = 3.
434 "HTTP/1.0 200 OK|"
435 "Foo: 1 |"
436 "Bar: 2||"
439 // Valid line continuation (No value bytes in first line).
441 "HTTP/1.0 200 OK\n"
442 "Foo:\n"
443 " value\n"
444 "Bar: 2\n\n",
446 "HTTP/1.0 200 OK|"
447 "Foo: value|"
448 "Bar: 2||"
451 // Not a line continuation (can't continue status line).
453 "HTTP/1.0 200 OK\n"
454 " Foo: 1\n"
455 "Bar: 2\n\n",
457 "HTTP/1.0 200 OK|"
458 " Foo: 1|"
459 "Bar: 2||"
462 // Not a line continuation (can't continue status line).
464 "HTTP/1.0\n"
465 " 200 OK\n"
466 "Foo: 1\n"
467 "Bar: 2\n\n",
469 "HTTP/1.0|"
470 " 200 OK|"
471 "Foo: 1|"
472 "Bar: 2||"
475 // Not a line continuation (can't continue status line).
477 "HTTP/1.0 404\n"
478 " Not Found\n"
479 "Foo: 1\n"
480 "Bar: 2\n\n",
482 "HTTP/1.0 404|"
483 " Not Found|"
484 "Foo: 1|"
485 "Bar: 2||"
488 // Unterminated status line.
490 "HTTP/1.0 200 OK",
492 "HTTP/1.0 200 OK||"
495 // Single terminated, with headers
497 "HTTP/1.0 200 OK\n"
498 "Foo: 1\n"
499 "Bar: 2\n",
501 "HTTP/1.0 200 OK|"
502 "Foo: 1|"
503 "Bar: 2||"
506 // Not terminated, with headers
508 "HTTP/1.0 200 OK\n"
509 "Foo: 1\n"
510 "Bar: 2",
512 "HTTP/1.0 200 OK|"
513 "Foo: 1|"
514 "Bar: 2||"
517 // Not a line continuation (VT)
519 "HTTP/1.0 200 OK\n"
520 "Foo: 1\n"
521 "\vInvalidContinuation\n"
522 "Bar: 2\n\n",
524 "HTTP/1.0 200 OK|"
525 "Foo: 1|"
526 "\vInvalidContinuation|"
527 "Bar: 2||"
530 // Not a line continuation (formfeed)
532 "HTTP/1.0 200 OK\n"
533 "Foo: 1\n"
534 "\fInvalidContinuation\n"
535 "Bar: 2\n\n",
537 "HTTP/1.0 200 OK|"
538 "Foo: 1|"
539 "\fInvalidContinuation|"
540 "Bar: 2||"
543 // Not a line continuation -- can't continue header names.
545 "HTTP/1.0 200 OK\n"
546 "Serv\n"
547 " er: Apache\n"
548 "\tInvalidContinuation\n"
549 "Bar: 2\n\n",
551 "HTTP/1.0 200 OK|"
552 "Serv|"
553 " er: Apache|"
554 "\tInvalidContinuation|"
555 "Bar: 2||"
558 // Not a line continuation -- no value to continue.
560 "HTTP/1.0 200 OK\n"
561 "Foo: 1\n"
562 "garbage\n"
563 " not-a-continuation\n"
564 "Bar: 2\n\n",
566 "HTTP/1.0 200 OK|"
567 "Foo: 1|"
568 "garbage|"
569 " not-a-continuation|"
570 "Bar: 2||",
573 // Not a line continuation -- no valid name.
575 "HTTP/1.0 200 OK\n"
576 ": 1\n"
577 " garbage\n"
578 "Bar: 2\n\n",
580 "HTTP/1.0 200 OK|"
581 ": 1|"
582 " garbage|"
583 "Bar: 2||",
586 // Not a line continuation -- no valid name (whitespace)
588 "HTTP/1.0 200 OK\n"
589 " : 1\n"
590 " garbage\n"
591 "Bar: 2\n\n",
593 "HTTP/1.0 200 OK|"
594 " : 1|"
595 " garbage|"
596 "Bar: 2||",
599 // Embed NULLs in the status line. They should not be understood
600 // as line separators.
602 "HTTP/1.0 200 OK|Bar2:0|Baz2:1\r\nFoo: 1\r\nBar: 2\r\n\r\n",
603 "HTTP/1.0 200 OKBar2:0Baz2:1|Foo: 1|Bar: 2||"
606 // Embed NULLs in a header line. They should not be understood as
607 // line separators.
609 "HTTP/1.0 200 OK\nFoo: 1|Foo2: 3\nBar: 2\n\n",
610 "HTTP/1.0 200 OK|Foo: 1Foo2: 3|Bar: 2||"
613 for (size_t i = 0; i < arraysize(tests); ++i) {
614 std::string input = tests[i].input;
615 std::replace(input.begin(), input.end(), '|', '\0');
616 std::string raw = HttpUtil::AssembleRawHeaders(input.data(), input.size());
617 std::replace(raw.begin(), raw.end(), '\0', '|');
618 EXPECT_EQ(tests[i].expected_result, raw);
622 // Test SpecForRequest().
623 TEST(HttpUtilTest, RequestUrlSanitize) {
624 struct {
625 const char* const url;
626 const char* const expected_spec;
627 } tests[] = {
628 { // Check that #hash is removed.
629 "http://www.google.com:78/foobar?query=1#hash",
630 "http://www.google.com:78/foobar?query=1",
632 { // The reference may itself contain # -- strip all of it.
633 "http://192.168.0.1?query=1#hash#10#11#13#14",
634 "http://192.168.0.1/?query=1",
636 { // Strip username/password.
637 "http://user:pass@google.com",
638 "http://google.com/",
640 { // https scheme
641 "https://www.google.com:78/foobar?query=1#hash",
642 "https://www.google.com:78/foobar?query=1",
644 { // WebSocket's ws scheme
645 "ws://www.google.com:78/foobar?query=1#hash",
646 "ws://www.google.com:78/foobar?query=1",
648 { // WebSocket's wss scheme
649 "wss://www.google.com:78/foobar?query=1#hash",
650 "wss://www.google.com:78/foobar?query=1",
653 for (size_t i = 0; i < arraysize(tests); ++i) {
654 SCOPED_TRACE(i);
656 GURL url(GURL(tests[i].url));
657 std::string expected_spec(tests[i].expected_spec);
659 EXPECT_EQ(expected_spec, HttpUtil::SpecForRequest(url));
663 // Test SpecForRequest() for "ftp" scheme.
664 TEST(HttpUtilTest, SpecForRequestForUrlWithFtpScheme) {
665 GURL ftp_url("ftp://user:pass@google.com/pub/chromium/");
666 EXPECT_EQ("ftp://google.com/pub/chromium/",
667 HttpUtil::SpecForRequest(ftp_url));
670 TEST(HttpUtilTest, GenerateAcceptLanguageHeader) {
671 EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6"),
672 HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de"));
673 EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6,ko;q=0.4,zh-CN;q=0.2,"
674 "ja;q=0.2"),
675 HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de,ko,zh-CN,ja"));
678 // HttpResponseHeadersTest.GetMimeType also tests ParseContentType.
679 TEST(HttpUtilTest, ParseContentType) {
680 const struct {
681 const char* const content_type;
682 const char* const expected_mime_type;
683 const char* const expected_charset;
684 const bool expected_had_charset;
685 const char* const expected_boundary;
686 } tests[] = {
687 { "text/html; charset=utf-8",
688 "text/html",
689 "utf-8",
690 true,
693 { "text/html; charset =utf-8",
694 "text/html",
695 "utf-8",
696 true,
699 { "text/html; charset= utf-8",
700 "text/html",
701 "utf-8",
702 true,
705 { "text/html; charset=utf-8 ",
706 "text/html",
707 "utf-8",
708 true,
711 { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs\"",
712 "text/html",
714 false,
715 "\"WebKit-ada-df-dsf-adsfadsfs\""
717 { "text/html; boundary =\"WebKit-ada-df-dsf-adsfadsfs\"",
718 "text/html",
720 false,
721 "\"WebKit-ada-df-dsf-adsfadsfs\""
723 { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\"",
724 "text/html",
726 false,
727 "\"WebKit-ada-df-dsf-adsfadsfs\""
729 { "text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\" ",
730 "text/html",
732 false,
733 "\"WebKit-ada-df-dsf-adsfadsfs\""
735 { "text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs \"",
736 "text/html",
738 false,
739 "\"WebKit-ada-df-dsf-adsfadsfs \""
741 { "text/html; boundary=WebKit-ada-df-dsf-adsfadsfs",
742 "text/html",
744 false,
745 "WebKit-ada-df-dsf-adsfadsfs"
747 // TODO(abarth): Add more interesting test cases.
749 for (size_t i = 0; i < arraysize(tests); ++i) {
750 std::string mime_type;
751 std::string charset;
752 bool had_charset = false;
753 std::string boundary;
754 HttpUtil::ParseContentType(tests[i].content_type, &mime_type, &charset,
755 &had_charset, &boundary);
756 EXPECT_EQ(tests[i].expected_mime_type, mime_type) << "i=" << i;
757 EXPECT_EQ(tests[i].expected_charset, charset) << "i=" << i;
758 EXPECT_EQ(tests[i].expected_had_charset, had_charset) << "i=" << i;
759 EXPECT_EQ(tests[i].expected_boundary, boundary) << "i=" << i;
763 TEST(HttpUtilTest, ParseRanges) {
764 const struct {
765 const char* const headers;
766 bool expected_return_value;
767 size_t expected_ranges_size;
768 const struct {
769 int64 expected_first_byte_position;
770 int64 expected_last_byte_position;
771 int64 expected_suffix_length;
772 } expected_ranges[10];
773 } tests[] = {
774 { "Range: bytes=0-10",
775 true,
777 { {0, 10, -1}, }
779 { "Range: bytes=10-0",
780 false,
784 { "Range: BytES=0-10",
785 true,
787 { {0, 10, -1}, }
789 { "Range: megabytes=0-10",
790 false,
794 { "Range: bytes0-10",
795 false,
799 { "Range: bytes=0-0,0-10,10-20,100-200,100-,-200",
800 true,
802 { {0, 0, -1},
803 {0, 10, -1},
804 {10, 20, -1},
805 {100, 200, -1},
806 {100, -1, -1},
807 {-1, -1, 200},
810 { "Range: bytes=0-10\r\n"
811 "Range: bytes=0-10,10-20,100-200,100-,-200",
812 true,
814 { {0, 10, -1}
817 { "Range: bytes=",
818 false,
822 { "Range: bytes=-",
823 false,
827 { "Range: bytes=0-10-",
828 false,
832 { "Range: bytes=-0-10",
833 false,
837 { "Range: bytes =0-10\r\n",
838 true,
840 { {0, 10, -1}
843 { "Range: bytes= 0-10 \r\n",
844 true,
846 { {0, 10, -1}
849 { "Range: bytes = 0 - 10 \r\n",
850 true,
852 { {0, 10, -1}
855 { "Range: bytes= 0-1 0\r\n",
856 false,
860 { "Range: bytes= 0- -10\r\n",
861 false,
865 { "Range: bytes= 0 - 1 , 10 -20, 100- 200 , 100-, -200 \r\n",
866 true,
868 { {0, 1, -1},
869 {10, 20, -1},
870 {100, 200, -1},
871 {100, -1, -1},
872 {-1, -1, 200},
877 for (size_t i = 0; i < arraysize(tests); ++i) {
878 std::vector<HttpByteRange> ranges;
879 bool return_value = HttpUtil::ParseRanges(std::string(tests[i].headers),
880 &ranges);
881 EXPECT_EQ(tests[i].expected_return_value, return_value);
882 if (return_value) {
883 EXPECT_EQ(tests[i].expected_ranges_size, ranges.size());
884 for (size_t j = 0; j < ranges.size(); ++j) {
885 EXPECT_EQ(tests[i].expected_ranges[j].expected_first_byte_position,
886 ranges[j].first_byte_position());
887 EXPECT_EQ(tests[i].expected_ranges[j].expected_last_byte_position,
888 ranges[j].last_byte_position());
889 EXPECT_EQ(tests[i].expected_ranges[j].expected_suffix_length,
890 ranges[j].suffix_length());
896 TEST(HttpUtilTest, ParseRetryAfterHeader) {
897 base::Time::Exploded now_exploded = { 2014, 11, -1, 5, 22, 39, 30, 0 };
898 base::Time now = base::Time::FromUTCExploded(now_exploded);
900 base::Time::Exploded later_exploded = { 2015, 1, -1, 1, 12, 34, 56, 0 };
901 base::Time later = base::Time::FromUTCExploded(later_exploded);
903 const struct {
904 const char* retry_after_string;
905 bool expected_return_value;
906 base::TimeDelta expected_retry_after;
907 } tests[] = {
908 { "", false, base::TimeDelta() },
909 { "-3", false, base::TimeDelta() },
910 { "-2", false, base::TimeDelta() },
911 { "-1", false, base::TimeDelta() },
912 { "0", true, base::TimeDelta::FromSeconds(0) },
913 { "1", true, base::TimeDelta::FromSeconds(1) },
914 { "2", true, base::TimeDelta::FromSeconds(2) },
915 { "3", true, base::TimeDelta::FromSeconds(3) },
916 { "60", true, base::TimeDelta::FromSeconds(60) },
917 { "3600", true, base::TimeDelta::FromSeconds(3600) },
918 { "86400", true, base::TimeDelta::FromSeconds(86400) },
919 { "Thu, 1 Jan 2015 12:34:56 GMT", true, later - now },
920 { "Mon, 1 Jan 1900 12:34:56 GMT", false, base::TimeDelta() }
923 for (size_t i = 0; i < arraysize(tests); ++i) {
924 base::TimeDelta retry_after;
925 bool return_value = HttpUtil::ParseRetryAfterHeader(
926 tests[i].retry_after_string, now, &retry_after);
927 EXPECT_EQ(tests[i].expected_return_value, return_value)
928 << "Test case " << i << ": expected " << tests[i].expected_return_value
929 << " but got " << return_value << ".";
930 if (tests[i].expected_return_value && return_value) {
931 EXPECT_EQ(tests[i].expected_retry_after, retry_after)
932 << "Test case " << i << ": expected "
933 << tests[i].expected_retry_after.InSeconds() << "s but got "
934 << retry_after.InSeconds() << "s.";
939 namespace {
940 void CheckCurrentNameValuePair(HttpUtil::NameValuePairsIterator* parser,
941 bool expect_valid,
942 std::string expected_name,
943 std::string expected_value) {
944 ASSERT_EQ(expect_valid, parser->valid());
945 if (!expect_valid) {
946 return;
949 // Let's make sure that these never change (i.e., when a quoted value is
950 // unquoted, it should be cached on the first calls and not regenerated
951 // later).
952 std::string::const_iterator first_value_begin = parser->value_begin();
953 std::string::const_iterator first_value_end = parser->value_end();
955 ASSERT_EQ(expected_name, std::string(parser->name_begin(),
956 parser->name_end()));
957 ASSERT_EQ(expected_name, parser->name());
958 ASSERT_EQ(expected_value, std::string(parser->value_begin(),
959 parser->value_end()));
960 ASSERT_EQ(expected_value, parser->value());
962 // Make sure they didn't/don't change.
963 ASSERT_TRUE(first_value_begin == parser->value_begin());
964 ASSERT_TRUE(first_value_end == parser->value_end());
967 void CheckNextNameValuePair(HttpUtil::NameValuePairsIterator* parser,
968 bool expect_next,
969 bool expect_valid,
970 std::string expected_name,
971 std::string expected_value) {
972 ASSERT_EQ(expect_next, parser->GetNext());
973 ASSERT_EQ(expect_valid, parser->valid());
974 if (!expect_next || !expect_valid) {
975 return;
978 CheckCurrentNameValuePair(parser,
979 expect_valid,
980 expected_name,
981 expected_value);
984 void CheckInvalidNameValuePair(std::string valid_part,
985 std::string invalid_part) {
986 std::string whole_string = valid_part + invalid_part;
988 HttpUtil::NameValuePairsIterator valid_parser(valid_part.begin(),
989 valid_part.end(),
990 ';');
991 HttpUtil::NameValuePairsIterator invalid_parser(whole_string.begin(),
992 whole_string.end(),
993 ';');
995 ASSERT_TRUE(valid_parser.valid());
996 ASSERT_TRUE(invalid_parser.valid());
998 // Both parsers should return all the same values until "valid_parser" is
999 // exhausted.
1000 while (valid_parser.GetNext()) {
1001 ASSERT_TRUE(invalid_parser.GetNext());
1002 ASSERT_TRUE(valid_parser.valid());
1003 ASSERT_TRUE(invalid_parser.valid());
1004 ASSERT_EQ(valid_parser.name(), invalid_parser.name());
1005 ASSERT_EQ(valid_parser.value(), invalid_parser.value());
1008 // valid_parser is exhausted and remains 'valid'
1009 ASSERT_TRUE(valid_parser.valid());
1011 // invalid_parser's corresponding call to GetNext also returns false...
1012 ASSERT_FALSE(invalid_parser.GetNext());
1013 // ...but the parser is in an invalid state.
1014 ASSERT_FALSE(invalid_parser.valid());
1017 } // namespace
1019 TEST(HttpUtilTest, NameValuePairsIteratorCopyAndAssign) {
1020 std::string data = "alpha='\\'a\\''; beta=\" b \"; cappa='c;'; delta=\"d\"";
1021 HttpUtil::NameValuePairsIterator parser_a(data.begin(), data.end(), ';');
1023 EXPECT_TRUE(parser_a.valid());
1024 ASSERT_NO_FATAL_FAILURE(
1025 CheckNextNameValuePair(&parser_a, true, true, "alpha", "'a'"));
1027 HttpUtil::NameValuePairsIterator parser_b(parser_a);
1028 // a and b now point to same location
1029 ASSERT_NO_FATAL_FAILURE(
1030 CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'"));
1031 ASSERT_NO_FATAL_FAILURE(
1032 CheckCurrentNameValuePair(&parser_a, true, "alpha", "'a'"));
1034 // advance a, no effect on b
1035 ASSERT_NO_FATAL_FAILURE(
1036 CheckNextNameValuePair(&parser_a, true, true, "beta", " b "));
1037 ASSERT_NO_FATAL_FAILURE(
1038 CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'"));
1040 // assign b the current state of a, no effect on a
1041 parser_b = parser_a;
1042 ASSERT_NO_FATAL_FAILURE(
1043 CheckCurrentNameValuePair(&parser_b, true, "beta", " b "));
1044 ASSERT_NO_FATAL_FAILURE(
1045 CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
1047 // advance b, no effect on a
1048 ASSERT_NO_FATAL_FAILURE(
1049 CheckNextNameValuePair(&parser_b, true, true, "cappa", "c;"));
1050 ASSERT_NO_FATAL_FAILURE(
1051 CheckCurrentNameValuePair(&parser_a, true, "beta", " b "));
1054 TEST(HttpUtilTest, NameValuePairsIteratorEmptyInput) {
1055 std::string data;
1056 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
1058 EXPECT_TRUE(parser.valid());
1059 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1060 &parser, false, true, std::string(), std::string()));
1063 TEST(HttpUtilTest, NameValuePairsIterator) {
1064 std::string data = "alpha=1; beta= 2 ;cappa =' 3; ';"
1065 "delta= \" \\\"4\\\" \"; e= \" '5'\"; e=6;"
1066 "f='\\'\\h\\e\\l\\l\\o\\ \\w\\o\\r\\l\\d\\'';"
1067 "g=''; h='hello'";
1068 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
1069 EXPECT_TRUE(parser.valid());
1071 ASSERT_NO_FATAL_FAILURE(
1072 CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
1073 ASSERT_NO_FATAL_FAILURE(
1074 CheckNextNameValuePair(&parser, true, true, "beta", "2"));
1075 ASSERT_NO_FATAL_FAILURE(
1076 CheckNextNameValuePair(&parser, true, true, "cappa", " 3; "));
1077 ASSERT_NO_FATAL_FAILURE(
1078 CheckNextNameValuePair(&parser, true, true, "delta", " \"4\" "));
1079 ASSERT_NO_FATAL_FAILURE(
1080 CheckNextNameValuePair(&parser, true, true, "e", " '5'"));
1081 ASSERT_NO_FATAL_FAILURE(
1082 CheckNextNameValuePair(&parser, true, true, "e", "6"));
1083 ASSERT_NO_FATAL_FAILURE(
1084 CheckNextNameValuePair(&parser, true, true, "f", "'hello world'"));
1085 ASSERT_NO_FATAL_FAILURE(
1086 CheckNextNameValuePair(&parser, true, true, "g", std::string()));
1087 ASSERT_NO_FATAL_FAILURE(
1088 CheckNextNameValuePair(&parser, true, true, "h", "hello"));
1089 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1090 &parser, false, true, std::string(), std::string()));
1093 TEST(HttpUtilTest, NameValuePairsIteratorOptionalValues) {
1094 std::string data = "alpha=1; beta;cappa ; delta; e ; f=1";
1095 // Test that the default parser requires values.
1096 HttpUtil::NameValuePairsIterator default_parser(data.begin(), data.end(),
1097 ';');
1098 EXPECT_TRUE(default_parser.valid());
1099 ASSERT_NO_FATAL_FAILURE(
1100 CheckNextNameValuePair(&default_parser, true, true, "alpha", "1"));
1101 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&default_parser, false, false,
1102 std::string(), std::string()));
1104 HttpUtil::NameValuePairsIterator values_required_parser(
1105 data.begin(), data.end(), ';',
1106 HttpUtil::NameValuePairsIterator::VALUES_NOT_OPTIONAL);
1107 EXPECT_TRUE(values_required_parser.valid());
1108 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&values_required_parser, true,
1109 true, "alpha", "1"));
1110 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1111 &values_required_parser, false, false, std::string(), std::string()));
1113 HttpUtil::NameValuePairsIterator parser(
1114 data.begin(), data.end(), ';',
1115 HttpUtil::NameValuePairsIterator::VALUES_OPTIONAL);
1116 EXPECT_TRUE(parser.valid());
1118 ASSERT_NO_FATAL_FAILURE(
1119 CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
1120 ASSERT_NO_FATAL_FAILURE(
1121 CheckNextNameValuePair(&parser, true, true, "beta", std::string()));
1122 ASSERT_NO_FATAL_FAILURE(
1123 CheckNextNameValuePair(&parser, true, true, "cappa", std::string()));
1124 ASSERT_NO_FATAL_FAILURE(
1125 CheckNextNameValuePair(&parser, true, true, "delta", std::string()));
1126 ASSERT_NO_FATAL_FAILURE(
1127 CheckNextNameValuePair(&parser, true, true, "e", std::string()));
1128 ASSERT_NO_FATAL_FAILURE(
1129 CheckNextNameValuePair(&parser, true, true, "f", "1"));
1130 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(&parser, false, true,
1131 std::string(), std::string()));
1132 EXPECT_TRUE(parser.valid());
1135 TEST(HttpUtilTest, NameValuePairsIteratorIllegalInputs) {
1136 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; beta"));
1137 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "beta"));
1139 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; 'beta'=2"));
1140 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair(std::string(), "'beta'=2"));
1141 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";beta="));
1142 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1",
1143 ";beta=;cappa=2"));
1145 // According to the spec this is an error, but it doesn't seem appropriate to
1146 // change our behaviour to be less permissive at this time.
1147 // See NameValuePairsIteratorExtraSeparators test
1148 // ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";; beta=2"));
1151 // If we are going to support extra separators against the spec, let's just make
1152 // sure they work rationally.
1153 TEST(HttpUtilTest, NameValuePairsIteratorExtraSeparators) {
1154 std::string data = " ; ;;alpha=1; ;; ; beta= 2;cappa=3;;; ; ";
1155 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
1156 EXPECT_TRUE(parser.valid());
1158 ASSERT_NO_FATAL_FAILURE(
1159 CheckNextNameValuePair(&parser, true, true, "alpha", "1"));
1160 ASSERT_NO_FATAL_FAILURE(
1161 CheckNextNameValuePair(&parser, true, true, "beta", "2"));
1162 ASSERT_NO_FATAL_FAILURE(
1163 CheckNextNameValuePair(&parser, true, true, "cappa", "3"));
1164 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1165 &parser, false, true, std::string(), std::string()));
1168 // See comments on the implementation of NameValuePairsIterator::GetNext
1169 // regarding this derogation from the spec.
1170 TEST(HttpUtilTest, NameValuePairsIteratorMissingEndQuote) {
1171 std::string data = "name='value";
1172 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';');
1173 EXPECT_TRUE(parser.valid());
1175 ASSERT_NO_FATAL_FAILURE(
1176 CheckNextNameValuePair(&parser, true, true, "name", "value"));
1177 ASSERT_NO_FATAL_FAILURE(CheckNextNameValuePair(
1178 &parser, false, true, std::string(), std::string()));
1181 } // namespace net