diff options
author | mostynb@opera.com <mostynb@opera.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-21 08:51:58 +0000 |
---|---|---|
committer | mostynb@opera.com <mostynb@opera.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-21 08:53:25 +0000 |
commit | 778be59b18a1275beec722a549f5c4c2ee1882db (patch) | |
tree | 202f808a42e2a3cf20cb34deacbdfe70906ee01a /net/http/http_response_headers_unittest.cc | |
parent | e2a8b0ed108ed2d26e3f7af5cd9bd0592d9df626 (diff) | |
download | chromium_src-778be59b18a1275beec722a549f5c4c2ee1882db.zip chromium_src-778be59b18a1275beec722a549f5c4c2ee1882db.tar.gz chromium_src-778be59b18a1275beec722a549f5c4c2ee1882db.tar.bz2 |
parameterize http response header tests
Followup to https://codereview.chromium.org/391763002 to allow this
to build on more compilers (which can fail when using structs defined
in funtions in arraysize calls- as mentioned in base/macros.h).
By refactoring to use TEST_P we can skip the manual loop entirely.
BUG=348877
TEST=net_unittests --gtest_filter=Http*
Review URL: https://codereview.chromium.org/448373003
Cr-Commit-Position: refs/heads/master@{#291025}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@291025 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http/http_response_headers_unittest.cc')
-rw-r--r-- | net/http/http_response_headers_unittest.cc | 3139 |
1 files changed, 1654 insertions, 1485 deletions
diff --git a/net/http/http_response_headers_unittest.cc b/net/http/http_response_headers_unittest.cc index 76790e3..409a6bd 100644 --- a/net/http/http_response_headers_unittest.cc +++ b/net/http/http_response_headers_unittest.cc @@ -24,15 +24,6 @@ struct TestData { net::HttpVersion expected_version; }; -struct ContentTypeTestData { - const std::string raw_headers; - const std::string mime_type; - const bool has_mimetype; - const std::string charset; - const bool has_charset; - const std::string all_content_type; -}; - class HttpResponseHeadersTest : public testing::Test { }; @@ -91,7 +82,14 @@ class HttpResponseHeadersCacheControlTest : public HttpResponseHeadersTest { TimeDelta delta_; }; -void TestCommon(const TestData& test) { +class CommonHttpResponseHeadersTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<TestData> { +}; + +TEST_P(CommonHttpResponseHeadersTest, TestCommon) { + const TestData test = GetParam(); + std::string raw_headers(test.raw_headers); HeadersToRaw(&raw_headers); std::string expected_headers(test.expected_headers); @@ -115,11 +113,10 @@ void TestCommon(const TestData& test) { EXPECT_TRUE(test.expected_version == parsed->GetHttpVersion()); } -} // end namespace +TestData response_headers_tests[] = { + { + // Normalise whitespace. -// Check that we normalize headers properly. -TEST(HttpResponseHeadersTest, NormalizeHeadersWhitespace) { - TestData test = { "HTTP/1.1 202 Accepted \n" "Content-TYPE : text/html; charset=utf-8 \n" "Set-Cookie: a \n" @@ -132,14 +129,10 @@ TEST(HttpResponseHeadersTest, NormalizeHeadersWhitespace) { 202, net::HttpVersion(1,1), net::HttpVersion(1,1) - }; - TestCommon(test); -} + }, + { + // Normalize leading whitespace. -// Check that we normalize headers properly (header name is invalid if starts -// with LWS). -TEST(HttpResponseHeadersTest, NormalizeHeadersLeadingWhitespace) { - TestData test = { "HTTP/1.1 202 Accepted \n" // Starts with space -- will be skipped as invalid. " Content-TYPE : text/html; charset=utf-8 \n" @@ -152,12 +145,10 @@ TEST(HttpResponseHeadersTest, NormalizeHeadersLeadingWhitespace) { 202, net::HttpVersion(1,1), net::HttpVersion(1,1) - }; - TestCommon(test); -} + }, + { + // Normalize blank headers. -TEST(HttpResponseHeadersTest, BlankHeaders) { - TestData test = { "HTTP/1.1 200 OK\n" "Header1 : \n" "Header2: \n" @@ -174,13 +165,10 @@ TEST(HttpResponseHeadersTest, BlankHeaders) { 200, net::HttpVersion(1,1), net::HttpVersion(1,1) - }; - TestCommon(test); -} + }, + { + // Don't believe the http/0.9 version if there are headers! -TEST(HttpResponseHeadersTest, NormalizeHeadersVersion) { - // Don't believe the http/0.9 version if there are headers! - TestData test = { "hTtP/0.9 201\n" "Content-TYPE: text/html; charset=utf-8\n", @@ -190,14 +178,12 @@ TEST(HttpResponseHeadersTest, NormalizeHeadersVersion) { 201, net::HttpVersion(0,9), net::HttpVersion(1,0) - }; - TestCommon(test); -} + }, + { + // Accept the HTTP/0.9 version number if there are no headers. + // This is how HTTP/0.9 responses get constructed from + // HttpNetworkTransaction. -TEST(HttpResponseHeadersTest, PreserveHttp09) { - // Accept the HTTP/0.9 version number if there are no headers. - // This is how HTTP/0.9 responses get constructed from HttpNetworkTransaction. - TestData test = { "hTtP/0.9 200 OK\n", "HTTP/0.9 200 OK\n", @@ -205,12 +191,10 @@ TEST(HttpResponseHeadersTest, PreserveHttp09) { 200, net::HttpVersion(0,9), net::HttpVersion(0,9) - }; - TestCommon(test); -} + }, + { + // Add missing OK. -TEST(HttpResponseHeadersTest, NormalizeHeadersMissingOK) { - TestData test = { "HTTP/1.1 201\n" "Content-TYPE: text/html; charset=utf-8\n", @@ -220,12 +204,10 @@ TEST(HttpResponseHeadersTest, NormalizeHeadersMissingOK) { 201, net::HttpVersion(1,1), net::HttpVersion(1,1) - }; - TestCommon(test); -} + }, + { + // Normalize bad status line. -TEST(HttpResponseHeadersTest, NormalizeHeadersBadStatus) { - TestData test = { "SCREWED_UP_STATUS_LINE\n" "Content-TYPE: text/html; charset=utf-8\n", @@ -233,14 +215,12 @@ TEST(HttpResponseHeadersTest, NormalizeHeadersBadStatus) { "Content-TYPE: text/html; charset=utf-8\n", 200, - net::HttpVersion(0,0), // Parse error + net::HttpVersion(0,0), // Parse error. net::HttpVersion(1,0) - }; - TestCommon(test); -} + }, + { + // Normalize invalid status code. -TEST(HttpResponseHeadersTest, NormalizeHeadersInvalidStatusCode) { - TestData test = { "HTTP/1.1 -1 Unknown\n", "HTTP/1.1 200 OK\n", @@ -248,25 +228,21 @@ TEST(HttpResponseHeadersTest, NormalizeHeadersInvalidStatusCode) { 200, net::HttpVersion(1,1), net::HttpVersion(1,1) - }; - TestCommon(test); -} + }, + { + // Normalize empty header. -TEST(HttpResponseHeadersTest, NormalizeHeadersEmpty) { - TestData test = { "", "HTTP/1.0 200 OK\n", 200, - net::HttpVersion(0,0), // Parse Error + net::HttpVersion(0,0), // Parse Error. net::HttpVersion(1,0) - }; - TestCommon(test); -} + }, + { + // Normalize headers that start with a colon. -TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColon) { - TestData test = { "HTTP/1.1 202 Accepted \n" "foo: bar\n" ": a \n" @@ -280,12 +256,10 @@ TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColon) { 202, net::HttpVersion(1,1), net::HttpVersion(1,1) - }; - TestCommon(test); -} + }, + { + // Normalize headers that end with a colon. -TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColonAtEOL) { - TestData test = { "HTTP/1.1 202 Accepted \n" "foo: \n" "bar:\n" @@ -301,25 +275,21 @@ TEST(HttpResponseHeadersTest, NormalizeHeadersStartWithColonAtEOL) { 202, net::HttpVersion(1,1), net::HttpVersion(1,1) - }; - TestCommon(test); -} + }, + { + // Normalize whitespace headers. -TEST(HttpResponseHeadersTest, NormalizeHeadersOfWhitepace) { - TestData test = { "\n \n", "HTTP/1.0 200 OK\n", 200, - net::HttpVersion(0,0), // Parse error + net::HttpVersion(0,0), // Parse error. net::HttpVersion(1,0) - }; - TestCommon(test); -} + }, + { + // Consolidate Set-Cookie headers. -TEST(HttpResponseHeadersTest, RepeatedSetCookie) { - TestData test = { "HTTP/1.1 200 OK\n" "Set-Cookie: x=1\n" "Set-Cookie: y=2\n", @@ -330,9 +300,12 @@ TEST(HttpResponseHeadersTest, RepeatedSetCookie) { 200, net::HttpVersion(1,1), net::HttpVersion(1,1) - }; - TestCommon(test); -} + }, +}; + +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + CommonHttpResponseHeadersTest, + testing::ValuesIn(response_headers_tests)); TEST(HttpResponseHeadersTest, GetNormalizedHeader) { std::string headers = @@ -348,197 +321,208 @@ TEST(HttpResponseHeadersTest, GetNormalizedHeader) { EXPECT_EQ("private, no-store", value); } -TEST(HttpResponseHeadersTest, Persist) { - const struct { - net::HttpResponseHeaders::PersistOptions options; - const char* raw_headers; - const char* expected_headers; - } tests[] = { - { net::HttpResponseHeaders::PERSIST_ALL, - "HTTP/1.1 200 OK\n" - "Cache-control:private\n" - "cache-Control:no-store\n", +struct PersistData { + net::HttpResponseHeaders::PersistOptions options; + const char* raw_headers; + const char* expected_headers; +}; - "HTTP/1.1 200 OK\n" - "Cache-control: private, no-store\n" - }, - { net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP, - "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "server: blah\n", +class PersistenceTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<PersistData> { +}; - "HTTP/1.1 200 OK\n" - "server: blah\n" - }, - { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE | - net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP, - "HTTP/1.1 200 OK\n" - "fOo: 1\n" - "Foo: 2\n" - "Transfer-Encoding: chunked\n" - "CoNnection: keep-alive\n" - "cache-control: private, no-cache=\"foo\"\n", +TEST_P(PersistenceTest, Persist) { + const PersistData test = GetParam(); - "HTTP/1.1 200 OK\n" - "cache-control: private, no-cache=\"foo\"\n" - }, - { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private,no-cache=\"foo, bar\"\n" - "bar", + std::string headers = test.raw_headers; + HeadersToRaw(&headers); + scoped_refptr<net::HttpResponseHeaders> parsed1( + new net::HttpResponseHeaders(headers)); - "HTTP/1.1 200 OK\n" - "Cache-Control: private,no-cache=\"foo, bar\"\n" - }, - // ignore bogus no-cache value - { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private,no-cache=foo\n", + Pickle pickle; + parsed1->Persist(&pickle, test.options); - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private,no-cache=foo\n" - }, - // ignore bogus no-cache value - { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private, no-cache=\n", + PickleIterator iter(pickle); + scoped_refptr<net::HttpResponseHeaders> parsed2( + new net::HttpResponseHeaders(pickle, &iter)); - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private, no-cache=\n" - }, - // ignore empty no-cache value - { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private, no-cache=\"\"\n", + std::string h2; + parsed2->GetNormalizedHeaders(&h2); + EXPECT_EQ(std::string(test.expected_headers), h2); +} - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private, no-cache=\"\"\n" - }, - // ignore wrong quotes no-cache value - { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private, no-cache=\'foo\'\n", +const struct PersistData persistence_tests[] = { + { net::HttpResponseHeaders::PERSIST_ALL, + "HTTP/1.1 200 OK\n" + "Cache-control:private\n" + "cache-Control:no-store\n", - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private, no-cache=\'foo\'\n" - }, - // ignore unterminated quotes no-cache value - { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private, no-cache=\"foo\n", + "HTTP/1.1 200 OK\n" + "Cache-control: private, no-store\n" + }, + { net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP, + "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "server: blah\n", - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private, no-cache=\"foo\n" - }, - // accept sloppy LWS - { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, - "HTTP/1.1 200 OK\n" - "Foo: 2\n" - "Cache-Control: private, no-cache=\" foo\t, bar\"\n", + "HTTP/1.1 200 OK\n" + "server: blah\n" + }, + { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE | + net::HttpResponseHeaders::PERSIST_SANS_HOP_BY_HOP, + "HTTP/1.1 200 OK\n" + "fOo: 1\n" + "Foo: 2\n" + "Transfer-Encoding: chunked\n" + "CoNnection: keep-alive\n" + "cache-control: private, no-cache=\"foo\"\n", - "HTTP/1.1 200 OK\n" - "Cache-Control: private, no-cache=\" foo\t, bar\"\n" - }, - // header name appears twice, separated by another header - { net::HttpResponseHeaders::PERSIST_ALL, - "HTTP/1.1 200 OK\n" - "Foo: 1\n" - "Bar: 2\n" - "Foo: 3\n", + "HTTP/1.1 200 OK\n" + "cache-control: private, no-cache=\"foo\"\n" + }, + { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private,no-cache=\"foo, bar\"\n" + "bar", - "HTTP/1.1 200 OK\n" - "Foo: 1, 3\n" - "Bar: 2\n" - }, - // header name appears twice, separated by another header (type 2) - { net::HttpResponseHeaders::PERSIST_ALL, - "HTTP/1.1 200 OK\n" - "Foo: 1, 3\n" - "Bar: 2\n" - "Foo: 4\n", + "HTTP/1.1 200 OK\n" + "Cache-Control: private,no-cache=\"foo, bar\"\n" + }, + // Ignore bogus no-cache value. + { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private,no-cache=foo\n", - "HTTP/1.1 200 OK\n" - "Foo: 1, 3, 4\n" - "Bar: 2\n" - }, - // Test filtering of cookie headers. - { net::HttpResponseHeaders::PERSIST_SANS_COOKIES, - "HTTP/1.1 200 OK\n" - "Set-Cookie: foo=bar; httponly\n" - "Set-Cookie: bar=foo\n" - "Bar: 1\n" - "Set-Cookie2: bar2=foo2\n", + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private,no-cache=foo\n" + }, + // Ignore bogus no-cache value. + { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private, no-cache=\n", - "HTTP/1.1 200 OK\n" - "Bar: 1\n" - }, - // Test LWS at the end of a header. - { net::HttpResponseHeaders::PERSIST_ALL, - "HTTP/1.1 200 OK\n" - "Content-Length: 450 \n" - "Content-Encoding: gzip\n", + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private, no-cache=\n" + }, + // Ignore empty no-cache value. + { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private, no-cache=\"\"\n", - "HTTP/1.1 200 OK\n" - "Content-Length: 450\n" - "Content-Encoding: gzip\n" - }, - // Test LWS at the end of a header. - { net::HttpResponseHeaders::PERSIST_RAW, - "HTTP/1.1 200 OK\n" - "Content-Length: 450 \n" - "Content-Encoding: gzip\n", + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private, no-cache=\"\"\n" + }, + // Ignore wrong quotes no-cache value. + { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private, no-cache=\'foo\'\n", - "HTTP/1.1 200 OK\n" - "Content-Length: 450\n" - "Content-Encoding: gzip\n" - }, - // Test filtering of transport security state headers. - { net::HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE, - "HTTP/1.1 200 OK\n" - "Strict-Transport-Security: max-age=1576800\n" - "Bar: 1\n" - "Public-Key-Pins: max-age=100000; " - "pin-sha1=\"ObT42aoSpAqWdY9WfRfL7i0HsVk=\";" - "pin-sha1=\"7kW49EVwZG0hSNx41ZO/fUPN0ek=\"", + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private, no-cache=\'foo\'\n" + }, + // Ignore unterminated quotes no-cache value. + { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private, no-cache=\"foo\n", - "HTTP/1.1 200 OK\n" - "Bar: 1\n" - }, - }; - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers = tests[i].raw_headers; - HeadersToRaw(&headers); - scoped_refptr<net::HttpResponseHeaders> parsed1( - new net::HttpResponseHeaders(headers)); + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private, no-cache=\"foo\n" + }, + // Accept sloppy LWS. + { net::HttpResponseHeaders::PERSIST_SANS_NON_CACHEABLE, + "HTTP/1.1 200 OK\n" + "Foo: 2\n" + "Cache-Control: private, no-cache=\" foo\t, bar\"\n", + + "HTTP/1.1 200 OK\n" + "Cache-Control: private, no-cache=\" foo\t, bar\"\n" + }, + // Header name appears twice, separated by another header. + { net::HttpResponseHeaders::PERSIST_ALL, + "HTTP/1.1 200 OK\n" + "Foo: 1\n" + "Bar: 2\n" + "Foo: 3\n", + + "HTTP/1.1 200 OK\n" + "Foo: 1, 3\n" + "Bar: 2\n" + }, + // Header name appears twice, separated by another header (type 2). + { net::HttpResponseHeaders::PERSIST_ALL, + "HTTP/1.1 200 OK\n" + "Foo: 1, 3\n" + "Bar: 2\n" + "Foo: 4\n", - Pickle pickle; - parsed1->Persist(&pickle, tests[i].options); + "HTTP/1.1 200 OK\n" + "Foo: 1, 3, 4\n" + "Bar: 2\n" + }, + // Test filtering of cookie headers. + { net::HttpResponseHeaders::PERSIST_SANS_COOKIES, + "HTTP/1.1 200 OK\n" + "Set-Cookie: foo=bar; httponly\n" + "Set-Cookie: bar=foo\n" + "Bar: 1\n" + "Set-Cookie2: bar2=foo2\n", - PickleIterator iter(pickle); - scoped_refptr<net::HttpResponseHeaders> parsed2( - new net::HttpResponseHeaders(pickle, &iter)); + "HTTP/1.1 200 OK\n" + "Bar: 1\n" + }, + // Test LWS at the end of a header. + { net::HttpResponseHeaders::PERSIST_ALL, + "HTTP/1.1 200 OK\n" + "Content-Length: 450 \n" + "Content-Encoding: gzip\n", - std::string h2; - parsed2->GetNormalizedHeaders(&h2); - EXPECT_EQ(std::string(tests[i].expected_headers), h2); - } -} + "HTTP/1.1 200 OK\n" + "Content-Length: 450\n" + "Content-Encoding: gzip\n" + }, + // Test LWS at the end of a header. + { net::HttpResponseHeaders::PERSIST_RAW, + "HTTP/1.1 200 OK\n" + "Content-Length: 450 \n" + "Content-Encoding: gzip\n", + + "HTTP/1.1 200 OK\n" + "Content-Length: 450\n" + "Content-Encoding: gzip\n" + }, + // Test filtering of transport security state headers. + { net::HttpResponseHeaders::PERSIST_SANS_SECURITY_STATE, + "HTTP/1.1 200 OK\n" + "Strict-Transport-Security: max-age=1576800\n" + "Bar: 1\n" + "Public-Key-Pins: max-age=100000; " + "pin-sha1=\"ObT42aoSpAqWdY9WfRfL7i0HsVk=\";" + "pin-sha1=\"7kW49EVwZG0hSNx41ZO/fUPN0ek=\"", + + "HTTP/1.1 200 OK\n" + "Bar: 1\n" + }, +}; + +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + PersistenceTest, + testing::ValuesIn(persistence_tests)); TEST(HttpResponseHeadersTest, EnumerateHeader_Coalesced) { // Ensure that commas in quoted strings are not regarded as value separators. - // Ensure that whitespace following a value is trimmed properly + // Ensure that whitespace following a value is trimmed properly. std::string headers = "HTTP/1.1 200 OK\n" "Cache-control:private , no-cache=\"set-cookie,server\" \n" @@ -580,7 +564,7 @@ TEST(HttpResponseHeadersTest, EnumerateHeader_Challenge) { TEST(HttpResponseHeadersTest, EnumerateHeader_DateValued) { // The comma in a date valued header should not be treated as a - // field-value separator + // field-value separator. std::string headers = "HTTP/1.1 200 OK\n" "Date: Tue, 07 Aug 2007 23:10:55 GMT\n" @@ -626,1011 +610,1129 @@ TEST(HttpResponseHeadersTest, DefaultDateToGMT) { EXPECT_EQ(expected_value, value); } -TEST(HttpResponseHeadersTest, GetMimeType) { - const ContentTypeTestData tests[] = { - { "HTTP/1.1 200 OK\n" - "Content-type: text/html\n", - "text/html", true, - "", false, - "text/html" }, - // Multiple content-type headers should give us the last one. - { "HTTP/1.1 200 OK\n" - "Content-type: text/html\n" - "Content-type: text/html\n", - "text/html", true, - "", false, - "text/html, text/html" }, - { "HTTP/1.1 200 OK\n" - "Content-type: text/plain\n" - "Content-type: text/html\n" - "Content-type: text/plain\n" - "Content-type: text/html\n", - "text/html", true, - "", false, - "text/plain, text/html, text/plain, text/html" }, - // Test charset parsing. - { "HTTP/1.1 200 OK\n" - "Content-type: text/html\n" - "Content-type: text/html; charset=ISO-8859-1\n", - "text/html", true, - "iso-8859-1", true, - "text/html, text/html; charset=ISO-8859-1" }, - // Test charset in double quotes. - { "HTTP/1.1 200 OK\n" - "Content-type: text/html\n" - "Content-type: text/html; charset=\"ISO-8859-1\"\n", - "text/html", true, - "iso-8859-1", true, - "text/html, text/html; charset=\"ISO-8859-1\"" }, - // If there are multiple matching content-type headers, we carry - // over the charset value. - { "HTTP/1.1 200 OK\n" - "Content-type: text/html;charset=utf-8\n" - "Content-type: text/html\n", - "text/html", true, - "utf-8", true, - "text/html;charset=utf-8, text/html" }, - // Test single quotes. - { "HTTP/1.1 200 OK\n" - "Content-type: text/html;charset='utf-8'\n" - "Content-type: text/html\n", - "text/html", true, - "utf-8", true, - "text/html;charset='utf-8', text/html" }, - // Last charset wins if matching content-type. - { "HTTP/1.1 200 OK\n" - "Content-type: text/html;charset=utf-8\n" - "Content-type: text/html;charset=iso-8859-1\n", - "text/html", true, - "iso-8859-1", true, - "text/html;charset=utf-8, text/html;charset=iso-8859-1" }, - // Charset is ignored if the content types change. - { "HTTP/1.1 200 OK\n" - "Content-type: text/plain;charset=utf-8\n" - "Content-type: text/html\n", - "text/html", true, - "", false, - "text/plain;charset=utf-8, text/html" }, - // Empty content-type - { "HTTP/1.1 200 OK\n" - "Content-type: \n", - "", false, - "", false, - "" }, - // Emtpy charset - { "HTTP/1.1 200 OK\n" - "Content-type: text/html;charset=\n", - "text/html", true, - "", false, - "text/html;charset=" }, - // Multiple charsets, last one wins. - { "HTTP/1.1 200 OK\n" - "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n", - "text/html", true, - "iso-8859-1", true, - "text/html;charset=utf-8; charset=iso-8859-1" }, - // Multiple params. - { "HTTP/1.1 200 OK\n" - "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n", - "text/html", true, - "iso-8859-1", true, - "text/html; foo=utf-8; charset=iso-8859-1" }, - { "HTTP/1.1 200 OK\n" - "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n", - "text/html", true, - "utf-8", true, - "text/html ; charset=utf-8 ; bar=iso-8859-1" }, - // Comma embeded in quotes. - { "HTTP/1.1 200 OK\n" - "Content-type: text/html ; charset='utf-8,text/plain' ;\n", - "text/html", true, - "utf-8,text/plain", true, - "text/html ; charset='utf-8,text/plain' ;" }, - // Charset with leading spaces. - { "HTTP/1.1 200 OK\n" - "Content-type: text/html ; charset= 'utf-8' ;\n", - "text/html", true, - "utf-8", true, - "text/html ; charset= 'utf-8' ;" }, - // Media type comments in mime-type. - { "HTTP/1.1 200 OK\n" - "Content-type: text/html (html)\n", - "text/html", true, - "", false, - "text/html (html)" }, - // Incomplete charset= param - { "HTTP/1.1 200 OK\n" - "Content-type: text/html; char=\n", - "text/html", true, - "", false, - "text/html; char=" }, - // Invalid media type: no slash - { "HTTP/1.1 200 OK\n" - "Content-type: texthtml\n", - "", false, - "", false, - "texthtml" }, - // Invalid media type: */* - { "HTTP/1.1 200 OK\n" - "Content-type: */*\n", - "", false, - "", false, - "*/*" }, - }; - - for (size_t i = 0; i < arraysize(tests); ++i) { - std::string headers(tests[i].raw_headers); - HeadersToRaw(&headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(headers)); +struct ContentTypeTestData { + const std::string raw_headers; + const std::string mime_type; + const bool has_mimetype; + const std::string charset; + const bool has_charset; + const std::string all_content_type; +}; - std::string value; - EXPECT_EQ(tests[i].has_mimetype, parsed->GetMimeType(&value)); - EXPECT_EQ(tests[i].mime_type, value); - value.clear(); - EXPECT_EQ(tests[i].has_charset, parsed->GetCharset(&value)); - EXPECT_EQ(tests[i].charset, value); - EXPECT_TRUE(parsed->GetNormalizedHeader("content-type", &value)); - EXPECT_EQ(tests[i].all_content_type, value); - } +class ContentTypeTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<ContentTypeTestData> { +}; + +TEST_P(ContentTypeTest, GetMimeType) { + const ContentTypeTestData test = GetParam(); + + std::string headers(test.raw_headers); + HeadersToRaw(&headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(headers)); + + std::string value; + EXPECT_EQ(test.has_mimetype, parsed->GetMimeType(&value)); + EXPECT_EQ(test.mime_type, value); + value.clear(); + EXPECT_EQ(test.has_charset, parsed->GetCharset(&value)); + EXPECT_EQ(test.charset, value); + EXPECT_TRUE(parsed->GetNormalizedHeader("content-type", &value)); + EXPECT_EQ(test.all_content_type, value); } -TEST(HttpResponseHeadersTest, RequiresValidation) { - const struct { - const char* headers; - bool requires_validation; - } tests[] = { - // no expiry info: expires immediately - { "HTTP/1.1 200 OK\n" - "\n", - true - }, - // valid for a little while - { "HTTP/1.1 200 OK\n" - "cache-control: max-age=10000\n" - "\n", - false - }, - // expires in the future - { "HTTP/1.1 200 OK\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "expires: Wed, 28 Nov 2007 01:00:00 GMT\n" - "\n", - false - }, - // expired already - { "HTTP/1.1 200 OK\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "expires: Wed, 28 Nov 2007 00:00:00 GMT\n" - "\n", - true - }, - // max-age trumps expires - { "HTTP/1.1 200 OK\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "expires: Wed, 28 Nov 2007 00:00:00 GMT\n" - "cache-control: max-age=10000\n" - "\n", - false - }, - // last-modified heuristic: modified a while ago - { "HTTP/1.1 200 OK\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" - "\n", - false - }, - { "HTTP/1.1 203 Non-Authoritative Information\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" - "\n", - false - }, - { "HTTP/1.1 206 Partial Content\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" - "\n", - false - }, - // last-modified heuristic: modified recently - { "HTTP/1.1 200 OK\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n" - "\n", - true - }, - { "HTTP/1.1 203 Non-Authoritative Information\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n" - "\n", - true - }, - { "HTTP/1.1 206 Partial Content\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n" - "\n", - true - }, - // cached permanent redirect - { "HTTP/1.1 301 Moved Permanently\n" - "\n", - false - }, - // another cached permanent redirect - { "HTTP/1.1 308 Permanent Redirect\n" - "\n", - false - }, - // cached redirect: not reusable even though by default it would be - { "HTTP/1.1 300 Multiple Choices\n" - "Cache-Control: no-cache\n" - "\n", - true - }, - // cached forever by default - { "HTTP/1.1 410 Gone\n" - "\n", - false - }, - // cached temporary redirect: not reusable - { "HTTP/1.1 302 Found\n" - "\n", - true - }, - // cached temporary redirect: reusable - { "HTTP/1.1 302 Found\n" - "cache-control: max-age=10000\n" - "\n", - false - }, - // cache-control: max-age=N overrides expires: date in the past - { "HTTP/1.1 200 OK\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "expires: Wed, 28 Nov 2007 00:20:11 GMT\n" - "cache-control: max-age=10000\n" - "\n", - false - }, - // cache-control: no-store overrides expires: in the future - { "HTTP/1.1 200 OK\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "expires: Wed, 29 Nov 2007 00:40:11 GMT\n" - "cache-control: no-store,private,no-cache=\"foo\"\n" - "\n", - true - }, - // pragma: no-cache overrides last-modified heuristic - { "HTTP/1.1 200 OK\n" - "date: Wed, 28 Nov 2007 00:40:11 GMT\n" - "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" - "pragma: no-cache\n" - "\n", - true - }, - // TODO(darin): add many many more tests here - }; +const ContentTypeTestData mimetype_tests[] = { + { "HTTP/1.1 200 OK\n" + "Content-type: text/html\n", + "text/html", true, + "", false, + "text/html" }, + // Multiple content-type headers should give us the last one. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html\n" + "Content-type: text/html\n", + "text/html", true, + "", false, + "text/html, text/html" }, + { "HTTP/1.1 200 OK\n" + "Content-type: text/plain\n" + "Content-type: text/html\n" + "Content-type: text/plain\n" + "Content-type: text/html\n", + "text/html", true, + "", false, + "text/plain, text/html, text/plain, text/html" }, + // Test charset parsing. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html\n" + "Content-type: text/html; charset=ISO-8859-1\n", + "text/html", true, + "iso-8859-1", true, + "text/html, text/html; charset=ISO-8859-1" }, + // Test charset in double quotes. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html\n" + "Content-type: text/html; charset=\"ISO-8859-1\"\n", + "text/html", true, + "iso-8859-1", true, + "text/html, text/html; charset=\"ISO-8859-1\"" }, + // If there are multiple matching content-type headers, we carry + // over the charset value. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html;charset=utf-8\n" + "Content-type: text/html\n", + "text/html", true, + "utf-8", true, + "text/html;charset=utf-8, text/html" }, + // Test single quotes. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html;charset='utf-8'\n" + "Content-type: text/html\n", + "text/html", true, + "utf-8", true, + "text/html;charset='utf-8', text/html" }, + // Last charset wins if matching content-type. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html;charset=utf-8\n" + "Content-type: text/html;charset=iso-8859-1\n", + "text/html", true, + "iso-8859-1", true, + "text/html;charset=utf-8, text/html;charset=iso-8859-1" }, + // Charset is ignored if the content types change. + { "HTTP/1.1 200 OK\n" + "Content-type: text/plain;charset=utf-8\n" + "Content-type: text/html\n", + "text/html", true, + "", false, + "text/plain;charset=utf-8, text/html" }, + // Empty content-type. + { "HTTP/1.1 200 OK\n" + "Content-type: \n", + "", false, + "", false, + "" }, + // Emtpy charset. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html;charset=\n", + "text/html", true, + "", false, + "text/html;charset=" }, + // Multiple charsets, last one wins. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n", + "text/html", true, + "iso-8859-1", true, + "text/html;charset=utf-8; charset=iso-8859-1" }, + // Multiple params. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n", + "text/html", true, + "iso-8859-1", true, + "text/html; foo=utf-8; charset=iso-8859-1" }, + { "HTTP/1.1 200 OK\n" + "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n", + "text/html", true, + "utf-8", true, + "text/html ; charset=utf-8 ; bar=iso-8859-1" }, + // Comma embeded in quotes. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html ; charset='utf-8,text/plain' ;\n", + "text/html", true, + "utf-8,text/plain", true, + "text/html ; charset='utf-8,text/plain' ;" }, + // Charset with leading spaces. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html ; charset= 'utf-8' ;\n", + "text/html", true, + "utf-8", true, + "text/html ; charset= 'utf-8' ;" }, + // Media type comments in mime-type. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html (html)\n", + "text/html", true, + "", false, + "text/html (html)" }, + // Incomplete charset= param. + { "HTTP/1.1 200 OK\n" + "Content-type: text/html; char=\n", + "text/html", true, + "", false, + "text/html; char=" }, + // Invalid media type: no slash. + { "HTTP/1.1 200 OK\n" + "Content-type: texthtml\n", + "", false, + "", false, + "texthtml" }, + // Invalid media type: "*/*". + { "HTTP/1.1 200 OK\n" + "Content-type: */*\n", + "", false, + "", false, + "*/*" }, +}; + +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + ContentTypeTest, + testing::ValuesIn(mimetype_tests)); + +struct RequiresValidationTestData { + const char* headers; + bool requires_validation; +}; + +class RequiresValidationTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<RequiresValidationTestData> { +}; + +TEST_P(RequiresValidationTest, RequiresValidation) { + const RequiresValidationTestData test = GetParam(); + base::Time request_time, response_time, current_time; base::Time::FromString("Wed, 28 Nov 2007 00:40:09 GMT", &request_time); base::Time::FromString("Wed, 28 Nov 2007 00:40:12 GMT", &response_time); base::Time::FromString("Wed, 28 Nov 2007 00:45:20 GMT", ¤t_time); - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers(tests[i].headers); - HeadersToRaw(&headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(headers)); + std::string headers(test.headers); + HeadersToRaw(&headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(headers)); - bool requires_validation = - parsed->RequiresValidation(request_time, response_time, current_time); - EXPECT_EQ(tests[i].requires_validation, requires_validation); - } + bool requires_validation = + parsed->RequiresValidation(request_time, response_time, current_time); + EXPECT_EQ(test.requires_validation, requires_validation); } -TEST(HttpResponseHeadersTest, Update) { - const struct { - const char* orig_headers; - const char* new_headers; - const char* expected_headers; - } tests[] = { - { "HTTP/1.1 200 OK\n", +const struct RequiresValidationTestData requires_validation_tests[] = { + // No expiry info: expires immediately. + { "HTTP/1.1 200 OK\n" + "\n", + true + }, + // No expiry info: expires immediately. + { "HTTP/1.1 200 OK\n" + "\n", + true + }, + // Valid for a little while. + { "HTTP/1.1 200 OK\n" + "cache-control: max-age=10000\n" + "\n", + false + }, + // Expires in the future. + { "HTTP/1.1 200 OK\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "expires: Wed, 28 Nov 2007 01:00:00 GMT\n" + "\n", + false + }, + // Already expired. + { "HTTP/1.1 200 OK\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "expires: Wed, 28 Nov 2007 00:00:00 GMT\n" + "\n", + true + }, + // Max-age trumps expires. + { "HTTP/1.1 200 OK\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "expires: Wed, 28 Nov 2007 00:00:00 GMT\n" + "cache-control: max-age=10000\n" + "\n", + false + }, + // Last-modified heuristic: modified a while ago. + { "HTTP/1.1 200 OK\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" + "\n", + false + }, + { "HTTP/1.1 203 Non-Authoritative Information\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" + "\n", + false + }, + { "HTTP/1.1 206 Partial Content\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" + "\n", + false + }, + // Last-modified heuristic: modified recently. + { "HTTP/1.1 200 OK\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n" + "\n", + true + }, + { "HTTP/1.1 203 Non-Authoritative Information\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n" + "\n", + true + }, + { "HTTP/1.1 206 Partial Content\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "last-modified: Wed, 28 Nov 2007 00:40:10 GMT\n" + "\n", + true + }, + // Cached permanent redirect. + { "HTTP/1.1 301 Moved Permanently\n" + "\n", + false + }, + // Another cached permanent redirect. + { "HTTP/1.1 308 Permanent Redirect\n" + "\n", + false + }, + // Cached redirect: not reusable even though by default it would be. + { "HTTP/1.1 300 Multiple Choices\n" + "Cache-Control: no-cache\n" + "\n", + true + }, + // Cached forever by default. + { "HTTP/1.1 410 Gone\n" + "\n", + false + }, + // Cached temporary redirect: not reusable. + { "HTTP/1.1 302 Found\n" + "\n", + true + }, + // Cached temporary redirect: reusable. + { "HTTP/1.1 302 Found\n" + "cache-control: max-age=10000\n" + "\n", + false + }, + // Cache-control: max-age=N overrides expires: date in the past. + { "HTTP/1.1 200 OK\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "expires: Wed, 28 Nov 2007 00:20:11 GMT\n" + "cache-control: max-age=10000\n" + "\n", + false + }, + // Cache-control: no-store overrides expires: in the future. + { "HTTP/1.1 200 OK\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "expires: Wed, 29 Nov 2007 00:40:11 GMT\n" + "cache-control: no-store,private,no-cache=\"foo\"\n" + "\n", + true + }, + // Pragma: no-cache overrides last-modified heuristic. + { "HTTP/1.1 200 OK\n" + "date: Wed, 28 Nov 2007 00:40:11 GMT\n" + "last-modified: Wed, 27 Nov 2007 08:00:00 GMT\n" + "pragma: no-cache\n" + "\n", + true + }, + + // TODO(darin): Add many many more tests here. +}; - "HTTP/1/1 304 Not Modified\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n", +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + RequiresValidationTest, + testing::ValuesIn(requires_validation_tests)); - "HTTP/1.1 200 OK\n" - "Cache-control: max-age=10000\n" - }, - { "HTTP/1.1 200 OK\n" - "Foo: 1\n" - "Cache-control: private\n", +struct UpdateTestData { + const char* orig_headers; + const char* new_headers; + const char* expected_headers; +}; - "HTTP/1/1 304 Not Modified\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n", +class UpdateTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<UpdateTestData> { +}; - "HTTP/1.1 200 OK\n" - "Cache-control: max-age=10000\n" - "Foo: 1\n" - }, - { "HTTP/1.1 200 OK\n" - "Foo: 1\n" - "Cache-control: private\n", +TEST_P(UpdateTest, Update) { + const UpdateTestData test = GetParam(); - "HTTP/1/1 304 Not Modified\n" - "connection: keep-alive\n" - "Cache-CONTROL: max-age=10000\n", + std::string orig_headers(test.orig_headers); + HeadersToRaw(&orig_headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(orig_headers)); - "HTTP/1.1 200 OK\n" - "Cache-CONTROL: max-age=10000\n" - "Foo: 1\n" - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: 450\n", + std::string new_headers(test.new_headers); + HeadersToRaw(&new_headers); + scoped_refptr<net::HttpResponseHeaders> new_parsed( + new net::HttpResponseHeaders(new_headers)); - "HTTP/1/1 304 Not Modified\n" - "connection: keep-alive\n" - "Cache-control: max-age=10001 \n", + parsed->Update(*new_parsed.get()); - "HTTP/1.1 200 OK\n" - "Cache-control: max-age=10001\n" - "Content-Length: 450\n" - }, - { "HTTP/1.1 200 OK\n" - "X-Frame-Options: DENY\n", + std::string resulting_headers; + parsed->GetNormalizedHeaders(&resulting_headers); + EXPECT_EQ(std::string(test.expected_headers), resulting_headers); +} - "HTTP/1/1 304 Not Modified\n" - "X-Frame-Options: ALLOW\n", +const UpdateTestData update_tests[] = { + { "HTTP/1.1 200 OK\n", - "HTTP/1.1 200 OK\n" - "X-Frame-Options: DENY\n", - }, - { "HTTP/1.1 200 OK\n" - "X-WebKit-CSP: default-src 'none'\n", + "HTTP/1/1 304 Not Modified\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n", - "HTTP/1/1 304 Not Modified\n" - "X-WebKit-CSP: default-src *\n", + "HTTP/1.1 200 OK\n" + "Cache-control: max-age=10000\n" + }, + { "HTTP/1.1 200 OK\n" + "Foo: 1\n" + "Cache-control: private\n", - "HTTP/1.1 200 OK\n" - "X-WebKit-CSP: default-src 'none'\n", - }, - { "HTTP/1.1 200 OK\n" - "X-XSS-Protection: 1\n", + "HTTP/1/1 304 Not Modified\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n", - "HTTP/1/1 304 Not Modified\n" - "X-XSS-Protection: 0\n", + "HTTP/1.1 200 OK\n" + "Cache-control: max-age=10000\n" + "Foo: 1\n" + }, + { "HTTP/1.1 200 OK\n" + "Foo: 1\n" + "Cache-control: private\n", - "HTTP/1.1 200 OK\n" - "X-XSS-Protection: 1\n", - }, - { "HTTP/1.1 200 OK\n", + "HTTP/1/1 304 Not Modified\n" + "connection: keep-alive\n" + "Cache-CONTROL: max-age=10000\n", - "HTTP/1/1 304 Not Modified\n" - "X-Content-Type-Options: nosniff\n", + "HTTP/1.1 200 OK\n" + "Cache-CONTROL: max-age=10000\n" + "Foo: 1\n" + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: 450\n", - "HTTP/1.1 200 OK\n" - }, - }; + "HTTP/1/1 304 Not Modified\n" + "connection: keep-alive\n" + "Cache-control: max-age=10001 \n", - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string orig_headers(tests[i].orig_headers); - HeadersToRaw(&orig_headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(orig_headers)); + "HTTP/1.1 200 OK\n" + "Cache-control: max-age=10001\n" + "Content-Length: 450\n" + }, + { "HTTP/1.1 200 OK\n" + "X-Frame-Options: DENY\n", - std::string new_headers(tests[i].new_headers); - HeadersToRaw(&new_headers); - scoped_refptr<net::HttpResponseHeaders> new_parsed( - new net::HttpResponseHeaders(new_headers)); + "HTTP/1/1 304 Not Modified\n" + "X-Frame-Options: ALLOW\n", - parsed->Update(*new_parsed.get()); + "HTTP/1.1 200 OK\n" + "X-Frame-Options: DENY\n", + }, + { "HTTP/1.1 200 OK\n" + "X-WebKit-CSP: default-src 'none'\n", - std::string resulting_headers; - parsed->GetNormalizedHeaders(&resulting_headers); - EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers); - } -} + "HTTP/1/1 304 Not Modified\n" + "X-WebKit-CSP: default-src *\n", -TEST(HttpResponseHeadersTest, EnumerateHeaderLines) { - const struct { - const char* headers; - const char* expected_lines; - } tests[] = { - { "HTTP/1.1 200 OK\n", - - "" - }, - { "HTTP/1.1 200 OK\n" - "Foo: 1\n", - - "Foo: 1\n" - }, - { "HTTP/1.1 200 OK\n" - "Foo: 1\n" - "Bar: 2\n" - "Foo: 3\n", - - "Foo: 1\nBar: 2\nFoo: 3\n" - }, - { "HTTP/1.1 200 OK\n" - "Foo: 1, 2, 3\n", - - "Foo: 1, 2, 3\n" - }, - }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers(tests[i].headers); - HeadersToRaw(&headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(headers)); + "HTTP/1.1 200 OK\n" + "X-WebKit-CSP: default-src 'none'\n", + }, + { "HTTP/1.1 200 OK\n" + "X-XSS-Protection: 1\n", + + "HTTP/1/1 304 Not Modified\n" + "X-XSS-Protection: 0\n", + + "HTTP/1.1 200 OK\n" + "X-XSS-Protection: 1\n", + }, + { "HTTP/1.1 200 OK\n", + + "HTTP/1/1 304 Not Modified\n" + "X-Content-Type-Options: nosniff\n", + + "HTTP/1.1 200 OK\n" + }, +}; + +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + UpdateTest, + testing::ValuesIn(update_tests)); - std::string name, value, lines; +struct EnumerateHeaderTestData { + const char* headers; + const char* expected_lines; +}; + +class EnumerateHeaderLinesTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<EnumerateHeaderTestData> { +}; + +TEST_P(EnumerateHeaderLinesTest, EnumerateHeaderLines) { + const EnumerateHeaderTestData test = GetParam(); + + std::string headers(test.headers); + HeadersToRaw(&headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(headers)); - void* iter = NULL; - while (parsed->EnumerateHeaderLines(&iter, &name, &value)) { - lines.append(name); - lines.append(": "); - lines.append(value); - lines.append("\n"); - } + std::string name, value, lines; - EXPECT_EQ(std::string(tests[i].expected_lines), lines); + void* iter = NULL; + while (parsed->EnumerateHeaderLines(&iter, &name, &value)) { + lines.append(name); + lines.append(": "); + lines.append(value); + lines.append("\n"); } + + EXPECT_EQ(std::string(test.expected_lines), lines); } -TEST(HttpResponseHeadersTest, IsRedirect) { - const struct { - const char* headers; - const char* location; - bool is_redirect; - } tests[] = { - { "HTTP/1.1 200 OK\n", - "", - false - }, - { "HTTP/1.1 301 Moved\n" - "Location: http://foopy/\n", - "http://foopy/", - true - }, - { "HTTP/1.1 301 Moved\n" - "Location: \t \n", - "", - false - }, - // we use the first location header as the target of the redirect - { "HTTP/1.1 301 Moved\n" - "Location: http://foo/\n" - "Location: http://bar/\n", - "http://foo/", - true - }, - // we use the first _valid_ location header as the target of the redirect - { "HTTP/1.1 301 Moved\n" - "Location: \n" - "Location: http://bar/\n", - "http://bar/", - true - }, - // bug 1050541 (location header w/ an unescaped comma) - { "HTTP/1.1 301 Moved\n" - "Location: http://foo/bar,baz.html\n", - "http://foo/bar,baz.html", - true - }, - // bug 1224617 (location header w/ non-ASCII bytes) - { "HTTP/1.1 301 Moved\n" - "Location: http://foo/bar?key=\xE4\xF6\xFC\n", - "http://foo/bar?key=%E4%F6%FC", - true - }, - // Shift_JIS, Big5, and GBK contain multibyte characters with the trailing - // byte falling in the ASCII range. - { "HTTP/1.1 301 Moved\n" - "Location: http://foo/bar?key=\x81\x5E\xD8\xBF\n", - "http://foo/bar?key=%81^%D8%BF", - true - }, - { "HTTP/1.1 301 Moved\n" - "Location: http://foo/bar?key=\x82\x40\xBD\xC4\n", - "http://foo/bar?key=%82@%BD%C4", - true - }, - { "HTTP/1.1 301 Moved\n" - "Location: http://foo/bar?key=\x83\x5C\x82\x5D\xCB\xD7\n", - "http://foo/bar?key=%83\\%82]%CB%D7", - true - }, - }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers(tests[i].headers); - HeadersToRaw(&headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(headers)); +const EnumerateHeaderTestData enumerate_header_tests[] = { + { "HTTP/1.1 200 OK\n", - std::string location; - EXPECT_EQ(parsed->IsRedirect(&location), tests[i].is_redirect); - EXPECT_EQ(location, tests[i].location); - } + "" + }, + { "HTTP/1.1 200 OK\n" + "Foo: 1\n", + + "Foo: 1\n" + }, + { "HTTP/1.1 200 OK\n" + "Foo: 1\n" + "Bar: 2\n" + "Foo: 3\n", + + "Foo: 1\nBar: 2\nFoo: 3\n" + }, + { "HTTP/1.1 200 OK\n" + "Foo: 1, 2, 3\n", + + "Foo: 1, 2, 3\n" + }, +}; + +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + EnumerateHeaderLinesTest, + testing::ValuesIn(enumerate_header_tests)); + +struct IsRedirectTestData { + const char* headers; + const char* location; + bool is_redirect; +}; + +class IsRedirectTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<IsRedirectTestData> { +}; + +TEST_P(IsRedirectTest, IsRedirect) { + const IsRedirectTestData test = GetParam(); + + std::string headers(test.headers); + HeadersToRaw(&headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(headers)); + + std::string location; + EXPECT_EQ(parsed->IsRedirect(&location), test.is_redirect); + EXPECT_EQ(location, test.location); } -TEST(HttpResponseHeadersTest, GetContentLength) { - const struct { - const char* headers; - int64 expected_len; - } tests[] = { - { "HTTP/1.1 200 OK\n", - -1 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: 10\n", - 10 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: \n", - -1 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: abc\n", - -1 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: -10\n", - -1 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: +10\n", - -1 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: 23xb5\n", - -1 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: 0xA\n", - -1 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: 010\n", - 10 - }, - // Content-Length too big, will overflow an int64 - { "HTTP/1.1 200 OK\n" - "Content-Length: 40000000000000000000\n", - -1 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: 10\n", - 10 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: 10 \n", - 10 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: \t10\n", - 10 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: \v10\n", - -1 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: \f10\n", - -1 - }, - { "HTTP/1.1 200 OK\n" - "cOnTeNt-LENgth: 33\n", - 33 - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: 34\r\n", - -1 - }, - }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers(tests[i].headers); - HeadersToRaw(&headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(headers)); +const IsRedirectTestData is_redirect_tests[] = { + { "HTTP/1.1 200 OK\n", + "", + false + }, + { "HTTP/1.1 301 Moved\n" + "Location: http://foopy/\n", + "http://foopy/", + true + }, + { "HTTP/1.1 301 Moved\n" + "Location: \t \n", + "", + false + }, + // We use the first location header as the target of the redirect. + { "HTTP/1.1 301 Moved\n" + "Location: http://foo/\n" + "Location: http://bar/\n", + "http://foo/", + true + }, + // We use the first _valid_ location header as the target of the redirect. + { "HTTP/1.1 301 Moved\n" + "Location: \n" + "Location: http://bar/\n", + "http://bar/", + true + }, + // Bug 1050541 (location header with an unescaped comma). + { "HTTP/1.1 301 Moved\n" + "Location: http://foo/bar,baz.html\n", + "http://foo/bar,baz.html", + true + }, + // Bug 1224617 (location header with non-ASCII bytes). + { "HTTP/1.1 301 Moved\n" + "Location: http://foo/bar?key=\xE4\xF6\xFC\n", + "http://foo/bar?key=%E4%F6%FC", + true + }, + // Shift_JIS, Big5, and GBK contain multibyte characters with the trailing + // byte falling in the ASCII range. + { "HTTP/1.1 301 Moved\n" + "Location: http://foo/bar?key=\x81\x5E\xD8\xBF\n", + "http://foo/bar?key=%81^%D8%BF", + true + }, + { "HTTP/1.1 301 Moved\n" + "Location: http://foo/bar?key=\x82\x40\xBD\xC4\n", + "http://foo/bar?key=%82@%BD%C4", + true + }, + { "HTTP/1.1 301 Moved\n" + "Location: http://foo/bar?key=\x83\x5C\x82\x5D\xCB\xD7\n", + "http://foo/bar?key=%83\\%82]%CB%D7", + true + }, +}; - EXPECT_EQ(tests[i].expected_len, parsed->GetContentLength()); - } +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + IsRedirectTest, + testing::ValuesIn(is_redirect_tests)); + +struct ContentLengthTestData { + const char* headers; + int64 expected_len; +}; + +class GetContentLengthTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<ContentLengthTestData> { +}; + +TEST_P(GetContentLengthTest, GetContentLength) { + const ContentLengthTestData test = GetParam(); + + std::string headers(test.headers); + HeadersToRaw(&headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(headers)); + + EXPECT_EQ(test.expected_len, parsed->GetContentLength()); } -TEST(HttpResponseHeaders, GetContentRange) { - const struct { - const char* headers; - bool expected_return_value; - int64 expected_first_byte_position; - int64 expected_last_byte_position; - int64 expected_instance_size; - } tests[] = { - { "HTTP/1.1 206 Partial Content", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range:", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: megabytes 0-10/50", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: 0-10/50", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: Bytes 0-50/51", - true, - 0, - 50, - 51 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0-50/51", - true, - 0, - 50, - 51 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes\t0-50/51", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0-50/51", - true, - 0, - 50, - 51 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0 - 50 \t / \t51", - true, - 0, - 50, - 51 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0\t-\t50\t/\t51\t", - true, - 0, - 50, - 51 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: \tbytes\t\t\t 0\t-\t50\t/\t51\t", - true, - 0, - 50, - 51 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: \t bytes \t 0 - 50 / 5 1", - false, - 0, - 50, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: \t bytes \t 0 - 5 0 / 51", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 50-0/51", - false, - 50, - 0, - -1 - }, - { "HTTP/1.1 416 Requested range not satisfiable\n" - "Content-Range: bytes * /*", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 416 Requested range not satisfiable\n" - "Content-Range: bytes * / * ", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0-50/*", - false, - 0, - 50, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0-50 / * ", - false, - 0, - 50, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0-10000000000/10000000001", - true, - 0, - 10000000000ll, - 10000000001ll - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0-10000000000/10000000000", - false, - 0, - 10000000000ll, - 10000000000ll - }, - // 64 bits wraparound. - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0 - 9223372036854775807 / 100", - false, - 0, - kint64max, - 100 - }, - // 64 bits wraparound. - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0 - 100 / -9223372036854775808", - false, - 0, - 100, - kint64min - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes */50", - false, - -1, - -1, - 50 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0-50/10", - false, - 0, - 50, - 10 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 40-50/45", - false, - 40, - 50, - 45 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0-50/-10", - false, - 0, - 50, - -10 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0-0/1", - true, - 0, - 0, - 1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0-40000000000000000000/40000000000000000001", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 1-/100", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes -/100", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes -1/100", - false, - -1, - -1, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 0-1233/*", - false, - 0, - 1233, - -1 - }, - { "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes -123 - -1/100", - false, - -1, - -1, - -1 - }, - }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers(tests[i].headers); - HeadersToRaw(&headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(headers)); +const ContentLengthTestData content_length_tests[] = { + { "HTTP/1.1 200 OK\n", + -1 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: 10\n", + 10 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: \n", + -1 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: abc\n", + -1 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: -10\n", + -1 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: +10\n", + -1 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: 23xb5\n", + -1 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: 0xA\n", + -1 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: 010\n", + 10 + }, + // Content-Length too big, will overflow an int64. + { "HTTP/1.1 200 OK\n" + "Content-Length: 40000000000000000000\n", + -1 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: 10\n", + 10 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: 10 \n", + 10 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: \t10\n", + 10 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: \v10\n", + -1 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: \f10\n", + -1 + }, + { "HTTP/1.1 200 OK\n" + "cOnTeNt-LENgth: 33\n", + 33 + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: 34\r\n", + -1 + }, +}; - int64 first_byte_position; - int64 last_byte_position; - int64 instance_size; - bool return_value = parsed->GetContentRange(&first_byte_position, - &last_byte_position, - &instance_size); - EXPECT_EQ(tests[i].expected_return_value, return_value); - EXPECT_EQ(tests[i].expected_first_byte_position, first_byte_position); - EXPECT_EQ(tests[i].expected_last_byte_position, last_byte_position); - EXPECT_EQ(tests[i].expected_instance_size, instance_size); - } +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + GetContentLengthTest, + testing::ValuesIn(content_length_tests)); + +struct ContentRangeTestData { + const char* headers; + bool expected_return_value; + int64 expected_first_byte_position; + int64 expected_last_byte_position; + int64 expected_instance_size; +}; + +class ContentRangeTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<ContentRangeTestData> { +}; + +TEST_P(ContentRangeTest, GetContentRange) { + const ContentRangeTestData test = GetParam(); + + std::string headers(test.headers); + HeadersToRaw(&headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(headers)); + + int64 first_byte_position; + int64 last_byte_position; + int64 instance_size; + bool return_value = parsed->GetContentRange(&first_byte_position, + &last_byte_position, + &instance_size); + EXPECT_EQ(test.expected_return_value, return_value); + EXPECT_EQ(test.expected_first_byte_position, first_byte_position); + EXPECT_EQ(test.expected_last_byte_position, last_byte_position); + EXPECT_EQ(test.expected_instance_size, instance_size); } -TEST(HttpResponseHeadersTest, IsKeepAlive) { - const struct { - const char* headers; - bool expected_keep_alive; - } tests[] = { - // The status line fabricated by HttpNetworkTransaction for a 0.9 response. - // Treated as 0.9. - { "HTTP/0.9 200 OK", - false - }, - // This could come from a broken server. Treated as 1.0 because it has a - // header. - { "HTTP/0.9 200 OK\n" - "connection: keep-alive\n", - true - }, - { "HTTP/1.1 200 OK\n", - true - }, - { "HTTP/1.0 200 OK\n", - false - }, - { "HTTP/1.0 200 OK\n" - "connection: close\n", - false - }, - { "HTTP/1.0 200 OK\n" - "connection: keep-alive\n", - true - }, - { "HTTP/1.0 200 OK\n" - "connection: kEeP-AliVe\n", - true - }, - { "HTTP/1.0 200 OK\n" - "connection: keep-aliveX\n", - false - }, - { "HTTP/1.1 200 OK\n" - "connection: close\n", - false - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n", - true - }, - { "HTTP/1.0 200 OK\n" - "proxy-connection: close\n", - false - }, - { "HTTP/1.0 200 OK\n" - "proxy-connection: keep-alive\n", - true - }, - { "HTTP/1.1 200 OK\n" - "proxy-connection: close\n", - false - }, - { "HTTP/1.1 200 OK\n" - "proxy-connection: keep-alive\n", - true - }, - }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers(tests[i].headers); - HeadersToRaw(&headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(headers)); +const ContentRangeTestData content_range_tests[] = { + { "HTTP/1.1 206 Partial Content", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range:", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: megabytes 0-10/50", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: 0-10/50", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: Bytes 0-50/51", + true, + 0, + 50, + 51 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0-50/51", + true, + 0, + 50, + 51 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes\t0-50/51", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0-50/51", + true, + 0, + 50, + 51 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0 - 50 \t / \t51", + true, + 0, + 50, + 51 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0\t-\t50\t/\t51\t", + true, + 0, + 50, + 51 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: \tbytes\t\t\t 0\t-\t50\t/\t51\t", + true, + 0, + 50, + 51 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: \t bytes \t 0 - 50 / 5 1", + false, + 0, + 50, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: \t bytes \t 0 - 5 0 / 51", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 50-0/51", + false, + 50, + 0, + -1 + }, + { "HTTP/1.1 416 Requested range not satisfiable\n" + "Content-Range: bytes * /*", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 416 Requested range not satisfiable\n" + "Content-Range: bytes * / * ", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0-50/*", + false, + 0, + 50, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0-50 / * ", + false, + 0, + 50, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0-10000000000/10000000001", + true, + 0, + 10000000000ll, + 10000000001ll + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0-10000000000/10000000000", + false, + 0, + 10000000000ll, + 10000000000ll + }, + // 64 bit wraparound. + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0 - 9223372036854775807 / 100", + false, + 0, + kint64max, + 100 + }, + // 64 bit wraparound. + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0 - 100 / -9223372036854775808", + false, + 0, + 100, + kint64min + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes */50", + false, + -1, + -1, + 50 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0-50/10", + false, + 0, + 50, + 10 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 40-50/45", + false, + 40, + 50, + 45 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0-50/-10", + false, + 0, + 50, + -10 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0-0/1", + true, + 0, + 0, + 1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0-40000000000000000000/40000000000000000001", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 1-/100", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes -/100", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes -1/100", + false, + -1, + -1, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 0-1233/*", + false, + 0, + 1233, + -1 + }, + { "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes -123 - -1/100", + false, + -1, + -1, + -1 + }, +}; - EXPECT_EQ(tests[i].expected_keep_alive, parsed->IsKeepAlive()); - } +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + ContentRangeTest, + testing::ValuesIn(content_range_tests)); + +struct KeepAliveTestData { + const char* headers; + bool expected_keep_alive; +}; + +class IsKeepAliveTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<KeepAliveTestData> { +}; + +TEST_P(IsKeepAliveTest, IsKeepAlive) { + const KeepAliveTestData test = GetParam(); + + std::string headers(test.headers); + HeadersToRaw(&headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(headers)); + + EXPECT_EQ(test.expected_keep_alive, parsed->IsKeepAlive()); } -TEST(HttpResponseHeadersTest, HasStrongValidators) { - const struct { - const char* headers; - bool expected_result; - } tests[] = { - { "HTTP/0.9 200 OK", - false - }, - { "HTTP/1.0 200 OK\n" - "Date: Wed, 28 Nov 2007 01:40:10 GMT\n" - "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n" - "ETag: \"foo\"\n", - false - }, - { "HTTP/1.1 200 OK\n" - "Date: Wed, 28 Nov 2007 01:40:10 GMT\n" - "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n" - "ETag: \"foo\"\n", - true - }, - { "HTTP/1.1 200 OK\n" - "Date: Wed, 28 Nov 2007 00:41:10 GMT\n" - "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n", - true - }, - { "HTTP/1.1 200 OK\n" - "Date: Wed, 28 Nov 2007 00:41:09 GMT\n" - "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n", - false - }, - { "HTTP/1.1 200 OK\n" - "ETag: \"foo\"\n", - true - }, - // This is not really a weak etag: - { "HTTP/1.1 200 OK\n" - "etag: \"w/foo\"\n", - true - }, - // This is a weak etag: - { "HTTP/1.1 200 OK\n" - "etag: w/\"foo\"\n", - false - }, - { "HTTP/1.1 200 OK\n" - "etag: W / \"foo\"\n", - false - } - }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers(tests[i].headers); - HeadersToRaw(&headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(headers)); +const KeepAliveTestData keepalive_tests[] = { + // The status line fabricated by HttpNetworkTransaction for a 0.9 response. + // Treated as 0.9. + { "HTTP/0.9 200 OK", + false + }, + // This could come from a broken server. Treated as 1.0 because it has a + // header. + { "HTTP/0.9 200 OK\n" + "connection: keep-alive\n", + true + }, + { "HTTP/1.1 200 OK\n", + true + }, + { "HTTP/1.0 200 OK\n", + false + }, + { "HTTP/1.0 200 OK\n" + "connection: close\n", + false + }, + { "HTTP/1.0 200 OK\n" + "connection: keep-alive\n", + true + }, + { "HTTP/1.0 200 OK\n" + "connection: kEeP-AliVe\n", + true + }, + { "HTTP/1.0 200 OK\n" + "connection: keep-aliveX\n", + false + }, + { "HTTP/1.1 200 OK\n" + "connection: close\n", + false + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n", + true + }, + { "HTTP/1.0 200 OK\n" + "proxy-connection: close\n", + false + }, + { "HTTP/1.0 200 OK\n" + "proxy-connection: keep-alive\n", + true + }, + { "HTTP/1.1 200 OK\n" + "proxy-connection: close\n", + false + }, + { "HTTP/1.1 200 OK\n" + "proxy-connection: keep-alive\n", + true + }, +}; - EXPECT_EQ(tests[i].expected_result, parsed->HasStrongValidators()) << - "Failed test case " << i; - } +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + IsKeepAliveTest, + testing::ValuesIn(keepalive_tests)); + +struct HasStrongValidatorsTestData { + const char* headers; + bool expected_result; +}; + +class HasStrongValidatorsTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<HasStrongValidatorsTestData> { +}; + +TEST_P(HasStrongValidatorsTest, HasStrongValidators) { + const HasStrongValidatorsTestData test = GetParam(); + + std::string headers(test.headers); + HeadersToRaw(&headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(headers)); + + EXPECT_EQ(test.expected_result, parsed->HasStrongValidators()); } +const HasStrongValidatorsTestData strong_validators_tests[] = { + { "HTTP/0.9 200 OK", + false + }, + { "HTTP/1.0 200 OK\n" + "Date: Wed, 28 Nov 2007 01:40:10 GMT\n" + "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n" + "ETag: \"foo\"\n", + false + }, + { "HTTP/1.1 200 OK\n" + "Date: Wed, 28 Nov 2007 01:40:10 GMT\n" + "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n" + "ETag: \"foo\"\n", + true + }, + { "HTTP/1.1 200 OK\n" + "Date: Wed, 28 Nov 2007 00:41:10 GMT\n" + "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n", + true + }, + { "HTTP/1.1 200 OK\n" + "Date: Wed, 28 Nov 2007 00:41:09 GMT\n" + "Last-Modified: Wed, 28 Nov 2007 00:40:10 GMT\n", + false + }, + { "HTTP/1.1 200 OK\n" + "ETag: \"foo\"\n", + true + }, + // This is not really a weak etag: + { "HTTP/1.1 200 OK\n" + "etag: \"w/foo\"\n", + true + }, + // This is a weak etag: + { "HTTP/1.1 200 OK\n" + "etag: w/\"foo\"\n", + false + }, + { "HTTP/1.1 200 OK\n" + "etag: W / \"foo\"\n", + false + } +}; + +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + HasStrongValidatorsTest, + testing::ValuesIn(strong_validators_tests)); + TEST(HttpResponseHeadersTest, GetStatusText) { std::string headers("HTTP/1.1 404 Not Found"); HeadersToRaw(&headers); @@ -1644,7 +1746,7 @@ TEST(HttpResponseHeadersTest, GetStatusTextMissing) { HeadersToRaw(&headers); scoped_refptr<net::HttpResponseHeaders> parsed( new net::HttpResponseHeaders(headers)); - // Since the status line gets normalized, we have OK + // Since the status line gets normalized, we have OK. EXPECT_EQ(std::string("OK"), parsed->GetStatusText()); } @@ -1666,297 +1768,353 @@ TEST(HttpResponseHeadersTest, GetStatusBadStatusLine) { EXPECT_EQ(std::string("OK"), parsed->GetStatusText()); } -TEST(HttpResponseHeadersTest, AddHeader) { - const struct { - const char* orig_headers; - const char* new_header; - const char* expected_headers; - } tests[] = { - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n", +struct AddHeaderTestData { + const char* orig_headers; + const char* new_header; + const char* expected_headers; +}; - "Content-Length: 450", +class AddHeaderTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<AddHeaderTestData> { +}; - "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n" - "Content-Length: 450\n" - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000 \n", +TEST_P(AddHeaderTest, AddHeader) { + const AddHeaderTestData test = GetParam(); - "Content-Length: 450 ", + std::string orig_headers(test.orig_headers); + HeadersToRaw(&orig_headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(orig_headers)); - "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n" - "Content-Length: 450\n" - }, - }; - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string orig_headers(tests[i].orig_headers); - HeadersToRaw(&orig_headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(orig_headers)); + std::string new_header(test.new_header); + parsed->AddHeader(new_header); - std::string new_header(tests[i].new_header); - parsed->AddHeader(new_header); + std::string resulting_headers; + parsed->GetNormalizedHeaders(&resulting_headers); + EXPECT_EQ(std::string(test.expected_headers), resulting_headers); +} - std::string resulting_headers; - parsed->GetNormalizedHeaders(&resulting_headers); - EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers); - } +const AddHeaderTestData add_header_tests[] = { + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n", + + "Content-Length: 450", + + "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n" + "Content-Length: 450\n" + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000 \n", + + "Content-Length: 450 ", + + "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n" + "Content-Length: 450\n" + }, +}; + +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + AddHeaderTest, + testing::ValuesIn(add_header_tests)); + +struct RemoveHeaderTestData { + const char* orig_headers; + const char* to_remove; + const char* expected_headers; +}; + +class RemoveHeaderTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<RemoveHeaderTestData> { +}; + +TEST_P(RemoveHeaderTest, RemoveHeader) { + const RemoveHeaderTestData test = GetParam(); + + std::string orig_headers(test.orig_headers); + HeadersToRaw(&orig_headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(orig_headers)); + + std::string name(test.to_remove); + parsed->RemoveHeader(name); + + std::string resulting_headers; + parsed->GetNormalizedHeaders(&resulting_headers); + EXPECT_EQ(std::string(test.expected_headers), resulting_headers); } -TEST(HttpResponseHeadersTest, RemoveHeader) { - const struct { - const char* orig_headers; - const char* to_remove; - const char* expected_headers; - } tests[] = { - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n" - "Content-Length: 450\n", +const RemoveHeaderTestData remove_header_tests[] = { + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n" + "Content-Length: 450\n", - "Content-Length", + "Content-Length", - "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n" - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive \n" - "Content-Length : 450 \n" - "Cache-control: max-age=10000\n", + "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n" + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive \n" + "Content-Length : 450 \n" + "Cache-control: max-age=10000\n", - "Content-Length", + "Content-Length", - "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n" - }, - }; - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string orig_headers(tests[i].orig_headers); - HeadersToRaw(&orig_headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(orig_headers)); + "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n" + }, +}; - std::string name(tests[i].to_remove); - parsed->RemoveHeader(name); +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + RemoveHeaderTest, + testing::ValuesIn(remove_header_tests)); - std::string resulting_headers; - parsed->GetNormalizedHeaders(&resulting_headers); - EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers); - } +struct RemoveIndividualHeaderTestData { + const char* orig_headers; + const char* to_remove_name; + const char* to_remove_value; + const char* expected_headers; +}; + +class RemoveIndividualHeaderTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<RemoveIndividualHeaderTestData> { +}; + +TEST_P(RemoveIndividualHeaderTest, RemoveIndividualHeader) { + const RemoveIndividualHeaderTestData test = GetParam(); + + std::string orig_headers(test.orig_headers); + HeadersToRaw(&orig_headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(orig_headers)); + + std::string name(test.to_remove_name); + std::string value(test.to_remove_value); + parsed->RemoveHeaderLine(name, value); + + std::string resulting_headers; + parsed->GetNormalizedHeaders(&resulting_headers); + EXPECT_EQ(std::string(test.expected_headers), resulting_headers); } -TEST(HttpResponseHeadersTest, RemoveIndividualHeader) { - const struct { - const char* orig_headers; - const char* to_remove_name; - const char* to_remove_value; - const char* expected_headers; - } tests[] = { - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n" - "Content-Length: 450\n", +const RemoveIndividualHeaderTestData remove_individual_header_tests[] = { + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n" + "Content-Length: 450\n", - "Content-Length", + "Content-Length", - "450", + "450", - "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n" - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive \n" - "Content-Length : 450 \n" - "Cache-control: max-age=10000\n", + "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n" + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive \n" + "Content-Length : 450 \n" + "Cache-control: max-age=10000\n", - "Content-Length", + "Content-Length", - "450", + "450", - "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n" - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive \n" - "Content-Length: 450\n" - "Cache-control: max-age=10000\n", + "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n" + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive \n" + "Content-Length: 450\n" + "Cache-control: max-age=10000\n", - "Content-Length", // Matching name. + "Content-Length", // Matching name. - "999", // Mismatching value. + "999", // Mismatching value. - "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Content-Length: 450\n" - "Cache-control: max-age=10000\n" - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive \n" - "Foo: bar, baz\n" - "Foo: bar\n" - "Cache-control: max-age=10000\n", + "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Content-Length: 450\n" + "Cache-control: max-age=10000\n" + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive \n" + "Foo: bar, baz\n" + "Foo: bar\n" + "Cache-control: max-age=10000\n", - "Foo", + "Foo", - "bar, baz", // Space in value. + "bar, baz", // Space in value. - "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Foo: bar\n" - "Cache-control: max-age=10000\n" - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive \n" - "Foo: bar, baz\n" - "Cache-control: max-age=10000\n", + "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Foo: bar\n" + "Cache-control: max-age=10000\n" + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive \n" + "Foo: bar, baz\n" + "Cache-control: max-age=10000\n", - "Foo", + "Foo", - "baz", // Only partial match -> ignored. + "baz", // Only partial match -> ignored. - "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Foo: bar, baz\n" - "Cache-control: max-age=10000\n" - }, - }; - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string orig_headers(tests[i].orig_headers); - HeadersToRaw(&orig_headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(orig_headers)); + "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Foo: bar, baz\n" + "Cache-control: max-age=10000\n" + }, +}; - std::string name(tests[i].to_remove_name); - std::string value(tests[i].to_remove_value); - parsed->RemoveHeaderLine(name, value); +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + RemoveIndividualHeaderTest, + testing::ValuesIn(remove_individual_header_tests)); - std::string resulting_headers; - parsed->GetNormalizedHeaders(&resulting_headers); - EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers); - } -} +struct ReplaceStatusTestData { + const char* orig_headers; + const char* new_status; + const char* expected_headers; +}; -TEST(HttpResponseHeadersTest, ReplaceStatus) { - const struct { - const char* orig_headers; - const char* new_status; - const char* expected_headers; - } tests[] = { - { "HTTP/1.1 206 Partial Content\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n" - "Content-Length: 450\n", +class ReplaceStatusTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<ReplaceStatusTestData> { +}; - "HTTP/1.1 200 OK", +TEST_P(ReplaceStatusTest, ReplaceStatus) { + const ReplaceStatusTestData test = GetParam(); - "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Cache-control: max-age=10000\n" - "Content-Length: 450\n" - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n", - - "HTTP/1.1 304 Not Modified", - - "HTTP/1.1 304 Not Modified\n" - "connection: keep-alive\n" - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive \n" - "Content-Length : 450 \n" - "Cache-control: max-age=10000\n", - - "HTTP/1//1 304 Not Modified", - - "HTTP/1.0 304 Not Modified\n" - "connection: keep-alive\n" - "Content-Length: 450\n" - "Cache-control: max-age=10000\n" - }, - }; - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string orig_headers(tests[i].orig_headers); - HeadersToRaw(&orig_headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(orig_headers)); + std::string orig_headers(test.orig_headers); + HeadersToRaw(&orig_headers); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(orig_headers)); - std::string name(tests[i].new_status); - parsed->ReplaceStatusLine(name); + std::string name(test.new_status); + parsed->ReplaceStatusLine(name); - std::string resulting_headers; - parsed->GetNormalizedHeaders(&resulting_headers); - EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers); - } + std::string resulting_headers; + parsed->GetNormalizedHeaders(&resulting_headers); + EXPECT_EQ(std::string(test.expected_headers), resulting_headers); } -TEST(HttpResponseHeadersTest, UpdateWithNewRange) { - const struct { - const char* orig_headers; - const char* expected_headers; - const char* expected_headers_with_replaced_status; - } tests[] = { - { "HTTP/1.1 200 OK\n" - "Content-Length: 450\n", +const ReplaceStatusTestData replace_status_tests[] = { + { "HTTP/1.1 206 Partial Content\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n" + "Content-Length: 450\n", - "HTTP/1.1 200 OK\n" - "Content-Range: bytes 3-5/450\n" - "Content-Length: 3\n", + "HTTP/1.1 200 OK", - "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 3-5/450\n" - "Content-Length: 3\n", - }, - { "HTTP/1.1 200 OK\n" - "Content-Length: 5\n", + "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Cache-control: max-age=10000\n" + "Content-Length: 450\n" + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n", + + "HTTP/1.1 304 Not Modified", + + "HTTP/1.1 304 Not Modified\n" + "connection: keep-alive\n" + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive \n" + "Content-Length : 450 \n" + "Cache-control: max-age=10000\n", + + "HTTP/1//1 304 Not Modified", + + "HTTP/1.0 304 Not Modified\n" + "connection: keep-alive\n" + "Content-Length: 450\n" + "Cache-control: max-age=10000\n" + }, +}; + +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + ReplaceStatusTest, + testing::ValuesIn(replace_status_tests)); + +struct UpdateWithNewRangeTestData { + const char* orig_headers; + const char* expected_headers; + const char* expected_headers_with_replaced_status; +}; + +class UpdateWithNewRangeTest + : public HttpResponseHeadersTest, + public ::testing::WithParamInterface<UpdateWithNewRangeTestData> { +}; + +TEST_P(UpdateWithNewRangeTest, UpdateWithNewRange) { + const UpdateWithNewRangeTestData test = GetParam(); - "HTTP/1.1 200 OK\n" - "Content-Range: bytes 3-5/5\n" - "Content-Length: 3\n", - - "HTTP/1.1 206 Partial Content\n" - "Content-Range: bytes 3-5/5\n" - "Content-Length: 3\n", - }, - }; const net::HttpByteRange range = net::HttpByteRange::Bounded(3, 5); - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string orig_headers(tests[i].orig_headers); - std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0'); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(orig_headers + '\0')); - int64 content_size = parsed->GetContentLength(); - std::string resulting_headers; - - // Update headers without replacing status line. - parsed->UpdateWithNewRange(range, content_size, false); - parsed->GetNormalizedHeaders(&resulting_headers); - EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers); - - // Replace status line too. - parsed->UpdateWithNewRange(range, content_size, true); - parsed->GetNormalizedHeaders(&resulting_headers); - EXPECT_EQ(std::string(tests[i].expected_headers_with_replaced_status), - resulting_headers); - } + std::string orig_headers(test.orig_headers); + std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0'); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(orig_headers + '\0')); + int64 content_size = parsed->GetContentLength(); + std::string resulting_headers; + + // Update headers without replacing status line. + parsed->UpdateWithNewRange(range, content_size, false); + parsed->GetNormalizedHeaders(&resulting_headers); + EXPECT_EQ(std::string(test.expected_headers), resulting_headers); + + // Replace status line too. + parsed->UpdateWithNewRange(range, content_size, true); + parsed->GetNormalizedHeaders(&resulting_headers); + EXPECT_EQ(std::string(test.expected_headers_with_replaced_status), + resulting_headers); } +const UpdateWithNewRangeTestData update_range_tests[] = { + { "HTTP/1.1 200 OK\n" + "Content-Length: 450\n", + + "HTTP/1.1 200 OK\n" + "Content-Range: bytes 3-5/450\n" + "Content-Length: 3\n", + + "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 3-5/450\n" + "Content-Length: 3\n", + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: 5\n", + + "HTTP/1.1 200 OK\n" + "Content-Range: bytes 3-5/5\n" + "Content-Length: 3\n", + + "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 3-5/5\n" + "Content-Length: 3\n", + }, +}; + +INSTANTIATE_TEST_CASE_P(HttpResponseHeaders, + UpdateWithNewRangeTest, + testing::ValuesIn(update_range_tests)); + TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) { std::string headers("HTTP/1.1 404\n" "Content-Length: 450\n" @@ -2011,8 +2169,8 @@ TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeFirstMatchUsed) { } TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeBogusFirstMatchUsed) { - // max-age10 isn't parsed as max-age; max-age=now is parsed as max-age=0 and - // so max-age=20 is not used. + // "max-age10" isn't parsed as "max-age"; "max-age=now" is parsed as + // "max-age=0" and so "max-age=20" is not used. InitializeHeadersWithCacheControl("max-age10, max-age=now, max-age=20"); EXPECT_EQ(TimeDelta::FromSeconds(0), GetMaxAgeValue()); } @@ -2022,40 +2180,49 @@ TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeCaseInsensitive) { EXPECT_EQ(TimeDelta::FromSeconds(15), GetMaxAgeValue()); } -TEST_F(HttpResponseHeadersCacheControlTest, MaxAgeEdgeCases) { - // This test doesn't use TEST_P() for consistency with the rest of the tests - // in this file. - // TODO(ricea): Port the tests in this file to use TEST_P(). - const struct { - const char* max_age_string; - int64 expected_seconds; - } tests[] = { - {" 1 ", 1}, // Spaces are ignored - {"-1", -1}, // Negative numbers are passed through - {"--1", 0}, // Leading junk gives 0 - {"2s", 2}, // trailing junk is ignored - {"3 days", 3}, - {"'4'", 0}, // single quotes don't work - {"\"5\"", 0}, // double quotes don't work - {"0x6", 0}, // hex not parsed as hex - {"7F", 7}, // hex without 0x still not parsed as hex - {"010", 10}, // octal not parsed as octal - {"9223372036854", 9223372036854}, - // {"9223372036855", -9223372036854}, // undefined behaviour - // {"9223372036854775806", -2}, // undefined behaviour - {"9223372036854775807", 9223372036854775807}, - {"20000000000000000000", - std::numeric_limits<int64>::max()}, // overflow int64 - }; +struct MaxAgeTestData { + const char* max_age_string; + const int64 expected_seconds; +}; + +class MaxAgeEdgeCasesTest + : public HttpResponseHeadersCacheControlTest, + public ::testing::WithParamInterface<MaxAgeTestData> { +}; + +TEST_P(MaxAgeEdgeCasesTest, MaxAgeEdgeCases) { + const MaxAgeTestData test = GetParam(); + std::string max_age = "max-age="; - for (size_t i = 0; i < arraysize(tests); ++i) { - InitializeHeadersWithCacheControl( - (max_age + tests[i].max_age_string).c_str()); - EXPECT_EQ(tests[i].expected_seconds, GetMaxAgeValue().InSeconds()) - << " for max-age=" << tests[i].max_age_string; - } + InitializeHeadersWithCacheControl( + (max_age + test.max_age_string).c_str()); + EXPECT_EQ(test.expected_seconds, GetMaxAgeValue().InSeconds()) + << " for max-age=" << test.max_age_string; } +const MaxAgeTestData max_age_tests[] = { + {" 1 ", 1}, // Spaces are ignored. + {"-1", -1}, // Negative numbers are passed through. + {"--1", 0}, // Leading junk gives 0. + {"2s", 2}, // Trailing junk is ignored. + {"3 days", 3}, + {"'4'", 0}, // Single quotes don't work. + {"\"5\"", 0}, // Double quotes don't work. + {"0x6", 0}, // Hex not parsed as hex. + {"7F", 7}, // Hex without 0x still not parsed as hex. + {"010", 10}, // Octal not parsed as octal. + {"9223372036854", 9223372036854}, + // {"9223372036855", -9223372036854}, // Undefined behaviour. + // {"9223372036854775806", -2}, // Undefined behaviour. + {"9223372036854775807", 9223372036854775807}, + {"20000000000000000000", + std::numeric_limits<int64>::max()}, // Overflow int64. +}; + +INSTANTIATE_TEST_CASE_P(HttpResponseHeadersCacheControl, + MaxAgeEdgeCasesTest, + testing::ValuesIn(max_age_tests)); + TEST_F(HttpResponseHeadersCacheControlTest, AbsentStaleWhileRevalidateReturnsFalse) { InitializeHeadersWithCacheControl("max-age=3600"); @@ -2085,3 +2252,5 @@ TEST_F(HttpResponseHeadersCacheControlTest, "stale-while-revalidate=1,stale-while-revalidate=7200"); EXPECT_EQ(TimeDelta::FromSeconds(1), GetStaleWhileRevalidateValue()); } + +} // end namespace |