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