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