1 // Copyright (c) 2006-2009 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 "net/http/http_util.h" 9 #include "testing/gtest/include/gtest/gtest.h" 10 11 using net::HttpUtil; 12 13 namespace { 14 class HttpUtilTest : public testing::Test {}; 15 } 16 17 TEST(HttpUtilTest, HasHeader) { 18 static const struct { 19 const char* headers; 20 const char* name; 21 bool expected_result; 22 } tests[] = { 23 { "", "foo", false }, 24 { "foo\r\nbar", "foo", false }, 25 { "ffoo: 1", "foo", false }, 26 { "foo: 1", "foo", true }, 27 { "foo: 1\r\nbar: 2", "foo", true }, 28 { "fOO: 1\r\nbar: 2", "foo", true }, 29 { "g: 0\r\nfoo: 1\r\nbar: 2", "foo", true }, 30 }; 31 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 32 bool result = HttpUtil::HasHeader(tests[i].headers, tests[i].name); 33 EXPECT_EQ(tests[i].expected_result, result); 34 } 35 } 36 37 TEST(HttpUtilTest, StripHeaders) { 38 static const char* headers = 39 "Origin: origin\r\n" 40 "Content-Type: text/plain\r\n" 41 "Cookies: foo1\r\n" 42 "Custom: baz\r\n" 43 "COOKIES: foo2\r\n" 44 "Server: Apache\r\n" 45 "OrIGin: origin2\r\n"; 46 47 static const char* header_names[] = { 48 "origin", "content-type", "cookies" 49 }; 50 51 static const char* expected_stripped_headers = 52 "Custom: baz\r\n" 53 "Server: Apache\r\n"; 54 55 EXPECT_EQ(expected_stripped_headers, 56 HttpUtil::StripHeaders(headers, header_names, 57 arraysize(header_names))); 58 } 59 60 TEST(HttpUtilTest, HeadersIterator) { 61 std::string headers = "foo: 1\t\r\nbar: hello world\r\nbaz: 3 \r\n"; 62 63 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n"); 64 65 ASSERT_TRUE(it.GetNext()); 66 EXPECT_EQ(std::string("foo"), it.name()); 67 EXPECT_EQ(std::string("1"), it.values()); 68 69 ASSERT_TRUE(it.GetNext()); 70 EXPECT_EQ(std::string("bar"), it.name()); 71 EXPECT_EQ(std::string("hello world"), it.values()); 72 73 ASSERT_TRUE(it.GetNext()); 74 EXPECT_EQ(std::string("baz"), it.name()); 75 EXPECT_EQ(std::string("3"), it.values()); 76 77 EXPECT_FALSE(it.GetNext()); 78 } 79 80 TEST(HttpUtilTest, HeadersIterator_MalformedLine) { 81 std::string headers = "foo: 1\n: 2\n3\nbar: 4"; 82 83 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n"); 84 85 ASSERT_TRUE(it.GetNext()); 86 EXPECT_EQ(std::string("foo"), it.name()); 87 EXPECT_EQ(std::string("1"), it.values()); 88 89 ASSERT_TRUE(it.GetNext()); 90 EXPECT_EQ(std::string("bar"), it.name()); 91 EXPECT_EQ(std::string("4"), it.values()); 92 93 EXPECT_FALSE(it.GetNext()); 94 } 95 96 TEST(HttpUtilTest, HeadersIterator_AdvanceTo) { 97 std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4"; 98 99 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n"); 100 EXPECT_TRUE(it.AdvanceTo("foo")); 101 EXPECT_EQ("foo", it.name()); 102 EXPECT_TRUE(it.AdvanceTo("bar")); 103 EXPECT_EQ("bar", it.name()); 104 EXPECT_FALSE(it.AdvanceTo("blat")); 105 EXPECT_FALSE(it.GetNext()); // should be at end of headers 106 } 107 108 TEST(HttpUtilTest, HeadersIterator_Reset) { 109 std::string headers = "foo: 1\r\n: 2\r\n3\r\nbar: 4"; 110 HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\r\n"); 111 // Search past "foo". 112 EXPECT_TRUE(it.AdvanceTo("bar")); 113 // Now try advancing to "foo". This time it should fail since the iterator 114 // position is past it. 115 EXPECT_FALSE(it.AdvanceTo("foo")); 116 it.Reset(); 117 // Now that we reset the iterator position, we should find 'foo' 118 EXPECT_TRUE(it.AdvanceTo("foo")); 119 } 120 121 TEST(HttpUtilTest, ValuesIterator) { 122 std::string values = " must-revalidate, no-cache=\"foo, bar\"\t, private "; 123 124 HttpUtil::ValuesIterator it(values.begin(), values.end(), ','); 125 126 ASSERT_TRUE(it.GetNext()); 127 EXPECT_EQ(std::string("must-revalidate"), it.value()); 128 129 ASSERT_TRUE(it.GetNext()); 130 EXPECT_EQ(std::string("no-cache=\"foo, bar\""), it.value()); 131 132 ASSERT_TRUE(it.GetNext()); 133 EXPECT_EQ(std::string("private"), it.value()); 134 135 EXPECT_FALSE(it.GetNext()); 136 } 137 138 TEST(HttpUtilTest, ValuesIterator_Blanks) { 139 std::string values = " \t "; 140 141 HttpUtil::ValuesIterator it(values.begin(), values.end(), ','); 142 143 EXPECT_FALSE(it.GetNext()); 144 } 145 146 TEST(HttpUtilTest, Unquote) { 147 // Replace <backslash> " with ". 148 EXPECT_STREQ("xyz\"abc", HttpUtil::Unquote("\"xyz\\\"abc\"").c_str()); 149 150 // Replace <backslash> <backslash> with <backslash> 151 EXPECT_STREQ("xyz\\abc", HttpUtil::Unquote("\"xyz\\\\abc\"").c_str()); 152 EXPECT_STREQ("xyz\\\\\\abc", 153 HttpUtil::Unquote("\"xyz\\\\\\\\\\\\abc\"").c_str()); 154 155 // Replace <backslash> X with X 156 EXPECT_STREQ("xyzXabc", HttpUtil::Unquote("\"xyz\\Xabc\"").c_str()); 157 158 // Act as identity function on unquoted inputs. 159 EXPECT_STREQ("X", HttpUtil::Unquote("X").c_str()); 160 EXPECT_STREQ("\"", HttpUtil::Unquote("\"").c_str()); 161 162 // Allow single quotes to act as quote marks. 163 // Not part of RFC 2616. 164 EXPECT_STREQ("x\"", HttpUtil::Unquote("'x\"'").c_str()); 165 } 166 167 TEST(HttpUtilTest, Quote) { 168 EXPECT_STREQ("\"xyz\\\"abc\"", HttpUtil::Quote("xyz\"abc").c_str()); 169 170 // Replace <backslash> <backslash> with <backslash> 171 EXPECT_STREQ("\"xyz\\\\abc\"", HttpUtil::Quote("xyz\\abc").c_str()); 172 173 // Replace <backslash> X with X 174 EXPECT_STREQ("\"xyzXabc\"", HttpUtil::Quote("xyzXabc").c_str()); 175 } 176 177 TEST(HttpUtilTest, LocateEndOfHeaders) { 178 struct { 179 const char* input; 180 int expected_result; 181 } tests[] = { 182 { "foo\r\nbar\r\n\r\n", 12 }, 183 { "foo\nbar\n\n", 9 }, 184 { "foo\r\nbar\r\n\r\njunk", 12 }, 185 { "foo\nbar\n\njunk", 9 }, 186 { "foo\nbar\n\r\njunk", 10 }, 187 { "foo\nbar\r\n\njunk", 10 }, 188 }; 189 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 190 int input_len = static_cast<int>(strlen(tests[i].input)); 191 int eoh = HttpUtil::LocateEndOfHeaders(tests[i].input, input_len); 192 EXPECT_EQ(tests[i].expected_result, eoh); 193 } 194 } 195 196 TEST(HttpUtilTest, AssembleRawHeaders) { 197 struct { 198 const char* input; 199 const char* expected_result; // with '\0' changed to '|' 200 } tests[] = { 201 { "HTTP/1.0 200 OK\r\nFoo: 1\r\nBar: 2\r\n\r\n", 202 "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" }, 203 204 { "HTTP/1.0 200 OK\nFoo: 1\nBar: 2\n\n", 205 "HTTP/1.0 200 OK|Foo: 1|Bar: 2||" }, 206 207 // Valid line continuation (single SP). 208 { 209 "HTTP/1.0 200 OK\n" 210 "Foo: 1\n" 211 " continuation\n" 212 "Bar: 2\n\n", 213 214 "HTTP/1.0 200 OK|" 215 "Foo: 1 continuation|" 216 "Bar: 2||" 217 }, 218 219 // Valid line continuation (single HT). 220 { 221 "HTTP/1.0 200 OK\n" 222 "Foo: 1\n" 223 "\tcontinuation\n" 224 "Bar: 2\n\n", 225 226 "HTTP/1.0 200 OK|" 227 "Foo: 1 continuation|" 228 "Bar: 2||" 229 }, 230 231 // Valid line continuation (multiple SP). 232 { 233 "HTTP/1.0 200 OK\n" 234 "Foo: 1\n" 235 " continuation\n" 236 "Bar: 2\n\n", 237 238 "HTTP/1.0 200 OK|" 239 "Foo: 1 continuation|" 240 "Bar: 2||" 241 }, 242 243 // Valid line continuation (multiple HT). 244 { 245 "HTTP/1.0 200 OK\n" 246 "Foo: 1\n" 247 "\t\t\tcontinuation\n" 248 "Bar: 2\n\n", 249 250 "HTTP/1.0 200 OK|" 251 "Foo: 1 continuation|" 252 "Bar: 2||" 253 }, 254 255 // Valid line continuation (mixed HT, SP). 256 { 257 "HTTP/1.0 200 OK\n" 258 "Foo: 1\n" 259 " \t \t continuation\n" 260 "Bar: 2\n\n", 261 262 "HTTP/1.0 200 OK|" 263 "Foo: 1 continuation|" 264 "Bar: 2||" 265 }, 266 267 // Valid multi-line continuation 268 { 269 "HTTP/1.0 200 OK\n" 270 "Foo: 1\n" 271 " continuation1\n" 272 "\tcontinuation2\n" 273 " continuation3\n" 274 "Bar: 2\n\n", 275 276 "HTTP/1.0 200 OK|" 277 "Foo: 1 continuation1 continuation2 continuation3|" 278 "Bar: 2||" 279 }, 280 281 // Continuation of quoted value. 282 // This is different from what Firefox does, since it 283 // will preserve the LWS. 284 { 285 "HTTP/1.0 200 OK\n" 286 "Etag: \"34534-d3\n" 287 " 134q\"\n" 288 "Bar: 2\n\n", 289 290 "HTTP/1.0 200 OK|" 291 "Etag: \"34534-d3 134q\"|" 292 "Bar: 2||" 293 }, 294 295 // Valid multi-line continuation, full LWS lines 296 { 297 "HTTP/1.0 200 OK\n" 298 "Foo: 1\n" 299 " \n" 300 "\t\t\t\t\n" 301 "\t continuation\n" 302 "Bar: 2\n\n", 303 304 // One SP per continued line = 3. 305 "HTTP/1.0 200 OK|" 306 "Foo: 1 continuation|" 307 "Bar: 2||" 308 }, 309 310 // Valid multi-line continuation, all LWS 311 { 312 "HTTP/1.0 200 OK\n" 313 "Foo: 1\n" 314 " \n" 315 "\t\t\t\t\n" 316 "\t \n" 317 "Bar: 2\n\n", 318 319 // One SP per continued line = 3. 320 "HTTP/1.0 200 OK|" 321 "Foo: 1 |" 322 "Bar: 2||" 323 }, 324 325 // Valid line continuation (No value bytes in first line). 326 { 327 "HTTP/1.0 200 OK\n" 328 "Foo:\n" 329 " value\n" 330 "Bar: 2\n\n", 331 332 "HTTP/1.0 200 OK|" 333 "Foo: value|" 334 "Bar: 2||" 335 }, 336 337 // Not a line continuation (can't continue status line). 338 { 339 "HTTP/1.0 200 OK\n" 340 " Foo: 1\n" 341 "Bar: 2\n\n", 342 343 "HTTP/1.0 200 OK|" 344 " Foo: 1|" 345 "Bar: 2||" 346 }, 347 348 // Not a line continuation (can't continue status line). 349 { 350 "HTTP/1.0\n" 351 " 200 OK\n" 352 "Foo: 1\n" 353 "Bar: 2\n\n", 354 355 "HTTP/1.0|" 356 " 200 OK|" 357 "Foo: 1|" 358 "Bar: 2||" 359 }, 360 361 // Not a line continuation (can't continue status line). 362 { 363 "HTTP/1.0 404\n" 364 " Not Found\n" 365 "Foo: 1\n" 366 "Bar: 2\n\n", 367 368 "HTTP/1.0 404|" 369 " Not Found|" 370 "Foo: 1|" 371 "Bar: 2||" 372 }, 373 374 // Unterminated status line. 375 { 376 "HTTP/1.0 200 OK", 377 378 "HTTP/1.0 200 OK||" 379 }, 380 381 // Single terminated, with headers 382 { 383 "HTTP/1.0 200 OK\n" 384 "Foo: 1\n" 385 "Bar: 2\n", 386 387 "HTTP/1.0 200 OK|" 388 "Foo: 1|" 389 "Bar: 2||" 390 }, 391 392 // Not terminated, with headers 393 { 394 "HTTP/1.0 200 OK\n" 395 "Foo: 1\n" 396 "Bar: 2", 397 398 "HTTP/1.0 200 OK|" 399 "Foo: 1|" 400 "Bar: 2||" 401 }, 402 403 // Not a line continuation (VT) 404 { 405 "HTTP/1.0 200 OK\n" 406 "Foo: 1\n" 407 "\vInvalidContinuation\n" 408 "Bar: 2\n\n", 409 410 "HTTP/1.0 200 OK|" 411 "Foo: 1|" 412 "\vInvalidContinuation|" 413 "Bar: 2||" 414 }, 415 416 // Not a line continuation (formfeed) 417 { 418 "HTTP/1.0 200 OK\n" 419 "Foo: 1\n" 420 "\fInvalidContinuation\n" 421 "Bar: 2\n\n", 422 423 "HTTP/1.0 200 OK|" 424 "Foo: 1|" 425 "\fInvalidContinuation|" 426 "Bar: 2||" 427 }, 428 429 // Not a line continuation -- can't continue header names. 430 { 431 "HTTP/1.0 200 OK\n" 432 "Serv\n" 433 " er: Apache\n" 434 "\tInvalidContinuation\n" 435 "Bar: 2\n\n", 436 437 "HTTP/1.0 200 OK|" 438 "Serv|" 439 " er: Apache|" 440 "\tInvalidContinuation|" 441 "Bar: 2||" 442 }, 443 444 // Not a line continuation -- no value to continue. 445 { 446 "HTTP/1.0 200 OK\n" 447 "Foo: 1\n" 448 "garbage\n" 449 " not-a-continuation\n" 450 "Bar: 2\n\n", 451 452 "HTTP/1.0 200 OK|" 453 "Foo: 1|" 454 "garbage|" 455 " not-a-continuation|" 456 "Bar: 2||", 457 }, 458 459 // Not a line continuation -- no valid name. 460 { 461 "HTTP/1.0 200 OK\n" 462 ": 1\n" 463 " garbage\n" 464 "Bar: 2\n\n", 465 466 "HTTP/1.0 200 OK|" 467 ": 1|" 468 " garbage|" 469 "Bar: 2||", 470 }, 471 472 // Not a line continuation -- no valid name (whitespace) 473 { 474 "HTTP/1.0 200 OK\n" 475 " : 1\n" 476 " garbage\n" 477 "Bar: 2\n\n", 478 479 "HTTP/1.0 200 OK|" 480 " : 1|" 481 " garbage|" 482 "Bar: 2||", 483 }, 484 485 }; 486 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 487 int input_len = static_cast<int>(strlen(tests[i].input)); 488 std::string raw = HttpUtil::AssembleRawHeaders(tests[i].input, input_len); 489 std::replace(raw.begin(), raw.end(), '\0', '|'); 490 EXPECT_TRUE(raw == tests[i].expected_result); 491 } 492 } 493 494 // Test SpecForRequest() and PathForRequest(). 495 TEST(HttpUtilTest, RequestUrlSanitize) { 496 struct { 497 const char* url; 498 const char* expected_spec; 499 const char* expected_path; 500 } tests[] = { 501 { // Check that #hash is removed. 502 "http://www.google.com:78/foobar?query=1#hash", 503 "http://www.google.com:78/foobar?query=1", 504 "/foobar?query=1" 505 }, 506 { // The reference may itself contain # -- strip all of it. 507 "http://192.168.0.1?query=1#hash#10#11#13#14", 508 "http://192.168.0.1/?query=1", 509 "/?query=1" 510 }, 511 { // Strip username/password. 512 "http://user:pass@google.com", 513 "http://google.com/", 514 "/" 515 } 516 }; 517 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 518 GURL url(GURL(tests[i].url)); 519 std::string expected_spec(tests[i].expected_spec); 520 std::string expected_path(tests[i].expected_path); 521 522 EXPECT_EQ(expected_spec, HttpUtil::SpecForRequest(url)); 523 EXPECT_EQ(expected_path, HttpUtil::PathForRequest(url)); 524 } 525 } 526 527 TEST(HttpUtilTest, GenerateAcceptLanguageHeader) { 528 EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6"), 529 HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de")); 530 EXPECT_EQ(std::string("en-US,fr;q=0.8,de;q=0.6,ko;q=0.4,zh-CN;q=0.2," 531 "ja;q=0.2"), 532 HttpUtil::GenerateAcceptLanguageHeader("en-US,fr,de,ko,zh-CN,ja")); 533 } 534 535 TEST(HttpUtilTest, GenerateAcceptCharsetHeader) { 536 EXPECT_EQ(std::string("utf-8,*;q=0.5"), 537 HttpUtil::GenerateAcceptCharsetHeader("utf-8")); 538 EXPECT_EQ(std::string("EUC-JP,utf-8;q=0.7,*;q=0.3"), 539 HttpUtil::GenerateAcceptCharsetHeader("EUC-JP")); 540 } 541 542 TEST(HttpUtilTest, ParseRanges) { 543 const struct { 544 const char* headers; 545 bool expected_return_value; 546 size_t expected_ranges_size; 547 const struct { 548 int64 expected_first_byte_position; 549 int64 expected_last_byte_position; 550 int64 expected_suffix_length; 551 } expected_ranges[10]; 552 } tests[] = { 553 { "Range: bytes=0-10", 554 true, 555 1, 556 { {0, 10, -1}, } 557 }, 558 { "Range: bytes=10-0", 559 false, 560 0, 561 {} 562 }, 563 { "Range: BytES=0-10", 564 true, 565 1, 566 { {0, 10, -1}, } 567 }, 568 { "Range: megabytes=0-10", 569 false, 570 0, 571 {} 572 }, 573 { "Range: bytes0-10", 574 false, 575 0, 576 {} 577 }, 578 { "Range: bytes=0-0,0-10,10-20,100-200,100-,-200", 579 true, 580 6, 581 { {0, 0, -1}, 582 {0, 10, -1}, 583 {10, 20, -1}, 584 {100, 200, -1}, 585 {100, -1, -1}, 586 {-1, -1, 200}, 587 } 588 }, 589 { "Range: bytes=0-10\r\n" 590 "Range: bytes=0-10,10-20,100-200,100-,-200", 591 true, 592 1, 593 { {0, 10, -1} 594 } 595 }, 596 { "Range: bytes=", 597 false, 598 0, 599 {} 600 }, 601 { "Range: bytes=-", 602 false, 603 0, 604 {} 605 }, 606 { "Range: bytes=0-10-", 607 false, 608 0, 609 {} 610 }, 611 { "Range: bytes=-0-10", 612 false, 613 0, 614 {} 615 }, 616 { "Range: bytes =0-10\r\n", 617 true, 618 1, 619 { {0, 10, -1} 620 } 621 }, 622 { "Range: bytes= 0-10 \r\n", 623 true, 624 1, 625 { {0, 10, -1} 626 } 627 }, 628 { "Range: bytes = 0 - 10 \r\n", 629 true, 630 1, 631 { {0, 10, -1} 632 } 633 }, 634 { "Range: bytes= 0-1 0\r\n", 635 false, 636 0, 637 {} 638 }, 639 { "Range: bytes= 0- -10\r\n", 640 false, 641 0, 642 {} 643 }, 644 { "Range: bytes= 0 - 1 , 10 -20, 100- 200 , 100-, -200 \r\n", 645 true, 646 5, 647 { {0, 1, -1}, 648 {10, 20, -1}, 649 {100, 200, -1}, 650 {100, -1, -1}, 651 {-1, -1, 200}, 652 } 653 }, 654 }; 655 656 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 657 std::vector<net::HttpByteRange> ranges; 658 bool return_value = HttpUtil::ParseRanges(std::string(tests[i].headers), 659 &ranges); 660 EXPECT_EQ(tests[i].expected_return_value, return_value); 661 if (return_value) { 662 EXPECT_EQ(tests[i].expected_ranges_size, ranges.size()); 663 for (size_t j = 0; j < ranges.size(); ++j) { 664 EXPECT_EQ(tests[i].expected_ranges[j].expected_first_byte_position, 665 ranges[j].first_byte_position()); 666 EXPECT_EQ(tests[i].expected_ranges[j].expected_last_byte_position, 667 ranges[j].last_byte_position()); 668 EXPECT_EQ(tests[i].expected_ranges[j].expected_suffix_length, 669 ranges[j].suffix_length()); 670 } 671 } 672 } 673 } 674 675 namespace { 676 void CheckCurrentNameValuePair(HttpUtil::NameValuePairsIterator* parser, 677 bool expect_valid, 678 std::string expected_name, 679 std::string expected_value) { 680 ASSERT_EQ(expect_valid, parser->valid()); 681 if (!expect_valid) { 682 return; 683 } 684 685 // Let's make sure that these never change (i.e., when a quoted value is 686 // unquoted, it should be cached on the first calls and not regenerated 687 // later). 688 std::string::const_iterator first_value_begin = parser->value_begin(); 689 std::string::const_iterator first_value_end = parser->value_end(); 690 691 ASSERT_EQ(expected_name, std::string(parser->name_begin(), 692 parser->name_end())); 693 ASSERT_EQ(expected_name, parser->name()); 694 ASSERT_EQ(expected_value, std::string(parser->value_begin(), 695 parser->value_end())); 696 ASSERT_EQ(expected_value, parser->value()); 697 698 // Make sure they didn't/don't change. 699 ASSERT_TRUE(first_value_begin == parser->value_begin()); 700 ASSERT_TRUE(first_value_end == parser->value_end()); 701 } 702 703 void CheckNextNameValuePair(HttpUtil::NameValuePairsIterator* parser, 704 bool expect_next, 705 bool expect_valid, 706 std::string expected_name, 707 std::string expected_value) { 708 ASSERT_EQ(expect_next, parser->GetNext()); 709 ASSERT_EQ(expect_valid, parser->valid()); 710 if (!expect_next || !expect_valid) { 711 return; 712 } 713 714 CheckCurrentNameValuePair(parser, 715 expect_valid, 716 expected_name, 717 expected_value); 718 } 719 720 void CheckInvalidNameValuePair(std::string valid_part, 721 std::string invalid_part) { 722 std::string whole_string = valid_part + invalid_part; 723 724 HttpUtil::NameValuePairsIterator valid_parser(valid_part.begin(), 725 valid_part.end(), 726 ';'); 727 HttpUtil::NameValuePairsIterator invalid_parser(whole_string.begin(), 728 whole_string.end(), 729 ';'); 730 731 ASSERT_TRUE(valid_parser.valid()); 732 ASSERT_TRUE(invalid_parser.valid()); 733 734 // Both parsers should return all the same values until "valid_parser" is 735 // exhausted. 736 while (valid_parser.GetNext()) { 737 ASSERT_TRUE(invalid_parser.GetNext()); 738 ASSERT_TRUE(valid_parser.valid()); 739 ASSERT_TRUE(invalid_parser.valid()); 740 ASSERT_EQ(valid_parser.name(), invalid_parser.name()); 741 ASSERT_EQ(valid_parser.value(), invalid_parser.value()); 742 } 743 744 // valid_parser is exhausted and remains 'valid' 745 ASSERT_TRUE(valid_parser.valid()); 746 747 // invalid_parser's corresponding call to GetNext also returns false... 748 ASSERT_FALSE(invalid_parser.GetNext()); 749 // ...but the parser is in an invalid state. 750 ASSERT_FALSE(invalid_parser.valid()); 751 } 752 753 } // anonymous namespace 754 755 TEST(HttpUtilTest, NameValuePairsIteratorCopyAndAssign) { 756 std::string data = "alpha='\\'a\\''; beta=\" b \"; cappa='c;'; delta=\"d\""; 757 HttpUtil::NameValuePairsIterator parser_a(data.begin(), data.end(), ';'); 758 759 EXPECT_TRUE(parser_a.valid()); 760 ASSERT_NO_FATAL_FAILURE( 761 CheckNextNameValuePair(&parser_a, true, true, "alpha", "'a'")); 762 763 HttpUtil::NameValuePairsIterator parser_b(parser_a); 764 // a and b now point to same location 765 ASSERT_NO_FATAL_FAILURE( 766 CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'")); 767 ASSERT_NO_FATAL_FAILURE( 768 CheckCurrentNameValuePair(&parser_a, true, "alpha", "'a'")); 769 770 // advance a, no effect on b 771 ASSERT_NO_FATAL_FAILURE( 772 CheckNextNameValuePair(&parser_a, true, true, "beta", " b ")); 773 ASSERT_NO_FATAL_FAILURE( 774 CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'")); 775 776 // assign b the current state of a, no effect on a 777 parser_b = parser_a; 778 ASSERT_NO_FATAL_FAILURE( 779 CheckCurrentNameValuePair(&parser_b, true, "beta", " b ")); 780 ASSERT_NO_FATAL_FAILURE( 781 CheckCurrentNameValuePair(&parser_a, true, "beta", " b ")); 782 783 // advance b, no effect on a 784 ASSERT_NO_FATAL_FAILURE( 785 CheckNextNameValuePair(&parser_b, true, true, "cappa", "c;")); 786 ASSERT_NO_FATAL_FAILURE( 787 CheckCurrentNameValuePair(&parser_a, true, "beta", " b ")); 788 } 789 790 TEST(HttpUtilTest, NameValuePairsIteratorEmptyInput) { 791 std::string data = ""; 792 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';'); 793 794 EXPECT_TRUE(parser.valid()); 795 ASSERT_NO_FATAL_FAILURE( 796 CheckNextNameValuePair(&parser, false, true, "", "")); 797 } 798 799 TEST(HttpUtilTest, NameValuePairsIterator) { 800 std::string data = "alpha=1; beta= 2 ;cappa =' 3; ';" 801 "delta= \" \\\"4\\\" \"; e= \" '5'\"; e=6;" 802 "f='\\'\\h\\e\\l\\l\\o\\ \\w\\o\\r\\l\\d\\'';" 803 "g=''; h='hello'"; 804 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';'); 805 EXPECT_TRUE(parser.valid()); 806 807 ASSERT_NO_FATAL_FAILURE( 808 CheckNextNameValuePair(&parser, true, true, "alpha", "1")); 809 ASSERT_NO_FATAL_FAILURE( 810 CheckNextNameValuePair(&parser, true, true, "beta", "2")); 811 ASSERT_NO_FATAL_FAILURE( 812 CheckNextNameValuePair(&parser, true, true, "cappa", " 3; ")); 813 ASSERT_NO_FATAL_FAILURE( 814 CheckNextNameValuePair(&parser, true, true, "delta", " \"4\" ")); 815 ASSERT_NO_FATAL_FAILURE( 816 CheckNextNameValuePair(&parser, true, true, "e", " '5'")); 817 ASSERT_NO_FATAL_FAILURE( 818 CheckNextNameValuePair(&parser, true, true, "e", "6")); 819 ASSERT_NO_FATAL_FAILURE( 820 CheckNextNameValuePair(&parser, true, true, "f", "'hello world'")); 821 ASSERT_NO_FATAL_FAILURE( 822 CheckNextNameValuePair(&parser, true, true, "g", "")); 823 ASSERT_NO_FATAL_FAILURE( 824 CheckNextNameValuePair(&parser, true, true, "h", "hello")); 825 ASSERT_NO_FATAL_FAILURE( 826 CheckNextNameValuePair(&parser, false, true, "", "")); 827 } 828 829 TEST(HttpUtilTest, NameValuePairsIteratorIllegalInputs) { 830 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; beta")); 831 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("", "beta")); 832 833 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; 'beta'=2")); 834 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("", "'beta'=2")); 835 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";beta=")); 836 ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", 837 ";beta=;cappa=2")); 838 839 // According to the spec this is an error, but it doesn't seem appropriate to 840 // change our behaviour to be less permissive at this time. 841 // See NameValuePairsIteratorExtraSeparators test 842 // ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";; beta=2")); 843 } 844 845 // If we are going to support extra separators against the spec, let's just make 846 // sure they work rationally. 847 TEST(HttpUtilTest, NameValuePairsIteratorExtraSeparators) { 848 std::string data = " ; ;;alpha=1; ;; ; beta= 2;cappa=3;;; ; "; 849 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';'); 850 EXPECT_TRUE(parser.valid()); 851 852 ASSERT_NO_FATAL_FAILURE( 853 CheckNextNameValuePair(&parser, true, true, "alpha", "1")); 854 ASSERT_NO_FATAL_FAILURE( 855 CheckNextNameValuePair(&parser, true, true, "beta", "2")); 856 ASSERT_NO_FATAL_FAILURE( 857 CheckNextNameValuePair(&parser, true, true, "cappa", "3")); 858 ASSERT_NO_FATAL_FAILURE( 859 CheckNextNameValuePair(&parser, false, true, "", "")); 860 } 861 862 // See comments on the implementation of NameValuePairsIterator::GetNext 863 // regarding this derogation from the spec. 864 TEST(HttpUtilTest, NameValuePairsIteratorMissingEndQuote) { 865 std::string data = "name='value"; 866 HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';'); 867 EXPECT_TRUE(parser.valid()); 868 869 ASSERT_NO_FATAL_FAILURE( 870 CheckNextNameValuePair(&parser, true, true, "name", "value")); 871 ASSERT_NO_FATAL_FAILURE( 872 CheckNextNameValuePair(&parser, false, true, "", "")); 873 } 874