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 } 1052