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