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 "base/pickle.h" 9 #include "base/time.h" 10 #include "net/http/http_response_headers.h" 11 #include "testing/gtest/include/gtest/gtest.h" 12 13 using namespace std; 14 using base::Time; 15 using net::HttpResponseHeaders; 16 using net::HttpVersion; 17 18 namespace { 19 20 struct TestData { 21 const char* raw_headers; 22 const char* expected_headers; 23 int expected_response_code; 24 HttpVersion expected_parsed_version; 25 HttpVersion expected_version; 26 }; 27 28 struct ContentTypeTestData { 29 const string raw_headers; 30 const string mime_type; 31 const bool has_mimetype; 32 const string charset; 33 const bool has_charset; 34 const string all_content_type; 35 }; 36 37 class HttpResponseHeadersTest : public testing::Test { 38 }; 39 40 // Transform "normal"-looking headers (\n-separated) to the appropriate 41 // input format for ParseRawHeaders (\0-separated). 42 void HeadersToRaw(std::string* headers) { 43 replace(headers->begin(), headers->end(), '\n', '\0'); 44 if (!headers->empty()) 45 *headers += '\0'; 46 } 47 48 void TestCommon(const TestData& test) { 49 string raw_headers(test.raw_headers); 50 HeadersToRaw(&raw_headers); 51 string expected_headers(test.expected_headers); 52 53 string headers; 54 scoped_refptr<HttpResponseHeaders> parsed = 55 new HttpResponseHeaders(raw_headers); 56 parsed->GetNormalizedHeaders(&headers); 57 58 // Transform to readable output format (so it's easier to see diffs). 59 replace(headers.begin(), headers.end(), ' ', '_'); 60 replace(headers.begin(), headers.end(), '\n', '\\'); 61 replace(expected_headers.begin(), expected_headers.end(), ' ', '_'); 62 replace(expected_headers.begin(), expected_headers.end(), '\n', '\\'); 63 64 EXPECT_EQ(expected_headers, headers); 65 66 EXPECT_EQ(test.expected_response_code, parsed->response_code()); 67 68 EXPECT_TRUE(test.expected_parsed_version == parsed->GetParsedHttpVersion()); 69 EXPECT_TRUE(test.expected_version == parsed->GetHttpVersion()); 70 } 71 72 } // end namespace 73 74 // Check that we normalize headers properly. 75 TEST(HttpResponseHeadersTest, NormalizeHeadersWhitespace) { 76 TestData test = { 77 "HTTP/1.1 202 Accepted \n" 78 "Content-TYPE : text/html; charset=utf-8 \n" 79 "Set-Cookie: a \n" 80 "Set-Cookie: b \n", 81 82 "HTTP/1.1 202 Accepted\n" 83 "Content-TYPE: text/html; charset=utf-8\n" 84 "Set-Cookie: a, b\n", 85 86 202, 87 HttpVersion(1,1), 88 HttpVersion(1,1) 89 }; 90 TestCommon(test); 91 } 92 93 // Check that we normalize headers properly (header name is invalid if starts 94 // with LWS). 95 TEST(HttpResponseHeadersTest, NormalizeHeadersLeadingWhitespace) { 96 TestData test = { 97 "HTTP/1.1 202 Accepted \n" 98 // Starts with space -- will be skipped as invalid. 99 " Content-TYPE : text/html; charset=utf-8 \n" 100 "Set-Cookie: a \n" 101 "Set-Cookie: b \n", 102 103 "HTTP/1.1 202 Accepted\n" 104 "Set-Cookie: a, b\n", 105 106 202, 107 HttpVersion(1,1), 108 HttpVersion(1,1) 109 }; 110 TestCommon(test); 111 } 112 113 TEST(HttpResponseHeadersTest, BlankHeaders) { 114 TestData test = { 115 "HTTP/1.1 200 OK\n" 116 "Header1 : \n" 117 "Header2: \n" 118 "Header3:\n" 119 "Header4\n" 120 "Header5 :\n", 121 122 "HTTP/1.1 200 OK\n" 123 "Header1: \n" 124 "Header2: \n" 125 "Header3: \n" 126 "Header5: \n", 127 128 200, 129 HttpVersion(1,1), 130 HttpVersion(1,1) 131 }; 132 TestCommon(test); 133 } 134 135 TEST(HttpResponseHeadersTest, NormalizeHeadersVersion) { 136 // Don't believe the http/0.9 version if there are headers! 137 TestData test = { 138 "hTtP/0.9 201\n" 139 "Content-TYPE: text/html; charset=utf-8\n", 140 141 "HTTP/1.0 201 OK\n" 142 "Content-TYPE: text/html; charset=utf-8\n", 143 144 201, 145 HttpVersion(0,9), 146 HttpVersion(1,0) 147 }; 148 TestCommon(test); 149 } 150 151 TEST(HttpResponseHeadersTest, PreserveHttp09) { 152 // Accept the HTTP/0.9 version number if there are no headers. 153 // This is how HTTP/0.9 responses get constructed from HttpNetworkTransaction. 154 TestData test = { 155 "hTtP/0.9 200 OK\n", 156 157 "HTTP/0.9 200 OK\n", 158 159 200, 160 HttpVersion(0,9), 161 HttpVersion(0,9) 162 }; 163 TestCommon(test); 164 } 165 166 TEST(HttpResponseHeadersTest, NormalizeHeadersMissingOK) { 167 TestData test = { 168 "HTTP/1.1 201\n" 169 "Content-TYPE: text/html; charset=utf-8\n", 170 171 "HTTP/1.1 201 OK\n" 172 "Content-TYPE: text/html; charset=utf-8\n", 173 174 201, 175 HttpVersion(1,1), 176 HttpVersion(1,1) 177 }; 178 TestCommon(test); 179 } 180 181 TEST(HttpResponseHeadersTest, NormalizeHeadersBadStatus) { 182 TestData test = { 183 "SCREWED_UP_STATUS_LINE\n" 184 "Content-TYPE: text/html; charset=utf-8\n", 185 186 "HTTP/1.0 200 OK\n" 187 "Content-TYPE: text/html; charset=utf-8\n", 188 189 200, 190 HttpVersion(0,0), // Parse error 191 HttpVersion(1,0) 192 }; 193 TestCommon(test); 194 } 195 196 TEST(HttpResponseHeadersTest, NormalizeHeadersEmpty) { 197 TestData test = { 198 "", 199 200 "HTTP/1.0 200 OK\n", 201 202 200, 203 HttpVersion(0,0), // Parse Error 204 HttpVersion(1,0) 205 }; 206 TestCommon(test); 207 } 208 209 TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColon) { 210 TestData test = { 211 "HTTP/1.1 202 Accepted \n" 212 "foo: bar\n" 213 ": a \n" 214 " : b\n" 215 "baz: blat \n", 216 217 "HTTP/1.1 202 Accepted\n" 218 "foo: bar\n" 219 "baz: blat\n", 220 221 202, 222 HttpVersion(1,1), 223 HttpVersion(1,1) 224 }; 225 TestCommon(test); 226 } 227 228 TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColonAtEOL) { 229 TestData test = { 230 "HTTP/1.1 202 Accepted \n" 231 "foo: \n" 232 "bar:\n" 233 "baz: blat \n" 234 "zip:\n", 235 236 "HTTP/1.1 202 Accepted\n" 237 "foo: \n" 238 "bar: \n" 239 "baz: blat\n" 240 "zip: \n", 241 242 202, 243 HttpVersion(1,1), 244 HttpVersion(1,1) 245 }; 246 TestCommon(test); 247 } 248 249 TEST(HttpResponseHeadersTest, NormalizeHeadersOfWhitepace) { 250 TestData test = { 251 "\n \n", 252 253 "HTTP/1.0 200 OK\n", 254 255 200, 256 HttpVersion(0,0), // Parse error 257 HttpVersion(1,0) 258 }; 259 TestCommon(test); 260 } 261 262 TEST(HttpResponseHeadersTest, RepeatedSetCookie) { 263 TestData test = { 264 "HTTP/1.1 200 OK\n" 265 "Set-Cookie: x=1\n" 266 "Set-Cookie: y=2\n", 267 268 "HTTP/1.1 200 OK\n" 269 "Set-Cookie: x=1, y=2\n", 270 271 200, 272 HttpVersion(1,1), 273 HttpVersion(1,1) 274 }; 275 TestCommon(test); 276 } 277 278 TEST(HttpResponseHeadersTest, GetNormalizedHeader) { 279 std::string headers = 280 "HTTP/1.1 200 OK\n" 281 "Cache-control: private\n" 282 "cache-Control: no-store\n"; 283 HeadersToRaw(&headers); 284 scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); 285 286 std::string value; 287 EXPECT_TRUE(parsed->GetNormalizedHeader("cache-control", &value)); 288 EXPECT_EQ("private, no-store", value); 289 } 290 291 TEST(HttpResponseHeadersTest, Persist) { 292 const struct { 293 net::HttpResponseHeaders::PersistOptions options; 294 const char* raw_headers; 295 const char* expected_headers; 296 } tests[] = { 297 { net::HttpResponseHeaders::PERSIST_ALL, 298 "HTTP/1.1 200 OK\n" 299 "Cache-control:private\n" 300 "cache-Control:no-store\n", 301 302 "HTTP/1.1 200 OK\n" 303 "Cache-control: private, no-store\n" 304 }, 305 { net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP, 306 "HTTP/1.1 200 OK\n" 307 "connection: keep-alive\n" 308 "server: blah\n", 309 310 "HTTP/1.1 200 OK\n" 311 "server: blah\n" 312 }, 313 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE | 314 net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP, 315 "HTTP/1.1 200 OK\n" 316 "fOo: 1\n" 317 "Foo: 2\n" 318 "Transfer-Encoding: chunked\n" 319 "CoNnection: keep-alive\n" 320 "cache-control: private, no-cache=\"foo\"\n", 321 322 "HTTP/1.1 200 OK\n" 323 "cache-control: private, no-cache=\"foo\"\n" 324 }, 325 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, 326 "HTTP/1.1 200 OK\n" 327 "Foo: 2\n" 328 "Cache-Control: private,no-cache=\"foo, bar\"\n" 329 "bar", 330 331 "HTTP/1.1 200 OK\n" 332 "Cache-Control: private,no-cache=\"foo, bar\"\n" 333 }, 334 // ignore bogus no-cache value 335 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, 336 "HTTP/1.1 200 OK\n" 337 "Foo: 2\n" 338 "Cache-Control: private,no-cache=foo\n", 339 340 "HTTP/1.1 200 OK\n" 341 "Foo: 2\n" 342 "Cache-Control: private,no-cache=foo\n" 343 }, 344 // ignore bogus no-cache value 345 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, 346 "HTTP/1.1 200 OK\n" 347 "Foo: 2\n" 348 "Cache-Control: private, no-cache=\n", 349 350 "HTTP/1.1 200 OK\n" 351 "Foo: 2\n" 352 "Cache-Control: private, no-cache=\n" 353 }, 354 // ignore empty no-cache value 355 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, 356 "HTTP/1.1 200 OK\n" 357 "Foo: 2\n" 358 "Cache-Control: private, no-cache=\"\"\n", 359 360 "HTTP/1.1 200 OK\n" 361 "Foo: 2\n" 362 "Cache-Control: private, no-cache=\"\"\n" 363 }, 364 // ignore wrong quotes no-cache value 365 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, 366 "HTTP/1.1 200 OK\n" 367 "Foo: 2\n" 368 "Cache-Control: private, no-cache=\'foo\'\n", 369 370 "HTTP/1.1 200 OK\n" 371 "Foo: 2\n" 372 "Cache-Control: private, no-cache=\'foo\'\n" 373 }, 374 // ignore unterminated quotes no-cache value 375 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, 376 "HTTP/1.1 200 OK\n" 377 "Foo: 2\n" 378 "Cache-Control: private, no-cache=\"foo\n", 379 380 "HTTP/1.1 200 OK\n" 381 "Foo: 2\n" 382 "Cache-Control: private, no-cache=\"foo\n" 383 }, 384 // accept sloppy LWS 385 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, 386 "HTTP/1.1 200 OK\n" 387 "Foo: 2\n" 388 "Cache-Control: private, no-cache=\" foo\t, bar\"\n", 389 390 "HTTP/1.1 200 OK\n" 391 "Cache-Control: private, no-cache=\" foo\t, bar\"\n" 392 }, 393 // header name appears twice, separated by another header 394 { net::HttpResponseHeaders::PERSIST_ALL, 395 "HTTP/1.1 200 OK\n" 396 "Foo: 1\n" 397 "Bar: 2\n" 398 "Foo: 3\n", 399 400 "HTTP/1.1 200 OK\n" 401 "Foo: 1, 3\n" 402 "Bar: 2\n" 403 }, 404 // header name appears twice, separated by another header (type 2) 405 { net::HttpResponseHeaders::PERSIST_ALL, 406 "HTTP/1.1 200 OK\n" 407 "Foo: 1, 3\n" 408 "Bar: 2\n" 409 "Foo: 4\n", 410 411 "HTTP/1.1 200 OK\n" 412 "Foo: 1, 3, 4\n" 413 "Bar: 2\n" 414 }, 415 // Test filtering of cookie headers. 416 { net::HttpResponseHeaders::PERSIST_SANS_COOKIES, 417 "HTTP/1.1 200 OK\n" 418 "Set-Cookie: foo=bar; httponly\n" 419 "Set-Cookie: bar=foo\n" 420 "Bar: 1\n" 421 "Set-Cookie2: bar2=foo2\n", 422 423 "HTTP/1.1 200 OK\n" 424 "Bar: 1\n" 425 }, 426 // Test LWS at the end of a header. 427 { net::HttpResponseHeaders::PERSIST_ALL, 428 "HTTP/1.1 200 OK\n" 429 "Content-Length: 450 \n" 430 "Content-Encoding: gzip\n", 431 432 "HTTP/1.1 200 OK\n" 433 "Content-Length: 450\n" 434 "Content-Encoding: gzip\n" 435 }, 436 // Test LWS at the end of a header. 437 { net::HttpResponseHeaders::PERSIST_RAW, 438 "HTTP/1.1 200 OK\n" 439 "Content-Length: 450 \n" 440 "Content-Encoding: gzip\n", 441 442 "HTTP/1.1 200 OK\n" 443 "Content-Length: 450\n" 444 "Content-Encoding: gzip\n" 445 }, 446 }; 447 448 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 449 std::string headers = tests[i].raw_headers; 450 HeadersToRaw(&headers); 451 scoped_refptr<HttpResponseHeaders> parsed1 = 452 new HttpResponseHeaders(headers); 453 454 Pickle pickle; 455 parsed1->Persist(&pickle, tests[i].options); 456 457 void* iter = NULL; 458 scoped_refptr<HttpResponseHeaders> parsed2 = 459 new HttpResponseHeaders(pickle, &iter); 460 461 std::string h2; 462 parsed2->GetNormalizedHeaders(&h2); 463 EXPECT_EQ(string(tests[i].expected_headers), h2); 464 } 465 } 466 467 TEST(HttpResponseHeadersTest, EnumerateHeader_Coalesced) { 468 // Ensure that commas in quoted strings are not regarded as value separators. 469 // Ensure that whitespace following a value is trimmed properly 470 std::string headers = 471 "HTTP/1.1 200 OK\n" 472 "Cache-control:private , no-cache=\"set-cookie,server\" \n" 473 "cache-Control: no-store\n"; 474 HeadersToRaw(&headers); 475 scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); 476 477 void* iter = NULL; 478 std::string value; 479 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value)); 480 EXPECT_EQ("private", value); 481 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value)); 482 EXPECT_EQ("no-cache=\"set-cookie,server\"", value); 483 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value)); 484 EXPECT_EQ("no-store", value); 485 EXPECT_FALSE(parsed->EnumerateHeader(&iter, "cache-control", &value)); 486 } 487 488 TEST(HttpResponseHeadersTest, EnumerateHeader_Challenge) { 489 // Even though WWW-Authenticate has commas, it should not be treated as 490 // coalesced values. 491 std::string headers = 492 "HTTP/1.1 401 OK\n" 493 "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n" 494 "WWW-Authenticate:Basic realm=quatar\n"; 495 HeadersToRaw(&headers); 496 scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); 497 498 void* iter = NULL; 499 std::string value; 500 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value)); 501 EXPECT_EQ("Digest realm=foobar, nonce=x, domain=y", value); 502 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value)); 503 EXPECT_EQ("Basic realm=quatar", value); 504 EXPECT_FALSE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value)); 505 } 506 507 TEST(HttpResponseHeadersTest, EnumerateHeader_DateValued) { 508 // The comma in a date valued header should not be treated as a 509 // field-value separator 510 std::string headers = 511 "HTTP/1.1 200 OK\n" 512 "Date: Tue, 07 Aug 2007 23:10:55 GMT\n" 513 "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n"; 514 HeadersToRaw(&headers); 515 scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); 516 517 std::string value; 518 EXPECT_TRUE(parsed->EnumerateHeader(NULL, "date", &value)); 519 EXPECT_EQ("Tue, 07 Aug 2007 23:10:55 GMT", value); 520 EXPECT_TRUE(parsed->EnumerateHeader(NULL, "last-modified", &value)); 521 EXPECT_EQ("Wed, 01 Aug 2007 23:23:45 GMT", value); 522 } 523 524 TEST(HttpResponseHeadersTest, GetMimeType) { 525 const ContentTypeTestData tests[] = { 526 { "HTTP/1.1 200 OK\n" 527 "Content-type: text/html\n", 528 "text/html", true, 529 "", false, 530 "text/html" }, 531 // Multiple content-type headers should give us the last one. 532 { "HTTP/1.1 200 OK\n" 533 "Content-type: text/html\n" 534 "Content-type: text/html\n", 535 "text/html", true, 536 "", false, 537 "text/html, text/html" }, 538 { "HTTP/1.1 200 OK\n" 539 "Content-type: text/plain\n" 540 "Content-type: text/html\n" 541 "Content-type: text/plain\n" 542 "Content-type: text/html\n", 543 "text/html", true, 544 "", false, 545 "text/plain, text/html, text/plain, text/html" }, 546 // Test charset parsing. 547 { "HTTP/1.1 200 OK\n" 548 "Content-type: text/html\n" 549 "Content-type: text/html; charset=ISO-8859-1\n", 550 "text/html", true, 551 "iso-8859-1", true, 552 "text/html, text/html; charset=ISO-8859-1" }, 553 // Test charset in double quotes. 554 { "HTTP/1.1 200 OK\n" 555 "Content-type: text/html\n" 556 "Content-type: text/html; charset=\"ISO-8859-1\"\n", 557 "text/html", true, 558 "iso-8859-1", true, 559 "text/html, text/html; charset=\"ISO-8859-1\"" }, 560 // If there are multiple matching content-type headers, we carry 561 // over the charset value. 562 { "HTTP/1.1 200 OK\n" 563 "Content-type: text/html;charset=utf-8\n" 564 "Content-type: text/html\n", 565 "text/html", true, 566 "utf-8", true, 567 "text/html;charset=utf-8, text/html" }, 568 // Test single quotes. 569 { "HTTP/1.1 200 OK\n" 570 "Content-type: text/html;charset='utf-8'\n" 571 "Content-type: text/html\n", 572 "text/html", true, 573 "utf-8", true, 574 "text/html;charset='utf-8', text/html" }, 575 // Last charset wins if matching content-type. 576 { "HTTP/1.1 200 OK\n" 577 "Content-type: text/html;charset=utf-8\n" 578 "Content-type: text/html;charset=iso-8859-1\n", 579 "text/html", true, 580 "iso-8859-1", true, 581 "text/html;charset=utf-8, text/html;charset=iso-8859-1" }, 582 // Charset is ignored if the content types change. 583 { "HTTP/1.1 200 OK\n" 584 "Content-type: text/plain;charset=utf-8\n" 585 "Content-type: text/html\n", 586 "text/html", true, 587 "", false, 588 "text/plain;charset=utf-8, text/html" }, 589 // Empty content-type 590 { "HTTP/1.1 200 OK\n" 591 "Content-type: \n", 592 "", false, 593 "", false, 594 "" }, 595 // Emtpy charset 596 { "HTTP/1.1 200 OK\n" 597 "Content-type: text/html;charset=\n", 598 "text/html", true, 599 "", false, 600 "text/html;charset=" }, 601 // Multiple charsets, last one wins. 602 { "HTTP/1.1 200 OK\n" 603 "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n", 604 "text/html", true, 605 "iso-8859-1", true, 606 "text/html;charset=utf-8; charset=iso-8859-1" }, 607 // Multiple params. 608 { "HTTP/1.1 200 OK\n" 609 "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n", 610 "text/html", true, 611 "iso-8859-1", true, 612 "text/html; foo=utf-8; charset=iso-8859-1" }, 613 { "HTTP/1.1 200 OK\n" 614 "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n", 615 "text/html", true, 616 "utf-8", true, 617 "text/html ; charset=utf-8 ; bar=iso-8859-1" }, 618 // Comma embeded in quotes. 619 { "HTTP/1.1 200 OK\n" 620 "Content-type: text/html ; charset='utf-8,text/plain' ;\n", 621 "text/html", true, 622 "utf-8,text/plain", true, 623 "text/html ; charset='utf-8,text/plain' ;" }, 624 // Charset with leading spaces. 625 { "HTTP/1.1 200 OK\n" 626 "Content-type: text/html ; charset= 'utf-8' ;\n", 627 "text/html", true, 628 "utf-8", true, 629 "text/html ; charset= 'utf-8' ;" }, 630 // Media type comments in mime-type. 631 { "HTTP/1.1 200 OK\n" 632 "Content-type: text/html (html)\n", 633 "text/html", true, 634 "", false, 635 "text/html (html)" }, 636 // Incomplete charset= param 637 { "HTTP/1.1 200 OK\n" 638 "Content-type: text/html; char=\n", 639 "text/html", true, 640 "", false, 641 "text/html; char=" }, 642 // Invalid media type: no slash 643 { "HTTP/1.1 200 OK\n" 644 "Content-type: texthtml\n", 645 "", false, 646 "", false, 647 "texthtml" }, 648 // Invalid media type: */* 649 { "HTTP/1.1 200 OK\n" 650 "Content-type: */*\n", 651 "", false, 652 "", false, 653 "*/*" }, 654 }; 655 656 for (size_t i = 0; i < arraysize(tests); ++i) { 657 string headers(tests[i].raw_headers); 658 HeadersToRaw(&headers); 659 scoped_refptr<HttpResponseHeaders> parsed = 660 new HttpResponseHeaders(headers); 661 662 std::string value; 663 EXPECT_EQ(tests[i].has_mimetype, parsed->GetMimeType(&value)); 664 EXPECT_EQ(tests[i].mime_type, value); 665 value.clear(); 666 EXPECT_EQ(tests[i].has_charset, parsed->GetCharset(&value)); 667 EXPECT_EQ(tests[i].charset, value); 668 EXPECT_TRUE(parsed->GetNormalizedHeader("content-type", &value)); 669 EXPECT_EQ(tests[i].all_content_type, value); 670 } 671 } 672 673 TEST(HttpResponseHeadersTest, RequiresValidation) { 674 const struct { 675 const char* headers; 676 bool requires_validation; 677 } tests[] = { 678 // no expiry info: expires immediately 679 { "HTTP/1.1 200 OK\n" 680 "\n", 681 true 682 }, 683 // valid for a little while 684 { "HTTP/1.1 200 OK\n" 685 "cache-control: max-age=10000\n" 686 "\n", 687 false 688 }, 689 // expires in the future 690 { "HTTP/1.1 200 OK\n" 691 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 692 "expires: Wed, 28 Nov 2007 01:00:00 GMT\n" 693 "\n", 694 false 695 }, 696 // expired already 697 { "HTTP/1.1 200 OK\n" 698 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 699 "expires: Wed, 28 Nov 2007 00:00:00 GMT\n" 700 "\n", 701 true 702 }, 703 // max-age trumps expires 704 { "HTTP/1.1 200 OK\n" 705 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 706 "expires: Wed, 28 Nov 2007 00:00:00 GMT\n" 707 "cache-control: max-age=10000\n" 708 "\n", 709 false 710 }, 711 // last-modified heuristic: modified a while ago 712 { "HTTP/1.1 200 OK\n" 713 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 714 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" 715 "\n", 716 false 717 }, 718 { "HTTP/1.1 203 Non-Authoritative Information\n" 719 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 720 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" 721 "\n", 722 false 723 }, 724 { "HTTP/1.1 206 Partial Content\n" 725 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 726 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" 727 "\n", 728 false 729 }, 730 // last-modified heuristic: modified recently 731 { "HTTP/1.1 200 OK\n" 732 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 733 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n" 734 "\n", 735 true 736 }, 737 { "HTTP/1.1 203 Non-Authoritative Information\n" 738 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 739 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n" 740 "\n", 741 true 742 }, 743 { "HTTP/1.1 206 Partial Content\n" 744 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 745 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n" 746 "\n", 747 true 748 }, 749 // cached permanent redirect 750 { "HTTP/1.1 301 Moved Permanently\n" 751 "\n", 752 false 753 }, 754 // cached redirect: not reusable even though by default it would be 755 { "HTTP/1.1 300 Multiple Choices\n" 756 "Cache-Control: no-cache\n" 757 "\n", 758 true 759 }, 760 // cached forever by default 761 { "HTTP/1.1 410 Gone\n" 762 "\n", 763 false 764 }, 765 // cached temporary redirect: not reusable 766 { "HTTP/1.1 302 Found\n" 767 "\n", 768 true 769 }, 770 // cached temporary redirect: reusable 771 { "HTTP/1.1 302 Found\n" 772 "cache-control: max-age=10000\n" 773 "\n", 774 false 775 }, 776 // cache-control: max-age=N overrides expires: date in the past 777 { "HTTP/1.1 200 OK\n" 778 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 779 "expires: Wed, 28 Nov 2007 00:20:11 GMT\n" 780 "cache-control: max-age=10000\n" 781 "\n", 782 false 783 }, 784 // cache-control: no-store overrides expires: in the future 785 { "HTTP/1.1 200 OK\n" 786 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 787 "expires: Wed, 29 Nov 2007 00:40:11 GMT\n" 788 "cache-control: no-store,private,no-cache=\"foo\"\n" 789 "\n", 790 true 791 }, 792 // pragma: no-cache overrides last-modified heuristic 793 { "HTTP/1.1 200 OK\n" 794 "date: Wed, 28 Nov 2007 00:40:11 GMT\n" 795 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" 796 "pragma: no-cache\n" 797 "\n", 798 true 799 }, 800 // TODO(darin): add many many more tests here 801 }; 802 Time request_time, response_time, current_time; 803 Time::FromString(L"Wed, 28 Nov 2007 00:40:09 GMT", &request_time); 804 Time::FromString(L"Wed, 28 Nov 2007 00:40:12 GMT", &response_time); 805 Time::FromString(L"Wed, 28 Nov 2007 00:45:20 GMT", ¤t_time); 806 807 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 808 string headers(tests[i].headers); 809 HeadersToRaw(&headers); 810 scoped_refptr<HttpResponseHeaders> parsed = 811 new HttpResponseHeaders(headers); 812 813 bool requires_validation = 814 parsed->RequiresValidation(request_time, response_time, current_time); 815 EXPECT_EQ(tests[i].requires_validation, requires_validation); 816 } 817 } 818 819 TEST(HttpResponseHeadersTest, Update) { 820 const struct { 821 const char* orig_headers; 822 const char* new_headers; 823 const char* expected_headers; 824 } tests[] = { 825 { "HTTP/1.1 200 OK\n", 826 827 "HTTP/1/1 304 Not Modified\n" 828 "connection: keep-alive\n" 829 "Cache-control: max-age=10000\n", 830 831 "HTTP/1.1 200 OK\n" 832 "Cache-control: max-age=10000\n" 833 }, 834 { "HTTP/1.1 200 OK\n" 835 "Foo: 1\n" 836 "Cache-control: private\n", 837 838 "HTTP/1/1 304 Not Modified\n" 839 "connection: keep-alive\n" 840 "Cache-control: max-age=10000\n", 841 842 "HTTP/1.1 200 OK\n" 843 "Cache-control: max-age=10000\n" 844 "Foo: 1\n" 845 }, 846 { "HTTP/1.1 200 OK\n" 847 "Foo: 1\n" 848 "Cache-control: private\n", 849 850 "HTTP/1/1 304 Not Modified\n" 851 "connection: keep-alive\n" 852 "Cache-CONTROL: max-age=10000\n", 853 854 "HTTP/1.1 200 OK\n" 855 "Cache-CONTROL: max-age=10000\n" 856 "Foo: 1\n" 857 }, 858 { "HTTP/1.1 200 OK\n" 859 "Content-Length: 450\n", 860 861 "HTTP/1/1 304 Not Modified\n" 862 "connection: keep-alive\n" 863 "Cache-control: max-age=10001 \n", 864 865 "HTTP/1.1 200 OK\n" 866 "Cache-control: max-age=10001\n" 867 "Content-Length: 450\n" 868 }, 869 }; 870 871 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 872 string orig_headers(tests[i].orig_headers); 873 HeadersToRaw(&orig_headers); 874 scoped_refptr<HttpResponseHeaders> parsed = 875 new HttpResponseHeaders(orig_headers); 876 877 string new_headers(tests[i].new_headers); 878 HeadersToRaw(&new_headers); 879 scoped_refptr<HttpResponseHeaders> new_parsed = 880 new HttpResponseHeaders(new_headers); 881 882 parsed->Update(*new_parsed); 883 884 string resulting_headers; 885 parsed->GetNormalizedHeaders(&resulting_headers); 886 EXPECT_EQ(string(tests[i].expected_headers), resulting_headers); 887 } 888 } 889 890 TEST(HttpResponseHeadersTest, EnumerateHeaderLines) { 891 const struct { 892 const char* headers; 893 const char* expected_lines; 894 } tests[] = { 895 { "HTTP/1.1 200 OK\n", 896 897 "" 898 }, 899 { "HTTP/1.1 200 OK\n" 900 "Foo: 1\n", 901 902 "Foo: 1\n" 903 }, 904 { "HTTP/1.1 200 OK\n" 905 "Foo: 1\n" 906 "Bar: 2\n" 907 "Foo: 3\n", 908 909 "Foo: 1\nBar: 2\nFoo: 3\n" 910 }, 911 { "HTTP/1.1 200 OK\n" 912 "Foo: 1, 2, 3\n", 913 914 "Foo: 1, 2, 3\n" 915 }, 916 }; 917 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 918 string headers(tests[i].headers); 919 HeadersToRaw(&headers); 920 scoped_refptr<HttpResponseHeaders> parsed = 921 new HttpResponseHeaders(headers); 922 923 string name, value, lines; 924 925 void* iter = NULL; 926 while (parsed->EnumerateHeaderLines(&iter, &name, &value)) { 927 lines.append(name); 928 lines.append(": "); 929 lines.append(value); 930 lines.append("\n"); 931 } 932 933 EXPECT_EQ(string(tests[i].expected_lines), lines); 934 } 935 } 936 937 TEST(HttpResponseHeadersTest, IsRedirect) { 938 const struct { 939 const char* headers; 940 const char* location; 941 bool is_redirect; 942 } tests[] = { 943 { "HTTP/1.1 200 OK\n", 944 "", 945 false 946 }, 947 { "HTTP/1.1 301 Moved\n" 948 "Location: http://foopy/\n", 949 "http://foopy/", 950 true 951 }, 952 { "HTTP/1.1 301 Moved\n" 953 "Location: \t \n", 954 "", 955 false 956 }, 957 // we use the first location header as the target of the redirect 958 { "HTTP/1.1 301 Moved\n" 959 "Location: http://foo/\n" 960 "Location: http://bar/\n", 961 "http://foo/", 962 true 963 }, 964 // we use the first _valid_ location header as the target of the redirect 965 { "HTTP/1.1 301 Moved\n" 966 "Location: \n" 967 "Location: http://bar/\n", 968 "http://bar/", 969 true 970 }, 971 // bug 1050541 (location header w/ an unescaped comma) 972 { "HTTP/1.1 301 Moved\n" 973 "Location: http://foo/bar,baz.html\n", 974 "http://foo/bar,baz.html", 975 true 976 }, 977 // bug 1224617 (location header w/ non-ASCII bytes) 978 { "HTTP/1.1 301 Moved\n" 979 "Location: http://foo/bar?key=\xE4\xF6\xFC\n", 980 "http://foo/bar?key=%E4%F6%FC", 981 true 982 }, 983 // Shift_JIS, Big5, and GBK contain multibyte characters with the trailing 984 // byte falling in the ASCII range. 985 { "HTTP/1.1 301 Moved\n" 986 "Location: http://foo/bar?key=\x81\x5E\xD8\xBF\n", 987 "http://foo/bar?key=%81^%D8%BF", 988 true 989 }, 990 { "HTTP/1.1 301 Moved\n" 991 "Location: http://foo/bar?key=\x82\x40\xBD\xC4\n", 992 "http://foo/bar?key=%82@%BD%C4", 993 true 994 }, 995 { "HTTP/1.1 301 Moved\n" 996 "Location: http://foo/bar?key=\x83\x5C\x82\x5D\xCB\xD7\n", 997 "http://foo/bar?key=%83\\%82]%CB%D7", 998 true 999 }, 1000 }; 1001 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 1002 string headers(tests[i].headers); 1003 HeadersToRaw(&headers); 1004 scoped_refptr<HttpResponseHeaders> parsed = 1005 new HttpResponseHeaders(headers); 1006 1007 std::string location; 1008 EXPECT_EQ(parsed->IsRedirect(&location), tests[i].is_redirect); 1009 EXPECT_EQ(location, tests[i].location); 1010 } 1011 } 1012 1013 TEST(HttpResponseHeadersTest, GetContentLength) { 1014 const struct { 1015 const char* headers; 1016 int64 expected_len; 1017 } tests[] = { 1018 { "HTTP/1.1 200 OK\n", 1019 -1 1020 }, 1021 { "HTTP/1.1 200 OK\n" 1022 "Content-Length: 10\n", 1023 10 1024 }, 1025 { "HTTP/1.1 200 OK\n" 1026 "Content-Length: \n", 1027 -1 1028 }, 1029 { "HTTP/1.1 200 OK\n" 1030 "Content-Length: abc\n", 1031 -1 1032 }, 1033 { "HTTP/1.1 200 OK\n" 1034 "Content-Length: -10\n", 1035 -1 1036 }, 1037 { "HTTP/1.1 200 OK\n" 1038 "Content-Length: +10\n", 1039 -1 1040 }, 1041 { "HTTP/1.1 200 OK\n" 1042 "Content-Length: 23xb5\n", 1043 -1 1044 }, 1045 { "HTTP/1.1 200 OK\n" 1046 "Content-Length: 0xA\n", 1047 -1 1048 }, 1049 { "HTTP/1.1 200 OK\n" 1050 "Content-Length: 010\n", 1051 10 1052 }, 1053 // Content-Length too big, will overflow an int64 1054 { "HTTP/1.1 200 OK\n" 1055 "Content-Length: 40000000000000000000\n", 1056 -1 1057 }, 1058 { "HTTP/1.1 200 OK\n" 1059 "Content-Length: 10\n", 1060 10 1061 }, 1062 { "HTTP/1.1 200 OK\n" 1063 "Content-Length: 10 \n", 1064 10 1065 }, 1066 { "HTTP/1.1 200 OK\n" 1067 "Content-Length: \t10\n", 1068 10 1069 }, 1070 { "HTTP/1.1 200 OK\n" 1071 "Content-Length: \v10\n", 1072 -1 1073 }, 1074 { "HTTP/1.1 200 OK\n" 1075 "Content-Length: \f10\n", 1076 -1 1077 }, 1078 { "HTTP/1.1 200 OK\n" 1079 "cOnTeNt-LENgth: 33\n", 1080 33 1081 }, 1082 { "HTTP/1.1 200 OK\n" 1083 "Content-Length: 34\r\n", 1084 -1 1085 }, 1086 }; 1087 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 1088 string headers(tests[i].headers); 1089 HeadersToRaw(&headers); 1090 scoped_refptr<HttpResponseHeaders> parsed = 1091 new HttpResponseHeaders(headers); 1092 1093 EXPECT_EQ(tests[i].expected_len, parsed->GetContentLength()); 1094 } 1095 } 1096 1097 TEST(HttpResponseHeaders, GetContentRange) { 1098 const struct { 1099 const char* headers; 1100 bool expected_return_value; 1101 int64 expected_first_byte_position; 1102 int64 expected_last_byte_position; 1103 int64 expected_instance_size; 1104 } tests[] = { 1105 { "HTTP/1.1 206 Partial Content", 1106 false, 1107 -1, 1108 -1, 1109 -1 1110 }, 1111 { "HTTP/1.1 206 Partial Content\n" 1112 "Content-Range:", 1113 false, 1114 -1, 1115 -1, 1116 -1 1117 }, 1118 { "HTTP/1.1 206 Partial Content\n" 1119 "Content-Range: megabytes 0-10/50", 1120 false, 1121 -1, 1122 -1, 1123 -1 1124 }, 1125 { "HTTP/1.1 206 Partial Content\n" 1126 "Content-Range: 0-10/50", 1127 false, 1128 -1, 1129 -1, 1130 -1 1131 }, 1132 { "HTTP/1.1 206 Partial Content\n" 1133 "Content-Range: Bytes 0-50/51", 1134 true, 1135 0, 1136 50, 1137 51 1138 }, 1139 { "HTTP/1.1 206 Partial Content\n" 1140 "Content-Range: bytes 0-50/51", 1141 true, 1142 0, 1143 50, 1144 51 1145 }, 1146 { "HTTP/1.1 206 Partial Content\n" 1147 "Content-Range: bytes\t0-50/51", 1148 false, 1149 -1, 1150 -1, 1151 -1 1152 }, 1153 { "HTTP/1.1 206 Partial Content\n" 1154 "Content-Range: bytes 0-50/51", 1155 true, 1156 0, 1157 50, 1158 51 1159 }, 1160 { "HTTP/1.1 206 Partial Content\n" 1161 "Content-Range: bytes 0 - 50 \t / \t51", 1162 true, 1163 0, 1164 50, 1165 51 1166 }, 1167 { "HTTP/1.1 206 Partial Content\n" 1168 "Content-Range: bytes 0\t-\t50\t/\t51\t", 1169 true, 1170 0, 1171 50, 1172 51 1173 }, 1174 { "HTTP/1.1 206 Partial Content\n" 1175 "Content-Range: \tbytes\t\t\t 0\t-\t50\t/\t51\t", 1176 true, 1177 0, 1178 50, 1179 51 1180 }, 1181 { "HTTP/1.1 206 Partial Content\n" 1182 "Content-Range: \t bytes \t 0 - 50 / 5 1", 1183 false, 1184 0, 1185 50, 1186 -1 1187 }, 1188 { "HTTP/1.1 206 Partial Content\n" 1189 "Content-Range: \t bytes \t 0 - 5 0 / 51", 1190 false, 1191 -1, 1192 -1, 1193 -1 1194 }, 1195 { "HTTP/1.1 206 Partial Content\n" 1196 "Content-Range: bytes 50-0/51", 1197 false, 1198 50, 1199 0, 1200 -1 1201 }, 1202 { "HTTP/1.1 416 Requested range not satisfiable\n" 1203 "Content-Range: bytes * /*", 1204 false, 1205 -1, 1206 -1, 1207 -1 1208 }, 1209 { "HTTP/1.1 416 Requested range not satisfiable\n" 1210 "Content-Range: bytes * / * ", 1211 false, 1212 -1, 1213 -1, 1214 -1 1215 }, 1216 { "HTTP/1.1 206 Partial Content\n" 1217 "Content-Range: bytes 0-50/*", 1218 false, 1219 0, 1220 50, 1221 -1 1222 }, 1223 { "HTTP/1.1 206 Partial Content\n" 1224 "Content-Range: bytes 0-50 / * ", 1225 false, 1226 0, 1227 50, 1228 -1 1229 }, 1230 { "HTTP/1.1 206 Partial Content\n" 1231 "Content-Range: bytes 0-10000000000/10000000001", 1232 true, 1233 0, 1234 10000000000ll, 1235 10000000001ll 1236 }, 1237 { "HTTP/1.1 206 Partial Content\n" 1238 "Content-Range: bytes 0-10000000000/10000000000", 1239 false, 1240 0, 1241 10000000000ll, 1242 10000000000ll 1243 }, 1244 // 64 bits wraparound. 1245 { "HTTP/1.1 206 Partial Content\n" 1246 "Content-Range: bytes 0 - 9223372036854775807 / 100", 1247 false, 1248 0, 1249 kint64max, 1250 100 1251 }, 1252 // 64 bits wraparound. 1253 { "HTTP/1.1 206 Partial Content\n" 1254 "Content-Range: bytes 0 - 100 / -9223372036854775808", 1255 false, 1256 0, 1257 100, 1258 kint64min 1259 }, 1260 { "HTTP/1.1 206 Partial Content\n" 1261 "Content-Range: bytes */50", 1262 false, 1263 -1, 1264 -1, 1265 50 1266 }, 1267 { "HTTP/1.1 206 Partial Content\n" 1268 "Content-Range: bytes 0-50/10", 1269 false, 1270 0, 1271 50, 1272 10 1273 }, 1274 { "HTTP/1.1 206 Partial Content\n" 1275 "Content-Range: bytes 40-50/45", 1276 false, 1277 40, 1278 50, 1279 45 1280 }, 1281 { "HTTP/1.1 206 Partial Content\n" 1282 "Content-Range: bytes 0-50/-10", 1283 false, 1284 0, 1285 50, 1286 -10 1287 }, 1288 { "HTTP/1.1 206 Partial Content\n" 1289 "Content-Range: bytes 0-0/1", 1290 true, 1291 0, 1292 0, 1293 1 1294 }, 1295 { "HTTP/1.1 206 Partial Content\n" 1296 "Content-Range: bytes 0-40000000000000000000/40000000000000000001", 1297 false, 1298 -1, 1299 -1, 1300 -1 1301 }, 1302 { "HTTP/1.1 206 Partial Content\n" 1303 "Content-Range: bytes 1-/100", 1304 false, 1305 -1, 1306 -1, 1307 -1 1308 }, 1309 { "HTTP/1.1 206 Partial Content\n" 1310 "Content-Range: bytes -/100", 1311 false, 1312 -1, 1313 -1, 1314 -1 1315 }, 1316 { "HTTP/1.1 206 Partial Content\n" 1317 "Content-Range: bytes -1/100", 1318 false, 1319 -1, 1320 -1, 1321 -1 1322 }, 1323 { "HTTP/1.1 206 Partial Content\n" 1324 "Content-Range: bytes 0-1233/*", 1325 false, 1326 0, 1327 1233, 1328 -1 1329 }, 1330 { "HTTP/1.1 206 Partial Content\n" 1331 "Content-Range: bytes -123 - -1/100", 1332 false, 1333 -1, 1334 -1, 1335 -1 1336 }, 1337 }; 1338 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 1339 string headers(tests[i].headers); 1340 HeadersToRaw(&headers); 1341 scoped_refptr<HttpResponseHeaders> parsed = 1342 new HttpResponseHeaders(headers); 1343 1344 int64 first_byte_position; 1345 int64 last_byte_position; 1346 int64 instance_size; 1347 bool return_value = parsed->GetContentRange(&first_byte_position, 1348 &last_byte_position, 1349 &instance_size); 1350 EXPECT_EQ(tests[i].expected_return_value, return_value); 1351 EXPECT_EQ(tests[i].expected_first_byte_position, first_byte_position); 1352 EXPECT_EQ(tests[i].expected_last_byte_position, last_byte_position); 1353 EXPECT_EQ(tests[i].expected_instance_size, instance_size); 1354 } 1355 } 1356 1357 TEST(HttpResponseHeadersTest, IsKeepAlive) { 1358 const struct { 1359 const char* headers; 1360 bool expected_keep_alive; 1361 } tests[] = { 1362 // The status line fabricated by HttpNetworkTransaction for a 0.9 response. 1363 // Treated as 0.9. 1364 { "HTTP/0.9 200 OK", 1365 false 1366 }, 1367 // This could come from a broken server. Treated as 1.0 because it has a 1368 // header. 1369 { "HTTP/0.9 200 OK\n" 1370 "connection: keep-alive\n", 1371 true 1372 }, 1373 { "HTTP/1.1 200 OK\n", 1374 true 1375 }, 1376 { "HTTP/1.0 200 OK\n", 1377 false 1378 }, 1379 { "HTTP/1.0 200 OK\n" 1380 "connection: close\n", 1381 false 1382 }, 1383 { "HTTP/1.0 200 OK\n" 1384 "connection: keep-alive\n", 1385 true 1386 }, 1387 { "HTTP/1.0 200 OK\n" 1388 "connection: kEeP-AliVe\n", 1389 true 1390 }, 1391 { "HTTP/1.0 200 OK\n" 1392 "connection: keep-aliveX\n", 1393 false 1394 }, 1395 { "HTTP/1.1 200 OK\n" 1396 "connection: close\n", 1397 false 1398 }, 1399 { "HTTP/1.1 200 OK\n" 1400 "connection: keep-alive\n", 1401 true 1402 }, 1403 { "HTTP/1.0 200 OK\n" 1404 "proxy-connection: close\n", 1405 false 1406 }, 1407 { "HTTP/1.0 200 OK\n" 1408 "proxy-connection: keep-alive\n", 1409 true 1410 }, 1411 { "HTTP/1.1 200 OK\n" 1412 "proxy-connection: close\n", 1413 false 1414 }, 1415 { "HTTP/1.1 200 OK\n" 1416 "proxy-connection: keep-alive\n", 1417 true 1418 }, 1419 }; 1420 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 1421 string headers(tests[i].headers); 1422 HeadersToRaw(&headers); 1423 scoped_refptr<HttpResponseHeaders> parsed = 1424 new HttpResponseHeaders(headers); 1425 1426 EXPECT_EQ(tests[i].expected_keep_alive, parsed->IsKeepAlive()); 1427 } 1428 } 1429 1430 TEST(HttpResponseHeadersTest, HasStrongValidators) { 1431 const struct { 1432 const char* headers; 1433 bool expected_result; 1434 } tests[] = { 1435 { "HTTP/0.9 200 OK", 1436 false 1437 }, 1438 { "HTTP/0.9 200 OK\n" 1439 "Date: Wed, 28 Nov 2007 01:40:10 GMT\n" 1440 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n" 1441 "ETag: \"foo\"\n", 1442 true 1443 }, 1444 { "HTTP/1.1 200 OK\n" 1445 "Date: Wed, 28 Nov 2007 00:41:10 GMT\n" 1446 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n", 1447 true 1448 }, 1449 { "HTTP/1.1 200 OK\n" 1450 "Date: Wed, 28 Nov 2007 00:41:09 GMT\n" 1451 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n", 1452 false 1453 }, 1454 { "HTTP/1.1 200 OK\n" 1455 "ETag: \"foo\"\n", 1456 true 1457 }, 1458 // This is not really a weak etag: 1459 { "HTTP/1.1 200 OK\n" 1460 "etag: \"w/foo\"\n", 1461 true 1462 }, 1463 // This is a weak etag: 1464 { "HTTP/1.1 200 OK\n" 1465 "etag: w/\"foo\"\n", 1466 false 1467 }, 1468 { "HTTP/1.1 200 OK\n" 1469 "etag: W / \"foo\"\n", 1470 false 1471 } 1472 }; 1473 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 1474 string headers(tests[i].headers); 1475 HeadersToRaw(&headers); 1476 scoped_refptr<HttpResponseHeaders> parsed = 1477 new HttpResponseHeaders(headers); 1478 1479 EXPECT_EQ(tests[i].expected_result, parsed->HasStrongValidators()) << 1480 "Failed test case " << i; 1481 } 1482 } 1483 1484 TEST(HttpResponseHeadersTest, GetStatusText) { 1485 std::string headers("HTTP/1.1 404 Not Found"); 1486 HeadersToRaw(&headers); 1487 scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); 1488 EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText()); 1489 } 1490 1491 TEST(HttpResponseHeadersTest, GetStatusTextMissing) { 1492 std::string headers("HTTP/1.1 404"); 1493 HeadersToRaw(&headers); 1494 scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); 1495 // Since the status line gets normalized, we have OK 1496 EXPECT_EQ(std::string("OK"), parsed->GetStatusText()); 1497 } 1498 1499 TEST(HttpResponseHeadersTest, GetStatusTextMultiSpace) { 1500 std::string headers("HTTP/1.0 404 Not Found"); 1501 HeadersToRaw(&headers); 1502 scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); 1503 EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText()); 1504 } 1505 1506 TEST(HttpResponseHeadersTest, GetStatusBadStatusLine) { 1507 std::string headers("Foo bar."); 1508 HeadersToRaw(&headers); 1509 scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); 1510 // The bad status line would have gotten rewritten as 1511 // HTTP/1.0 200 OK. 1512 EXPECT_EQ(std::string("OK"), parsed->GetStatusText()); 1513 } 1514 1515 TEST(HttpResponseHeadersTest, AddHeader) { 1516 const struct { 1517 const char* orig_headers; 1518 const char* new_header; 1519 const char* expected_headers; 1520 } tests[] = { 1521 { "HTTP/1.1 200 OK\n" 1522 "connection: keep-alive\n" 1523 "Cache-control: max-age=10000\n", 1524 1525 "Content-Length: 450", 1526 1527 "HTTP/1.1 200 OK\n" 1528 "connection: keep-alive\n" 1529 "Cache-control: max-age=10000\n" 1530 "Content-Length: 450\n" 1531 }, 1532 { "HTTP/1.1 200 OK\n" 1533 "connection: keep-alive\n" 1534 "Cache-control: max-age=10000 \n", 1535 1536 "Content-Length: 450 ", 1537 1538 "HTTP/1.1 200 OK\n" 1539 "connection: keep-alive\n" 1540 "Cache-control: max-age=10000\n" 1541 "Content-Length: 450\n" 1542 }, 1543 }; 1544 1545 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 1546 string orig_headers(tests[i].orig_headers); 1547 HeadersToRaw(&orig_headers); 1548 scoped_refptr<HttpResponseHeaders> parsed = 1549 new HttpResponseHeaders(orig_headers); 1550 1551 string new_header(tests[i].new_header); 1552 parsed->AddHeader(new_header); 1553 1554 string resulting_headers; 1555 parsed->GetNormalizedHeaders(&resulting_headers); 1556 EXPECT_EQ(string(tests[i].expected_headers), resulting_headers); 1557 } 1558 } 1559 1560 TEST(HttpResponseHeadersTest, RemoveHeader) { 1561 const struct { 1562 const char* orig_headers; 1563 const char* to_remove; 1564 const char* expected_headers; 1565 } tests[] = { 1566 { "HTTP/1.1 200 OK\n" 1567 "connection: keep-alive\n" 1568 "Cache-control: max-age=10000\n" 1569 "Content-Length: 450\n", 1570 1571 "Content-Length", 1572 1573 "HTTP/1.1 200 OK\n" 1574 "connection: keep-alive\n" 1575 "Cache-control: max-age=10000\n" 1576 }, 1577 { "HTTP/1.1 200 OK\n" 1578 "connection: keep-alive \n" 1579 "Content-Length : 450 \n" 1580 "Cache-control: max-age=10000\n", 1581 1582 "Content-Length", 1583 1584 "HTTP/1.1 200 OK\n" 1585 "connection: keep-alive\n" 1586 "Cache-control: max-age=10000\n" 1587 }, 1588 }; 1589 1590 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 1591 string orig_headers(tests[i].orig_headers); 1592 HeadersToRaw(&orig_headers); 1593 scoped_refptr<HttpResponseHeaders> parsed = 1594 new HttpResponseHeaders(orig_headers); 1595 1596 string name(tests[i].to_remove); 1597 parsed->RemoveHeader(name); 1598 1599 string resulting_headers; 1600 parsed->GetNormalizedHeaders(&resulting_headers); 1601 EXPECT_EQ(string(tests[i].expected_headers), resulting_headers); 1602 } 1603 } 1604 1605 TEST(HttpResponseHeadersTest, ReplaceStatus) { 1606 const struct { 1607 const char* orig_headers; 1608 const char* new_status; 1609 const char* expected_headers; 1610 } tests[] = { 1611 { "HTTP/1.1 206 Partial Content\n" 1612 "connection: keep-alive\n" 1613 "Cache-control: max-age=10000\n" 1614 "Content-Length: 450\n", 1615 1616 "HTTP/1.1 200 OK", 1617 1618 "HTTP/1.1 200 OK\n" 1619 "connection: keep-alive\n" 1620 "Cache-control: max-age=10000\n" 1621 "Content-Length: 450\n" 1622 }, 1623 { "HTTP/1.1 200 OK\n" 1624 "connection: keep-alive\n", 1625 1626 "HTTP/1.1 304 Not Modified", 1627 1628 "HTTP/1.1 304 Not Modified\n" 1629 "connection: keep-alive\n" 1630 }, 1631 { "HTTP/1.1 200 OK\n" 1632 "connection: keep-alive \n" 1633 "Content-Length : 450 \n" 1634 "Cache-control: max-age=10000\n", 1635 1636 "HTTP/1//1 304 Not Modified", 1637 1638 "HTTP/1.0 304 Not Modified\n" 1639 "connection: keep-alive\n" 1640 "Content-Length: 450\n" 1641 "Cache-control: max-age=10000\n" 1642 }, 1643 }; 1644 1645 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 1646 string orig_headers(tests[i].orig_headers); 1647 HeadersToRaw(&orig_headers); 1648 scoped_refptr<HttpResponseHeaders> parsed = 1649 new HttpResponseHeaders(orig_headers); 1650 1651 string name(tests[i].new_status); 1652 parsed->ReplaceStatusLine(name); 1653 1654 string resulting_headers; 1655 parsed->GetNormalizedHeaders(&resulting_headers); 1656 EXPECT_EQ(string(tests[i].expected_headers), resulting_headers); 1657 } 1658 } 1659