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.
8 #include "base/basictypes.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/pickle.h"
11 #include "base/time/time.h"
12 #include "base/values.h"
13 #include "net/http/http_byte_range.h"
14 #include "net/http/http_response_headers.h"
15 #include "testing/gtest/include/gtest/gtest.h"
20 const char* raw_headers;
21 const char* expected_headers;
22 int expected_response_code;
23 net::HttpVersion expected_parsed_version;
24 net::HttpVersion expected_version;
27 struct ContentTypeTestData {
28 const std::string raw_headers;
29 const std::string mime_type;
30 const bool has_mimetype;
31 const std::string charset;
32 const bool has_charset;
33 const std::string all_content_type;
36 class HttpResponseHeadersTest : public testing::Test {
39 // Transform "normal"-looking headers (\n-separated) to the appropriate
40 // input format for ParseRawHeaders (\0-separated).
41 void HeadersToRaw(std::string* headers) {
42 std::replace(headers->begin(), headers->end(), '\n', '\0');
43 if (!headers->empty())
47 class HttpResponseHeadersCacheControlTest : public HttpResponseHeadersTest {
49 // Make tests less verbose.
50 typedef base::TimeDelta TimeDelta;
52 // Initilise the headers() value with a Cache-Control header set to
53 // |cache_control|. |cache_control| is copied and so can safely be a
55 void InitializeHeadersWithCacheControl(const char* cache_control) {
56 std::string raw_headers("HTTP/1.1 200 OK\n");
57 raw_headers += "Cache-Control: ";
58 raw_headers += cache_control;
60 HeadersToRaw(&raw_headers);
61 headers_ = new net::HttpResponseHeaders(raw_headers);
64 const scoped_refptr<net::HttpResponseHeaders>& headers() { return headers_; }
66 // Return a pointer to a TimeDelta object. For use when the value doesn't
68 TimeDelta* TimeDeltaPointer() { return &delta_; }
70 // Get the max-age value. This should only be used in tests where a valid
71 // max-age parameter is expected to be present.
72 TimeDelta GetMaxAgeValue() {
73 DCHECK(headers_) << "Call InitializeHeadersWithCacheControl() first";
74 TimeDelta max_age_value;
75 EXPECT_TRUE(headers()->GetMaxAgeValue(&max_age_value));
79 // Get the stale-while-revalidate value. This should only be used in tests
80 // where a valid max-age parameter is expected to be present.
81 TimeDelta GetStaleWhileRevalidateValue() {
82 DCHECK(headers_) << "Call InitializeHeadersWithCacheControl() first";
83 TimeDelta stale_while_revalidate_value;
85 headers()->GetStaleWhileRevalidateValue(&stale_while_revalidate_value));
86 return stale_while_revalidate_value;
90 scoped_refptr<net::HttpResponseHeaders> headers_;
94 void TestCommon(const TestData& test) {
95 std::string raw_headers(test.raw_headers);
96 HeadersToRaw(&raw_headers);
97 std::string expected_headers(test.expected_headers);
100 scoped_refptr<net::HttpResponseHeaders> parsed(
101 new net::HttpResponseHeaders(raw_headers));
102 parsed->GetNormalizedHeaders(&headers);
104 // Transform to readable output format (so it's easier to see diffs).
105 std::replace(headers.begin(), headers.end(), ' ', '_');
106 std::replace(headers.begin(), headers.end(), '\n', '\\');
107 std::replace(expected_headers.begin(), expected_headers.end(), ' ', '_');
108 std::replace(expected_headers.begin(), expected_headers.end(), '\n', '\\');
110 EXPECT_EQ(expected_headers, headers);
112 EXPECT_EQ(test.expected_response_code, parsed->response_code());
114 EXPECT_TRUE(test.expected_parsed_version == parsed->GetParsedHttpVersion());
115 EXPECT_TRUE(test.expected_version == parsed->GetHttpVersion());
120 // Check that we normalize headers properly.
121 TEST(HttpResponseHeadersTest, NormalizeHeadersWhitespace) {
123 "HTTP/1.1 202 Accepted \n"
124 "Content-TYPE : text/html; charset=utf-8 \n"
128 "HTTP/1.1 202 Accepted\n"
129 "Content-TYPE: text/html; charset=utf-8\n"
130 "Set-Cookie: a, b\n",
133 net::HttpVersion(1,1),
134 net::HttpVersion(1,1)
139 // Check that we normalize headers properly (header name is invalid if starts
141 TEST(HttpResponseHeadersTest, NormalizeHeadersLeadingWhitespace) {
143 "HTTP/1.1 202 Accepted \n"
144 // Starts with space -- will be skipped as invalid.
145 " Content-TYPE : text/html; charset=utf-8 \n"
149 "HTTP/1.1 202 Accepted\n"
150 "Set-Cookie: a, b\n",
153 net::HttpVersion(1,1),
154 net::HttpVersion(1,1)
159 TEST(HttpResponseHeadersTest, BlankHeaders) {
175 net::HttpVersion(1,1),
176 net::HttpVersion(1,1)
181 TEST(HttpResponseHeadersTest, NormalizeHeadersVersion) {
182 // Don't believe the http/0.9 version if there are headers!
185 "Content-TYPE: text/html; charset=utf-8\n",
188 "Content-TYPE: text/html; charset=utf-8\n",
191 net::HttpVersion(0,9),
192 net::HttpVersion(1,0)
197 TEST(HttpResponseHeadersTest, PreserveHttp09) {
198 // Accept the HTTP/0.9 version number if there are no headers.
199 // This is how HTTP/0.9 responses get constructed from HttpNetworkTransaction.
206 net::HttpVersion(0,9),
207 net::HttpVersion(0,9)
212 TEST(HttpResponseHeadersTest, NormalizeHeadersMissingOK) {
215 "Content-TYPE: text/html; charset=utf-8\n",
218 "Content-TYPE: text/html; charset=utf-8\n",
221 net::HttpVersion(1,1),
222 net::HttpVersion(1,1)
227 TEST(HttpResponseHeadersTest, NormalizeHeadersBadStatus) {
229 "SCREWED_UP_STATUS_LINE\n"
230 "Content-TYPE: text/html; charset=utf-8\n",
233 "Content-TYPE: text/html; charset=utf-8\n",
236 net::HttpVersion(0,0), // Parse error
237 net::HttpVersion(1,0)
242 TEST(HttpResponseHeadersTest, NormalizeHeadersInvalidStatusCode) {
244 "HTTP/1.1 -1 Unknown\n",
249 net::HttpVersion(1,1),
250 net::HttpVersion(1,1)
255 TEST(HttpResponseHeadersTest, NormalizeHeadersEmpty) {
262 net::HttpVersion(0,0), // Parse Error
263 net::HttpVersion(1,0)
268 TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColon) {
270 "HTTP/1.1 202 Accepted \n"
276 "HTTP/1.1 202 Accepted\n"
281 net::HttpVersion(1,1),
282 net::HttpVersion(1,1)
287 TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColonAtEOL) {
289 "HTTP/1.1 202 Accepted \n"
295 "HTTP/1.1 202 Accepted\n"
302 net::HttpVersion(1,1),
303 net::HttpVersion(1,1)
308 TEST(HttpResponseHeadersTest, NormalizeHeadersOfWhitepace) {
315 net::HttpVersion(0,0), // Parse error
316 net::HttpVersion(1,0)
321 TEST(HttpResponseHeadersTest, RepeatedSetCookie) {
328 "Set-Cookie: x=1, y=2\n",
331 net::HttpVersion(1,1),
332 net::HttpVersion(1,1)
337 TEST(HttpResponseHeadersTest, GetNormalizedHeader) {
338 std::string headers =
340 "Cache-control: private\n"
341 "cache-Control: no-store\n";
342 HeadersToRaw(&headers);
343 scoped_refptr<net::HttpResponseHeaders> parsed(
344 new net::HttpResponseHeaders(headers));
347 EXPECT_TRUE(parsed->GetNormalizedHeader("cache-control", &value));
348 EXPECT_EQ("private, no-store", value);
351 TEST(HttpResponseHeadersTest, Persist) {
353 net::HttpResponseHeaders::PersistOptions options;
354 const char* raw_headers;
355 const char* expected_headers;
357 { net::HttpResponseHeaders::PERSIST_ALL,
359 "Cache-control:private\n"
360 "cache-Control:no-store\n",
363 "Cache-control: private, no-store\n"
365 { net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
367 "connection: keep-alive\n"
373 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE |
374 net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP,
378 "Transfer-Encoding: chunked\n"
379 "CoNnection: keep-alive\n"
380 "cache-control: private, no-cache=\"foo\"\n",
383 "cache-control: private, no-cache=\"foo\"\n"
385 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
388 "Cache-Control: private,no-cache=\"foo, bar\"\n"
392 "Cache-Control: private,no-cache=\"foo, bar\"\n"
394 // ignore bogus no-cache value
395 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
398 "Cache-Control: private,no-cache=foo\n",
402 "Cache-Control: private,no-cache=foo\n"
404 // ignore bogus no-cache value
405 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
408 "Cache-Control: private, no-cache=\n",
412 "Cache-Control: private, no-cache=\n"
414 // ignore empty no-cache value
415 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
418 "Cache-Control: private, no-cache=\"\"\n",
422 "Cache-Control: private, no-cache=\"\"\n"
424 // ignore wrong quotes no-cache value
425 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
428 "Cache-Control: private, no-cache=\'foo\'\n",
432 "Cache-Control: private, no-cache=\'foo\'\n"
434 // ignore unterminated quotes no-cache value
435 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
438 "Cache-Control: private, no-cache=\"foo\n",
442 "Cache-Control: private, no-cache=\"foo\n"
445 { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE,
448 "Cache-Control: private, no-cache=\" foo\t, bar\"\n",
451 "Cache-Control: private, no-cache=\" foo\t, bar\"\n"
453 // header name appears twice, separated by another header
454 { net::HttpResponseHeaders::PERSIST_ALL,
464 // header name appears twice, separated by another header (type 2)
465 { net::HttpResponseHeaders::PERSIST_ALL,
475 // Test filtering of cookie headers.
476 { net::HttpResponseHeaders::PERSIST_SANS_COOKIES,
478 "Set-Cookie: foo=bar; httponly\n"
479 "Set-Cookie: bar=foo\n"
481 "Set-Cookie2: bar2=foo2\n",
486 // Test LWS at the end of a header.
487 { net::HttpResponseHeaders::PERSIST_ALL,
489 "Content-Length: 450 \n"
490 "Content-Encoding: gzip\n",
493 "Content-Length: 450\n"
494 "Content-Encoding: gzip\n"
496 // Test LWS at the end of a header.
497 { net::HttpResponseHeaders::PERSIST_RAW,
499 "Content-Length: 450 \n"
500 "Content-Encoding: gzip\n",
503 "Content-Length: 450\n"
504 "Content-Encoding: gzip\n"
506 // Test filtering of transport security state headers.
507 { net::HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE,
509 "Strict-Transport-Security: max-age=1576800\n"
511 "Public-Key-Pins: max-age=100000; "
512 "pin-sha1=\"ObT42aoSpAqWdY9WfRfL7i0HsVk=\";"
513 "pin-sha1=\"7kW49EVwZG0hSNx41ZO/fUPN0ek=\"",
520 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
521 std::string headers = tests[i].raw_headers;
522 HeadersToRaw(&headers);
523 scoped_refptr<net::HttpResponseHeaders> parsed1(
524 new net::HttpResponseHeaders(headers));
527 parsed1->Persist(&pickle, tests[i].options);
529 PickleIterator iter(pickle);
530 scoped_refptr<net::HttpResponseHeaders> parsed2(
531 new net::HttpResponseHeaders(pickle, &iter));
534 parsed2->GetNormalizedHeaders(&h2);
535 EXPECT_EQ(std::string(tests[i].expected_headers), h2);
539 TEST(HttpResponseHeadersTest, EnumerateHeader_Coalesced) {
540 // Ensure that commas in quoted strings are not regarded as value separators.
541 // Ensure that whitespace following a value is trimmed properly
542 std::string headers =
544 "Cache-control:private , no-cache=\"set-cookie,server\" \n"
545 "cache-Control: no-store\n";
546 HeadersToRaw(&headers);
547 scoped_refptr<net::HttpResponseHeaders> parsed(
548 new net::HttpResponseHeaders(headers));
552 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
553 EXPECT_EQ("private", value);
554 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
555 EXPECT_EQ("no-cache=\"set-cookie,server\"", value);
556 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "cache-control", &value));
557 EXPECT_EQ("no-store", value);
558 EXPECT_FALSE(parsed->EnumerateHeader(&iter, "cache-control", &value));
561 TEST(HttpResponseHeadersTest, EnumerateHeader_Challenge) {
562 // Even though WWW-Authenticate has commas, it should not be treated as
564 std::string headers =
566 "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n"
567 "WWW-Authenticate:Basic realm=quatar\n";
568 HeadersToRaw(&headers);
569 scoped_refptr<net::HttpResponseHeaders> parsed(
570 new net::HttpResponseHeaders(headers));
574 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
575 EXPECT_EQ("Digest realm=foobar, nonce=x, domain=y", value);
576 EXPECT_TRUE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
577 EXPECT_EQ("Basic realm=quatar", value);
578 EXPECT_FALSE(parsed->EnumerateHeader(&iter, "WWW-Authenticate", &value));
581 TEST(HttpResponseHeadersTest, EnumerateHeader_DateValued) {
582 // The comma in a date valued header should not be treated as a
583 // field-value separator
584 std::string headers =
586 "Date: Tue, 07 Aug 2007 23:10:55 GMT\n"
587 "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n";
588 HeadersToRaw(&headers);
589 scoped_refptr<net::HttpResponseHeaders> parsed(
590 new net::HttpResponseHeaders(headers));
593 EXPECT_TRUE(parsed->EnumerateHeader(NULL, "date", &value));
594 EXPECT_EQ("Tue, 07 Aug 2007 23:10:55 GMT", value);
595 EXPECT_TRUE(parsed->EnumerateHeader(NULL, "last-modified", &value));
596 EXPECT_EQ("Wed, 01 Aug 2007 23:23:45 GMT", value);
599 TEST(HttpResponseHeadersTest, DefaultDateToGMT) {
600 // Verify we make the best interpretation when parsing dates that incorrectly
601 // do not end in "GMT" as RFC2616 requires.
602 std::string headers =
604 "Date: Tue, 07 Aug 2007 23:10:55\n"
605 "Last-Modified: Tue, 07 Aug 2007 19:10:55 EDT\n"
606 "Expires: Tue, 07 Aug 2007 23:10:55 UTC\n";
607 HeadersToRaw(&headers);
608 scoped_refptr<net::HttpResponseHeaders> parsed(
609 new net::HttpResponseHeaders(headers));
610 base::Time expected_value;
611 ASSERT_TRUE(base::Time::FromString("Tue, 07 Aug 2007 23:10:55 GMT",
615 // When the timezone is missing, GMT is a good guess as its what RFC2616
617 EXPECT_TRUE(parsed->GetDateValue(&value));
618 EXPECT_EQ(expected_value, value);
619 // If GMT is missing but an RFC822-conforming one is present, use that.
620 EXPECT_TRUE(parsed->GetLastModifiedValue(&value));
621 EXPECT_EQ(expected_value, value);
622 // If an unknown timezone is present, treat like a missing timezone and
623 // default to GMT. The only example of a web server not specifying "GMT"
624 // used "UTC" which is equivalent to GMT.
625 if (parsed->GetExpiresValue(&value))
626 EXPECT_EQ(expected_value, value);
629 TEST(HttpResponseHeadersTest, GetMimeType) {
630 const ContentTypeTestData tests[] = {
631 { "HTTP/1.1 200 OK\n"
632 "Content-type: text/html\n",
636 // Multiple content-type headers should give us the last one.
637 { "HTTP/1.1 200 OK\n"
638 "Content-type: text/html\n"
639 "Content-type: text/html\n",
642 "text/html, text/html" },
643 { "HTTP/1.1 200 OK\n"
644 "Content-type: text/plain\n"
645 "Content-type: text/html\n"
646 "Content-type: text/plain\n"
647 "Content-type: text/html\n",
650 "text/plain, text/html, text/plain, text/html" },
651 // Test charset parsing.
652 { "HTTP/1.1 200 OK\n"
653 "Content-type: text/html\n"
654 "Content-type: text/html; charset=ISO-8859-1\n",
657 "text/html, text/html; charset=ISO-8859-1" },
658 // Test charset in double quotes.
659 { "HTTP/1.1 200 OK\n"
660 "Content-type: text/html\n"
661 "Content-type: text/html; charset=\"ISO-8859-1\"\n",
664 "text/html, text/html; charset=\"ISO-8859-1\"" },
665 // If there are multiple matching content-type headers, we carry
666 // over the charset value.
667 { "HTTP/1.1 200 OK\n"
668 "Content-type: text/html;charset=utf-8\n"
669 "Content-type: text/html\n",
672 "text/html;charset=utf-8, text/html" },
673 // Test single quotes.
674 { "HTTP/1.1 200 OK\n"
675 "Content-type: text/html;charset='utf-8'\n"
676 "Content-type: text/html\n",
679 "text/html;charset='utf-8', text/html" },
680 // Last charset wins if matching content-type.
681 { "HTTP/1.1 200 OK\n"
682 "Content-type: text/html;charset=utf-8\n"
683 "Content-type: text/html;charset=iso-8859-1\n",
686 "text/html;charset=utf-8, text/html;charset=iso-8859-1" },
687 // Charset is ignored if the content types change.
688 { "HTTP/1.1 200 OK\n"
689 "Content-type: text/plain;charset=utf-8\n"
690 "Content-type: text/html\n",
693 "text/plain;charset=utf-8, text/html" },
694 // Empty content-type
695 { "HTTP/1.1 200 OK\n"
701 { "HTTP/1.1 200 OK\n"
702 "Content-type: text/html;charset=\n",
705 "text/html;charset=" },
706 // Multiple charsets, last one wins.
707 { "HTTP/1.1 200 OK\n"
708 "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n",
711 "text/html;charset=utf-8; charset=iso-8859-1" },
713 { "HTTP/1.1 200 OK\n"
714 "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n",
717 "text/html; foo=utf-8; charset=iso-8859-1" },
718 { "HTTP/1.1 200 OK\n"
719 "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n",
722 "text/html ; charset=utf-8 ; bar=iso-8859-1" },
723 // Comma embeded in quotes.
724 { "HTTP/1.1 200 OK\n"
725 "Content-type: text/html ; charset='utf-8,text/plain' ;\n",
727 "utf-8,text/plain", true,
728 "text/html ; charset='utf-8,text/plain' ;" },
729 // Charset with leading spaces.
730 { "HTTP/1.1 200 OK\n"
731 "Content-type: text/html ; charset= 'utf-8' ;\n",
734 "text/html ; charset= 'utf-8' ;" },
735 // Media type comments in mime-type.
736 { "HTTP/1.1 200 OK\n"
737 "Content-type: text/html (html)\n",
740 "text/html (html)" },
741 // Incomplete charset= param
742 { "HTTP/1.1 200 OK\n"
743 "Content-type: text/html; char=\n",
746 "text/html; char=" },
747 // Invalid media type: no slash
748 { "HTTP/1.1 200 OK\n"
749 "Content-type: texthtml\n",
753 // Invalid media type: */*
754 { "HTTP/1.1 200 OK\n"
755 "Content-type: */*\n",
761 for (size_t i = 0; i < arraysize(tests); ++i) {
762 std::string headers(tests[i].raw_headers);
763 HeadersToRaw(&headers);
764 scoped_refptr<net::HttpResponseHeaders> parsed(
765 new net::HttpResponseHeaders(headers));
768 EXPECT_EQ(tests[i].has_mimetype, parsed->GetMimeType(&value));
769 EXPECT_EQ(tests[i].mime_type, value);
771 EXPECT_EQ(tests[i].has_charset, parsed->GetCharset(&value));
772 EXPECT_EQ(tests[i].charset, value);
773 EXPECT_TRUE(parsed->GetNormalizedHeader("content-type", &value));
774 EXPECT_EQ(tests[i].all_content_type, value);
778 TEST(HttpResponseHeadersTest, RequiresValidation) {
781 bool requires_validation;
783 // no expiry info: expires immediately
784 { "HTTP/1.1 200 OK\n"
788 // valid for a little while
789 { "HTTP/1.1 200 OK\n"
790 "cache-control: max-age=10000\n"
794 // expires in the future
795 { "HTTP/1.1 200 OK\n"
796 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
797 "expires: Wed, 28 Nov 2007 01:00:00 GMT\n"
802 { "HTTP/1.1 200 OK\n"
803 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
804 "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
808 // max-age trumps expires
809 { "HTTP/1.1 200 OK\n"
810 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
811 "expires: Wed, 28 Nov 2007 00:00:00 GMT\n"
812 "cache-control: max-age=10000\n"
816 // last-modified heuristic: modified a while ago
817 { "HTTP/1.1 200 OK\n"
818 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
819 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
823 { "HTTP/1.1 203 Non-Authoritative Information\n"
824 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
825 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
829 { "HTTP/1.1 206 Partial Content\n"
830 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
831 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
835 // last-modified heuristic: modified recently
836 { "HTTP/1.1 200 OK\n"
837 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
838 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
842 { "HTTP/1.1 203 Non-Authoritative Information\n"
843 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
844 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
848 { "HTTP/1.1 206 Partial Content\n"
849 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
850 "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
854 // cached permanent redirect
855 { "HTTP/1.1 301 Moved Permanently\n"
859 // another cached permanent redirect
860 { "HTTP/1.1 308 Permanent Redirect\n"
864 // cached redirect: not reusable even though by default it would be
865 { "HTTP/1.1 300 Multiple Choices\n"
866 "Cache-Control: no-cache\n"
870 // cached forever by default
871 { "HTTP/1.1 410 Gone\n"
875 // cached temporary redirect: not reusable
876 { "HTTP/1.1 302 Found\n"
880 // cached temporary redirect: reusable
881 { "HTTP/1.1 302 Found\n"
882 "cache-control: max-age=10000\n"
886 // cache-control: max-age=N overrides expires: date in the past
887 { "HTTP/1.1 200 OK\n"
888 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
889 "expires: Wed, 28 Nov 2007 00:20:11 GMT\n"
890 "cache-control: max-age=10000\n"
894 // cache-control: no-store overrides expires: in the future
895 { "HTTP/1.1 200 OK\n"
896 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
897 "expires: Wed, 29 Nov 2007 00:40:11 GMT\n"
898 "cache-control: no-store,private,no-cache=\"foo\"\n"
902 // pragma: no-cache overrides last-modified heuristic
903 { "HTTP/1.1 200 OK\n"
904 "date: Wed, 28 Nov 2007 00:40:11 GMT\n"
905 "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n"
910 // TODO(darin): add many many more tests here
912 base::Time request_time, response_time, current_time;
913 base::Time::FromString("Wed, 28 Nov 2007 00:40:09 GMT", &request_time);
914 base::Time::FromString("Wed, 28 Nov 2007 00:40:12 GMT", &response_time);
915 base::Time::FromString("Wed, 28 Nov 2007 00:45:20 GMT", ¤t_time);
917 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
918 std::string headers(tests[i].headers);
919 HeadersToRaw(&headers);
920 scoped_refptr<net::HttpResponseHeaders> parsed(
921 new net::HttpResponseHeaders(headers));
923 bool requires_validation =
924 parsed->RequiresValidation(request_time, response_time, current_time);
925 EXPECT_EQ(tests[i].requires_validation, requires_validation);
929 TEST(HttpResponseHeadersTest, Update) {
931 const char* orig_headers;
932 const char* new_headers;
933 const char* expected_headers;
935 { "HTTP/1.1 200 OK\n",
937 "HTTP/1/1 304 Not Modified\n"
938 "connection: keep-alive\n"
939 "Cache-control: max-age=10000\n",
942 "Cache-control: max-age=10000\n"
944 { "HTTP/1.1 200 OK\n"
946 "Cache-control: private\n",
948 "HTTP/1/1 304 Not Modified\n"
949 "connection: keep-alive\n"
950 "Cache-control: max-age=10000\n",
953 "Cache-control: max-age=10000\n"
956 { "HTTP/1.1 200 OK\n"
958 "Cache-control: private\n",
960 "HTTP/1/1 304 Not Modified\n"
961 "connection: keep-alive\n"
962 "Cache-CONTROL: max-age=10000\n",
965 "Cache-CONTROL: max-age=10000\n"
968 { "HTTP/1.1 200 OK\n"
969 "Content-Length: 450\n",
971 "HTTP/1/1 304 Not Modified\n"
972 "connection: keep-alive\n"
973 "Cache-control: max-age=10001 \n",
976 "Cache-control: max-age=10001\n"
977 "Content-Length: 450\n"
979 { "HTTP/1.1 200 OK\n"
980 "X-Frame-Options: DENY\n",
982 "HTTP/1/1 304 Not Modified\n"
983 "X-Frame-Options: ALLOW\n",
986 "X-Frame-Options: DENY\n",
988 { "HTTP/1.1 200 OK\n"
989 "X-WebKit-CSP: default-src 'none'\n",
991 "HTTP/1/1 304 Not Modified\n"
992 "X-WebKit-CSP: default-src *\n",
995 "X-WebKit-CSP: default-src 'none'\n",
997 { "HTTP/1.1 200 OK\n"
998 "X-XSS-Protection: 1\n",
1000 "HTTP/1/1 304 Not Modified\n"
1001 "X-XSS-Protection: 0\n",
1004 "X-XSS-Protection: 1\n",
1006 { "HTTP/1.1 200 OK\n",
1008 "HTTP/1/1 304 Not Modified\n"
1009 "X-Content-Type-Options: nosniff\n",
1015 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1016 std::string orig_headers(tests[i].orig_headers);
1017 HeadersToRaw(&orig_headers);
1018 scoped_refptr<net::HttpResponseHeaders> parsed(
1019 new net::HttpResponseHeaders(orig_headers));
1021 std::string new_headers(tests[i].new_headers);
1022 HeadersToRaw(&new_headers);
1023 scoped_refptr<net::HttpResponseHeaders> new_parsed(
1024 new net::HttpResponseHeaders(new_headers));
1026 parsed->Update(*new_parsed.get());
1028 std::string resulting_headers;
1029 parsed->GetNormalizedHeaders(&resulting_headers);
1030 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1034 TEST(HttpResponseHeadersTest, EnumerateHeaderLines) {
1036 const char* headers;
1037 const char* expected_lines;
1039 { "HTTP/1.1 200 OK\n",
1043 { "HTTP/1.1 200 OK\n"
1048 { "HTTP/1.1 200 OK\n"
1053 "Foo: 1\nBar: 2\nFoo: 3\n"
1055 { "HTTP/1.1 200 OK\n"
1061 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1062 std::string headers(tests[i].headers);
1063 HeadersToRaw(&headers);
1064 scoped_refptr<net::HttpResponseHeaders> parsed(
1065 new net::HttpResponseHeaders(headers));
1067 std::string name, value, lines;
1070 while (parsed->EnumerateHeaderLines(&iter, &name, &value)) {
1073 lines.append(value);
1077 EXPECT_EQ(std::string(tests[i].expected_lines), lines);
1081 TEST(HttpResponseHeadersTest, IsRedirect) {
1083 const char* headers;
1084 const char* location;
1087 { "HTTP/1.1 200 OK\n",
1091 { "HTTP/1.1 301 Moved\n"
1092 "Location: http://foopy/\n",
1096 { "HTTP/1.1 301 Moved\n"
1101 // we use the first location header as the target of the redirect
1102 { "HTTP/1.1 301 Moved\n"
1103 "Location: http://foo/\n"
1104 "Location: http://bar/\n",
1108 // we use the first _valid_ location header as the target of the redirect
1109 { "HTTP/1.1 301 Moved\n"
1111 "Location: http://bar/\n",
1115 // bug 1050541 (location header w/ an unescaped comma)
1116 { "HTTP/1.1 301 Moved\n"
1117 "Location: http://foo/bar,baz.html\n",
1118 "http://foo/bar,baz.html",
1121 // bug 1224617 (location header w/ non-ASCII bytes)
1122 { "HTTP/1.1 301 Moved\n"
1123 "Location: http://foo/bar?key=\xE4\xF6\xFC\n",
1124 "http://foo/bar?key=%E4%F6%FC",
1127 // Shift_JIS, Big5, and GBK contain multibyte characters with the trailing
1128 // byte falling in the ASCII range.
1129 { "HTTP/1.1 301 Moved\n"
1130 "Location: http://foo/bar?key=\x81\x5E\xD8\xBF\n",
1131 "http://foo/bar?key=%81^%D8%BF",
1134 { "HTTP/1.1 301 Moved\n"
1135 "Location: http://foo/bar?key=\x82\x40\xBD\xC4\n",
1136 "http://foo/bar?key=%82@%BD%C4",
1139 { "HTTP/1.1 301 Moved\n"
1140 "Location: http://foo/bar?key=\x83\x5C\x82\x5D\xCB\xD7\n",
1141 "http://foo/bar?key=%83\\%82]%CB%D7",
1145 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1146 std::string headers(tests[i].headers);
1147 HeadersToRaw(&headers);
1148 scoped_refptr<net::HttpResponseHeaders> parsed(
1149 new net::HttpResponseHeaders(headers));
1151 std::string location;
1152 EXPECT_EQ(parsed->IsRedirect(&location), tests[i].is_redirect);
1153 EXPECT_EQ(location, tests[i].location);
1157 TEST(HttpResponseHeadersTest, GetContentLength) {
1159 const char* headers;
1162 { "HTTP/1.1 200 OK\n",
1165 { "HTTP/1.1 200 OK\n"
1166 "Content-Length: 10\n",
1169 { "HTTP/1.1 200 OK\n"
1170 "Content-Length: \n",
1173 { "HTTP/1.1 200 OK\n"
1174 "Content-Length: abc\n",
1177 { "HTTP/1.1 200 OK\n"
1178 "Content-Length: -10\n",
1181 { "HTTP/1.1 200 OK\n"
1182 "Content-Length: +10\n",
1185 { "HTTP/1.1 200 OK\n"
1186 "Content-Length: 23xb5\n",
1189 { "HTTP/1.1 200 OK\n"
1190 "Content-Length: 0xA\n",
1193 { "HTTP/1.1 200 OK\n"
1194 "Content-Length: 010\n",
1197 // Content-Length too big, will overflow an int64
1198 { "HTTP/1.1 200 OK\n"
1199 "Content-Length: 40000000000000000000\n",
1202 { "HTTP/1.1 200 OK\n"
1203 "Content-Length: 10\n",
1206 { "HTTP/1.1 200 OK\n"
1207 "Content-Length: 10 \n",
1210 { "HTTP/1.1 200 OK\n"
1211 "Content-Length: \t10\n",
1214 { "HTTP/1.1 200 OK\n"
1215 "Content-Length: \v10\n",
1218 { "HTTP/1.1 200 OK\n"
1219 "Content-Length: \f10\n",
1222 { "HTTP/1.1 200 OK\n"
1223 "cOnTeNt-LENgth: 33\n",
1226 { "HTTP/1.1 200 OK\n"
1227 "Content-Length: 34\r\n",
1231 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1232 std::string headers(tests[i].headers);
1233 HeadersToRaw(&headers);
1234 scoped_refptr<net::HttpResponseHeaders> parsed(
1235 new net::HttpResponseHeaders(headers));
1237 EXPECT_EQ(tests[i].expected_len, parsed->GetContentLength());
1241 TEST(HttpResponseHeaders, GetContentRange) {
1243 const char* headers;
1244 bool expected_return_value;
1245 int64 expected_first_byte_position;
1246 int64 expected_last_byte_position;
1247 int64 expected_instance_size;
1249 { "HTTP/1.1 206 Partial Content",
1255 { "HTTP/1.1 206 Partial Content\n"
1262 { "HTTP/1.1 206 Partial Content\n"
1263 "Content-Range: megabytes 0-10/50",
1269 { "HTTP/1.1 206 Partial Content\n"
1270 "Content-Range: 0-10/50",
1276 { "HTTP/1.1 206 Partial Content\n"
1277 "Content-Range: Bytes 0-50/51",
1283 { "HTTP/1.1 206 Partial Content\n"
1284 "Content-Range: bytes 0-50/51",
1290 { "HTTP/1.1 206 Partial Content\n"
1291 "Content-Range: bytes\t0-50/51",
1297 { "HTTP/1.1 206 Partial Content\n"
1298 "Content-Range: bytes 0-50/51",
1304 { "HTTP/1.1 206 Partial Content\n"
1305 "Content-Range: bytes 0 - 50 \t / \t51",
1311 { "HTTP/1.1 206 Partial Content\n"
1312 "Content-Range: bytes 0\t-\t50\t/\t51\t",
1318 { "HTTP/1.1 206 Partial Content\n"
1319 "Content-Range: \tbytes\t\t\t 0\t-\t50\t/\t51\t",
1325 { "HTTP/1.1 206 Partial Content\n"
1326 "Content-Range: \t bytes \t 0 - 50 / 5 1",
1332 { "HTTP/1.1 206 Partial Content\n"
1333 "Content-Range: \t bytes \t 0 - 5 0 / 51",
1339 { "HTTP/1.1 206 Partial Content\n"
1340 "Content-Range: bytes 50-0/51",
1346 { "HTTP/1.1 416 Requested range not satisfiable\n"
1347 "Content-Range: bytes * /*",
1353 { "HTTP/1.1 416 Requested range not satisfiable\n"
1354 "Content-Range: bytes * / * ",
1360 { "HTTP/1.1 206 Partial Content\n"
1361 "Content-Range: bytes 0-50/*",
1367 { "HTTP/1.1 206 Partial Content\n"
1368 "Content-Range: bytes 0-50 / * ",
1374 { "HTTP/1.1 206 Partial Content\n"
1375 "Content-Range: bytes 0-10000000000/10000000001",
1381 { "HTTP/1.1 206 Partial Content\n"
1382 "Content-Range: bytes 0-10000000000/10000000000",
1388 // 64 bits wraparound.
1389 { "HTTP/1.1 206 Partial Content\n"
1390 "Content-Range: bytes 0 - 9223372036854775807 / 100",
1396 // 64 bits wraparound.
1397 { "HTTP/1.1 206 Partial Content\n"
1398 "Content-Range: bytes 0 - 100 / -9223372036854775808",
1404 { "HTTP/1.1 206 Partial Content\n"
1405 "Content-Range: bytes */50",
1411 { "HTTP/1.1 206 Partial Content\n"
1412 "Content-Range: bytes 0-50/10",
1418 { "HTTP/1.1 206 Partial Content\n"
1419 "Content-Range: bytes 40-50/45",
1425 { "HTTP/1.1 206 Partial Content\n"
1426 "Content-Range: bytes 0-50/-10",
1432 { "HTTP/1.1 206 Partial Content\n"
1433 "Content-Range: bytes 0-0/1",
1439 { "HTTP/1.1 206 Partial Content\n"
1440 "Content-Range: bytes 0-40000000000000000000/40000000000000000001",
1446 { "HTTP/1.1 206 Partial Content\n"
1447 "Content-Range: bytes 1-/100",
1453 { "HTTP/1.1 206 Partial Content\n"
1454 "Content-Range: bytes -/100",
1460 { "HTTP/1.1 206 Partial Content\n"
1461 "Content-Range: bytes -1/100",
1467 { "HTTP/1.1 206 Partial Content\n"
1468 "Content-Range: bytes 0-1233/*",
1474 { "HTTP/1.1 206 Partial Content\n"
1475 "Content-Range: bytes -123 - -1/100",
1482 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1483 std::string headers(tests[i].headers);
1484 HeadersToRaw(&headers);
1485 scoped_refptr<net::HttpResponseHeaders> parsed(
1486 new net::HttpResponseHeaders(headers));
1488 int64 first_byte_position;
1489 int64 last_byte_position;
1490 int64 instance_size;
1491 bool return_value = parsed->GetContentRange(&first_byte_position,
1492 &last_byte_position,
1494 EXPECT_EQ(tests[i].expected_return_value, return_value);
1495 EXPECT_EQ(tests[i].expected_first_byte_position, first_byte_position);
1496 EXPECT_EQ(tests[i].expected_last_byte_position, last_byte_position);
1497 EXPECT_EQ(tests[i].expected_instance_size, instance_size);
1501 TEST(HttpResponseHeadersTest, IsKeepAlive) {
1503 const char* headers;
1504 bool expected_keep_alive;
1506 // The status line fabricated by HttpNetworkTransaction for a 0.9 response.
1508 { "HTTP/0.9 200 OK",
1511 // This could come from a broken server. Treated as 1.0 because it has a
1513 { "HTTP/0.9 200 OK\n"
1514 "connection: keep-alive\n",
1517 { "HTTP/1.1 200 OK\n",
1520 { "HTTP/1.0 200 OK\n",
1523 { "HTTP/1.0 200 OK\n"
1524 "connection: close\n",
1527 { "HTTP/1.0 200 OK\n"
1528 "connection: keep-alive\n",
1531 { "HTTP/1.0 200 OK\n"
1532 "connection: kEeP-AliVe\n",
1535 { "HTTP/1.0 200 OK\n"
1536 "connection: keep-aliveX\n",
1539 { "HTTP/1.1 200 OK\n"
1540 "connection: close\n",
1543 { "HTTP/1.1 200 OK\n"
1544 "connection: keep-alive\n",
1547 { "HTTP/1.0 200 OK\n"
1548 "proxy-connection: close\n",
1551 { "HTTP/1.0 200 OK\n"
1552 "proxy-connection: keep-alive\n",
1555 { "HTTP/1.1 200 OK\n"
1556 "proxy-connection: close\n",
1559 { "HTTP/1.1 200 OK\n"
1560 "proxy-connection: keep-alive\n",
1564 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1565 std::string headers(tests[i].headers);
1566 HeadersToRaw(&headers);
1567 scoped_refptr<net::HttpResponseHeaders> parsed(
1568 new net::HttpResponseHeaders(headers));
1570 EXPECT_EQ(tests[i].expected_keep_alive, parsed->IsKeepAlive());
1574 TEST(HttpResponseHeadersTest, HasStrongValidators) {
1576 const char* headers;
1577 bool expected_result;
1579 { "HTTP/0.9 200 OK",
1582 { "HTTP/1.0 200 OK\n"
1583 "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1584 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1588 { "HTTP/1.1 200 OK\n"
1589 "Date: Wed, 28 Nov 2007 01:40:10 GMT\n"
1590 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n"
1594 { "HTTP/1.1 200 OK\n"
1595 "Date: Wed, 28 Nov 2007 00:41:10 GMT\n"
1596 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1599 { "HTTP/1.1 200 OK\n"
1600 "Date: Wed, 28 Nov 2007 00:41:09 GMT\n"
1601 "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n",
1604 { "HTTP/1.1 200 OK\n"
1608 // This is not really a weak etag:
1609 { "HTTP/1.1 200 OK\n"
1610 "etag: \"w/foo\"\n",
1613 // This is a weak etag:
1614 { "HTTP/1.1 200 OK\n"
1615 "etag: w/\"foo\"\n",
1618 { "HTTP/1.1 200 OK\n"
1619 "etag: W / \"foo\"\n",
1623 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1624 std::string headers(tests[i].headers);
1625 HeadersToRaw(&headers);
1626 scoped_refptr<net::HttpResponseHeaders> parsed(
1627 new net::HttpResponseHeaders(headers));
1629 EXPECT_EQ(tests[i].expected_result, parsed->HasStrongValidators()) <<
1630 "Failed test case " << i;
1634 TEST(HttpResponseHeadersTest, GetStatusText) {
1635 std::string headers("HTTP/1.1 404 Not Found");
1636 HeadersToRaw(&headers);
1637 scoped_refptr<net::HttpResponseHeaders> parsed(
1638 new net::HttpResponseHeaders(headers));
1639 EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText());
1642 TEST(HttpResponseHeadersTest, GetStatusTextMissing) {
1643 std::string headers("HTTP/1.1 404");
1644 HeadersToRaw(&headers);
1645 scoped_refptr<net::HttpResponseHeaders> parsed(
1646 new net::HttpResponseHeaders(headers));
1647 // Since the status line gets normalized, we have OK
1648 EXPECT_EQ(std::string("OK"), parsed->GetStatusText());
1651 TEST(HttpResponseHeadersTest, GetStatusTextMultiSpace) {
1652 std::string headers("HTTP/1.0 404 Not Found");
1653 HeadersToRaw(&headers);
1654 scoped_refptr<net::HttpResponseHeaders> parsed(
1655 new net::HttpResponseHeaders(headers));
1656 EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText());
1659 TEST(HttpResponseHeadersTest, GetStatusBadStatusLine) {
1660 std::string headers("Foo bar.");
1661 HeadersToRaw(&headers);
1662 scoped_refptr<net::HttpResponseHeaders> parsed(
1663 new net::HttpResponseHeaders(headers));
1664 // The bad status line would have gotten rewritten as
1666 EXPECT_EQ(std::string("OK"), parsed->GetStatusText());
1669 TEST(HttpResponseHeadersTest, AddHeader) {
1671 const char* orig_headers;
1672 const char* new_header;
1673 const char* expected_headers;
1675 { "HTTP/1.1 200 OK\n"
1676 "connection: keep-alive\n"
1677 "Cache-control: max-age=10000\n",
1679 "Content-Length: 450",
1682 "connection: keep-alive\n"
1683 "Cache-control: max-age=10000\n"
1684 "Content-Length: 450\n"
1686 { "HTTP/1.1 200 OK\n"
1687 "connection: keep-alive\n"
1688 "Cache-control: max-age=10000 \n",
1690 "Content-Length: 450 ",
1693 "connection: keep-alive\n"
1694 "Cache-control: max-age=10000\n"
1695 "Content-Length: 450\n"
1699 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1700 std::string orig_headers(tests[i].orig_headers);
1701 HeadersToRaw(&orig_headers);
1702 scoped_refptr<net::HttpResponseHeaders> parsed(
1703 new net::HttpResponseHeaders(orig_headers));
1705 std::string new_header(tests[i].new_header);
1706 parsed->AddHeader(new_header);
1708 std::string resulting_headers;
1709 parsed->GetNormalizedHeaders(&resulting_headers);
1710 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1714 TEST(HttpResponseHeadersTest, RemoveHeader) {
1716 const char* orig_headers;
1717 const char* to_remove;
1718 const char* expected_headers;
1720 { "HTTP/1.1 200 OK\n"
1721 "connection: keep-alive\n"
1722 "Cache-control: max-age=10000\n"
1723 "Content-Length: 450\n",
1728 "connection: keep-alive\n"
1729 "Cache-control: max-age=10000\n"
1731 { "HTTP/1.1 200 OK\n"
1732 "connection: keep-alive \n"
1733 "Content-Length : 450 \n"
1734 "Cache-control: max-age=10000\n",
1739 "connection: keep-alive\n"
1740 "Cache-control: max-age=10000\n"
1744 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1745 std::string orig_headers(tests[i].orig_headers);
1746 HeadersToRaw(&orig_headers);
1747 scoped_refptr<net::HttpResponseHeaders> parsed(
1748 new net::HttpResponseHeaders(orig_headers));
1750 std::string name(tests[i].to_remove);
1751 parsed->RemoveHeader(name);
1753 std::string resulting_headers;
1754 parsed->GetNormalizedHeaders(&resulting_headers);
1755 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1759 TEST(HttpResponseHeadersTest, RemoveIndividualHeader) {
1761 const char* orig_headers;
1762 const char* to_remove_name;
1763 const char* to_remove_value;
1764 const char* expected_headers;
1766 { "HTTP/1.1 200 OK\n"
1767 "connection: keep-alive\n"
1768 "Cache-control: max-age=10000\n"
1769 "Content-Length: 450\n",
1776 "connection: keep-alive\n"
1777 "Cache-control: max-age=10000\n"
1779 { "HTTP/1.1 200 OK\n"
1780 "connection: keep-alive \n"
1781 "Content-Length : 450 \n"
1782 "Cache-control: max-age=10000\n",
1789 "connection: keep-alive\n"
1790 "Cache-control: max-age=10000\n"
1792 { "HTTP/1.1 200 OK\n"
1793 "connection: keep-alive \n"
1794 "Content-Length: 450\n"
1795 "Cache-control: max-age=10000\n",
1797 "Content-Length", // Matching name.
1799 "999", // Mismatching value.
1802 "connection: keep-alive\n"
1803 "Content-Length: 450\n"
1804 "Cache-control: max-age=10000\n"
1806 { "HTTP/1.1 200 OK\n"
1807 "connection: keep-alive \n"
1810 "Cache-control: max-age=10000\n",
1814 "bar, baz", // Space in value.
1817 "connection: keep-alive\n"
1819 "Cache-control: max-age=10000\n"
1821 { "HTTP/1.1 200 OK\n"
1822 "connection: keep-alive \n"
1824 "Cache-control: max-age=10000\n",
1828 "baz", // Only partial match -> ignored.
1831 "connection: keep-alive\n"
1833 "Cache-control: max-age=10000\n"
1837 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1838 std::string orig_headers(tests[i].orig_headers);
1839 HeadersToRaw(&orig_headers);
1840 scoped_refptr<net::HttpResponseHeaders> parsed(
1841 new net::HttpResponseHeaders(orig_headers));
1843 std::string name(tests[i].to_remove_name);
1844 std::string value(tests[i].to_remove_value);
1845 parsed->RemoveHeaderLine(name, value);
1847 std::string resulting_headers;
1848 parsed->GetNormalizedHeaders(&resulting_headers);
1849 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1853 TEST(HttpResponseHeadersTest, ReplaceStatus) {
1855 const char* orig_headers;
1856 const char* new_status;
1857 const char* expected_headers;
1859 { "HTTP/1.1 206 Partial Content\n"
1860 "connection: keep-alive\n"
1861 "Cache-control: max-age=10000\n"
1862 "Content-Length: 450\n",
1867 "connection: keep-alive\n"
1868 "Cache-control: max-age=10000\n"
1869 "Content-Length: 450\n"
1871 { "HTTP/1.1 200 OK\n"
1872 "connection: keep-alive\n",
1874 "HTTP/1.1 304 Not Modified",
1876 "HTTP/1.1 304 Not Modified\n"
1877 "connection: keep-alive\n"
1879 { "HTTP/1.1 200 OK\n"
1880 "connection: keep-alive \n"
1881 "Content-Length : 450 \n"
1882 "Cache-control: max-age=10000\n",
1884 "HTTP/1//1 304 Not Modified",
1886 "HTTP/1.0 304 Not Modified\n"
1887 "connection: keep-alive\n"
1888 "Content-Length: 450\n"
1889 "Cache-control: max-age=10000\n"
1893 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1894 std::string orig_headers(tests[i].orig_headers);
1895 HeadersToRaw(&orig_headers);
1896 scoped_refptr<net::HttpResponseHeaders> parsed(
1897 new net::HttpResponseHeaders(orig_headers));
1899 std::string name(tests[i].new_status);
1900 parsed->ReplaceStatusLine(name);
1902 std::string resulting_headers;
1903 parsed->GetNormalizedHeaders(&resulting_headers);
1904 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1908 TEST(HttpResponseHeadersTest, UpdateWithNewRange) {
1910 const char* orig_headers;
1911 const char* expected_headers;
1912 const char* expected_headers_with_replaced_status;
1914 { "HTTP/1.1 200 OK\n"
1915 "Content-Length: 450\n",
1918 "Content-Range: bytes 3-5/450\n"
1919 "Content-Length: 3\n",
1921 "HTTP/1.1 206 Partial Content\n"
1922 "Content-Range: bytes 3-5/450\n"
1923 "Content-Length: 3\n",
1925 { "HTTP/1.1 200 OK\n"
1926 "Content-Length: 5\n",
1929 "Content-Range: bytes 3-5/5\n"
1930 "Content-Length: 3\n",
1932 "HTTP/1.1 206 Partial Content\n"
1933 "Content-Range: bytes 3-5/5\n"
1934 "Content-Length: 3\n",
1937 const net::HttpByteRange range = net::HttpByteRange::Bounded(3, 5);
1939 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
1940 std::string orig_headers(tests[i].orig_headers);
1941 std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0');
1942 scoped_refptr<net::HttpResponseHeaders> parsed(
1943 new net::HttpResponseHeaders(orig_headers + '\0'));
1944 int64 content_size = parsed->GetContentLength();
1945 std::string resulting_headers;
1947 // Update headers without replacing status line.
1948 parsed->UpdateWithNewRange(range, content_size, false);
1949 parsed->GetNormalizedHeaders(&resulting_headers);
1950 EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
1952 // Replace status line too.
1953 parsed->UpdateWithNewRange(range, content_size, true);
1954 parsed->GetNormalizedHeaders(&resulting_headers);
1955 EXPECT_EQ(std::string(tests[i].expected_headers_with_replaced_status),
1960 TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) {
1961 std::string headers("HTTP/1.1 404\n"
1962 "Content-Length: 450\n"
1963 "Connection: keep-alive\n");
1964 HeadersToRaw(&headers);
1965 scoped_refptr<net::HttpResponseHeaders> parsed(
1966 new net::HttpResponseHeaders(headers));
1968 scoped_ptr<base::Value> event_param(
1969 parsed->NetLogCallback(net::NetLog::LOG_ALL_BUT_BYTES));
1970 scoped_refptr<net::HttpResponseHeaders> recreated;
1972 ASSERT_TRUE(net::HttpResponseHeaders::FromNetLogParam(event_param.get(),
1974 ASSERT_TRUE(recreated.get());
1975 EXPECT_EQ(parsed->GetHttpVersion(), recreated->GetHttpVersion());
1976 EXPECT_EQ(parsed->response_code(), recreated->response_code());
1977 EXPECT_EQ(parsed->GetContentLength(), recreated->GetContentLength());
1978 EXPECT_EQ(parsed->IsKeepAlive(), recreated->IsKeepAlive());
1980 std::string normalized_parsed;
1981 parsed->GetNormalizedHeaders(&normalized_parsed);
1982 std::string normalized_recreated;
1983 parsed->GetNormalizedHeaders(&normalized_recreated);
1984 EXPECT_EQ(normalized_parsed, normalized_recreated);
1987 TEST_F(HttpResponseHeadersCacheControlTest, AbsentMaxAgeReturnsFalse) {
1988 InitializeHeadersWithCacheControl("nocache");
1989 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
1992 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithNoParameterRejected) {
1993 InitializeHeadersWithCacheControl("max-age=,private");
1994 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
1997 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeWithSpaceParameterRejected) {
1998 InitializeHeadersWithCacheControl("max-age= ,private");
1999 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2002 TEST_F(HttpResponseHeadersCacheControlTest,
2003 MaxAgeWithSpaceBeforeEqualsIsRejected) {
2004 InitializeHeadersWithCacheControl("max-age = 7");
2005 EXPECT_FALSE(headers()->GetMaxAgeValue(TimeDeltaPointer()));
2008 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeFirstMatchUsed) {
2009 InitializeHeadersWithCacheControl("max-age=10, max-age=20");
2010 EXPECT_EQ(TimeDelta::FromSeconds(10), GetMaxAgeValue());
2013 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeBogusFirstMatchUsed) {
2014 // max-age10 isn't parsed as max-age; max-age=now is parsed as max-age=0 and
2015 // so max-age=20 is not used.
2016 InitializeHeadersWithCacheControl("max-age10, max-age=now, max-age=20");
2017 EXPECT_EQ(TimeDelta::FromSeconds(0), GetMaxAgeValue());
2020 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeCaseInsensitive) {
2021 InitializeHeadersWithCacheControl("Max-aGe=15");
2022 EXPECT_EQ(TimeDelta::FromSeconds(15), GetMaxAgeValue());
2025 TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeEdgeCases) {
2026 // This test doesn't use TEST_P() for consistency with the rest of the tests
2028 // TODO(ricea): Port the tests in this file to use TEST_P().
2030 const char* max_age_string;
2031 int64 expected_seconds;
2033 {" 1 ", 1}, // Spaces are ignored
2034 {"-1", -1}, // Negative numbers are passed through
2035 {"--1", 0}, // Leading junk gives 0
2036 {"2s", 2}, // trailing junk is ignored
2038 {"'4'", 0}, // single quotes don't work
2039 {"\"5\"", 0}, // double quotes don't work
2040 {"0x6", 0}, // hex not parsed as hex
2041 {"7F", 7}, // hex without 0x still not parsed as hex
2042 {"010", 10}, // octal not parsed as octal
2043 {"9223372036854", 9223372036854},
2044 // {"9223372036855", -9223372036854}, // undefined behaviour
2045 // {"9223372036854775806", -2}, // undefined behaviour
2046 {"9223372036854775807", 9223372036854775807},
2047 {"20000000000000000000",
2048 std::numeric_limits<int64>::max()}, // overflow int64
2050 std::string max_age = "max-age=";
2051 for (size_t i = 0; i < arraysize(tests); ++i) {
2052 InitializeHeadersWithCacheControl(
2053 (max_age + tests[i].max_age_string).c_str());
2054 EXPECT_EQ(tests[i].expected_seconds, GetMaxAgeValue().InSeconds())
2055 << " for max-age=" << tests[i].max_age_string;
2059 TEST_F(HttpResponseHeadersCacheControlTest,
2060 AbsentStaleWhileRevalidateReturnsFalse) {
2061 InitializeHeadersWithCacheControl("max-age=3600");
2062 EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2065 TEST_F(HttpResponseHeadersCacheControlTest,
2066 StaleWhileRevalidateWithoutValueRejected) {
2067 InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=");
2068 EXPECT_FALSE(headers()->GetStaleWhileRevalidateValue(TimeDeltaPointer()));
2071 TEST_F(HttpResponseHeadersCacheControlTest,
2072 StaleWhileRevalidateWithInvalidValueTreatedAsZero) {
2073 InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=true");
2074 EXPECT_EQ(TimeDelta(), GetStaleWhileRevalidateValue());
2077 TEST_F(HttpResponseHeadersCacheControlTest, StaleWhileRevalidateValueReturned) {
2078 InitializeHeadersWithCacheControl("max-age=3600,stale-while-revalidate=7200");
2079 EXPECT_EQ(TimeDelta::FromSeconds(7200), GetStaleWhileRevalidateValue());
2082 TEST_F(HttpResponseHeadersCacheControlTest,
2083 FirstStaleWhileRevalidateValueUsed) {
2084 InitializeHeadersWithCacheControl(
2085 "stale-while-revalidate=1,stale-while-revalidate=7200");
2086 EXPECT_EQ(TimeDelta::FromSeconds(1), GetStaleWhileRevalidateValue());