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