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