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