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 { // 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. 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 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. 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 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 { 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 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 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 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 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 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 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. 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. 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