| Index: net/http/http_cache_unittest.cc
|
| diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
|
| index 46dad2760b4e4160c69bc4b86acb0965f69f06e3..a7a04f14d9b54c4e3adc4ea34a389141dc5497d3 100644
|
| --- a/net/http/http_cache_unittest.cc
|
| +++ b/net/http/http_cache_unittest.cc
|
| @@ -10,6 +10,7 @@
|
| #include "base/bind_helpers.h"
|
| #include "base/memory/scoped_vector.h"
|
| #include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| #include "base/strings/string_util.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "net/base/cache_type.h"
|
| @@ -6757,6 +6758,52 @@ TEST(HttpCache, ReceivedBytesRange) {
|
| RemoveMockTransaction(&kRangeGET_TransactionOK);
|
| }
|
|
|
| +// Framework for tests of stale-while-revalidate related functionality. With
|
| +// the default settings (age=3601,stale-while-revalidate=7200,max-age=3600) it
|
| +// will trigger the stale-while-revalidate asynchronous revalidation. Setting
|
| +// |age_| to < 3600 will prevent any revalidation, and |age_| > 10800 will cause
|
| +// synchronous revalidation.
|
| +class HttpCacheStaleWhileRevalidateTest : public ::testing::Test {
|
| + protected:
|
| + HttpCacheStaleWhileRevalidateTest()
|
| + : transaction_(kSimpleGET_Transaction),
|
| + age_(3601),
|
| + stale_while_revalidate_(7200),
|
| + validator_("Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT") {
|
| + cache_.http_cache()->set_use_stale_while_revalidate_for_testing(true);
|
| + }
|
| +
|
| + // RunTransactionTest() with the arguments from this fixture.
|
| + void RunFixtureTransactionTest() {
|
| + std::string response_headers = base::StringPrintf(
|
| + "%s\n"
|
| + "Age: %d\n"
|
| + "Cache-Control: max-age=3600,stale-while-revalidate=%d\n",
|
| + validator_.c_str(),
|
| + age_,
|
| + stale_while_revalidate_);
|
| + transaction_.response_headers = response_headers.c_str();
|
| + RunTransactionTest(cache_.http_cache(), transaction_);
|
| + transaction_.response_headers = "";
|
| + }
|
| +
|
| + // How many times this test has sent requests to the (fake) origin
|
| + // server. Every test case needs to make at least one request to initialise
|
| + // the cache.
|
| + int transaction_count() {
|
| + return cache_.network_layer()->transaction_count();
|
| + }
|
| +
|
| + // How many times an existing cache entry was opened during the test case.
|
| + int open_count() { return cache_.disk_cache()->open_count(); }
|
| +
|
| + MockHttpCache cache_;
|
| + ScopedMockTransaction transaction_;
|
| + int age_;
|
| + int stale_while_revalidate_;
|
| + std::string validator_;
|
| +};
|
| +
|
| static void CheckResourceFreshnessHeader(const net::HttpRequestInfo* request,
|
| std::string* response_status,
|
| std::string* response_headers,
|
| @@ -6768,27 +6815,20 @@ static void CheckResourceFreshnessHeader(const net::HttpRequestInfo* request,
|
|
|
| // Verify that the Resource-Freshness header is sent on a revalidation if the
|
| // stale-while-revalidate directive was on the response.
|
| -TEST(HttpCache, ResourceFreshnessHeaderSent) {
|
| - MockHttpCache cache;
|
| -
|
| - ScopedMockTransaction stale_while_revalidate_transaction(
|
| - kSimpleGET_Transaction);
|
| - stale_while_revalidate_transaction.response_headers =
|
| - "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
|
| - "Age: 10801\n"
|
| - "Cache-Control: max-age=3600,stale-while-revalidate=7200\n";
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, ResourceFreshnessHeaderSent) {
|
| + age_ = 10801; // Outside the stale-while-revalidate window.
|
|
|
| // Write to the cache.
|
| - RunTransactionTest(cache.http_cache(), stale_while_revalidate_transaction);
|
| + RunFixtureTransactionTest();
|
|
|
| - EXPECT_EQ(1, cache.network_layer()->transaction_count());
|
| + EXPECT_EQ(1, transaction_count());
|
|
|
| // Send the request again and check that Resource-Freshness header is added.
|
| - stale_while_revalidate_transaction.handler = CheckResourceFreshnessHeader;
|
| + transaction_.handler = CheckResourceFreshnessHeader;
|
|
|
| - RunTransactionTest(cache.http_cache(), stale_while_revalidate_transaction);
|
| + RunFixtureTransactionTest();
|
|
|
| - EXPECT_EQ(2, cache.network_layer()->transaction_count());
|
| + EXPECT_EQ(2, transaction_count());
|
| }
|
|
|
| static void CheckResourceFreshnessAbsent(const net::HttpRequestInfo* request,
|
| @@ -6800,27 +6840,423 @@ static void CheckResourceFreshnessAbsent(const net::HttpRequestInfo* request,
|
|
|
| // Verify that the Resource-Freshness header is not sent when
|
| // stale-while-revalidate is 0.
|
| -TEST(HttpCache, ResourceFreshnessHeaderNotSent) {
|
| - MockHttpCache cache;
|
| -
|
| - ScopedMockTransaction stale_while_revalidate_transaction(
|
| - kSimpleGET_Transaction);
|
| - stale_while_revalidate_transaction.response_headers =
|
| - "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
|
| - "Age: 10801\n"
|
| - "Cache-Control: max-age=3600,stale-while-revalidate=0\n";
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, ResourceFreshnessHeaderNotSent) {
|
| + age_ = 10801;
|
| + stale_while_revalidate_ = 0;
|
|
|
| // Write to the cache.
|
| - RunTransactionTest(cache.http_cache(), stale_while_revalidate_transaction);
|
| + RunFixtureTransactionTest();
|
|
|
| - EXPECT_EQ(1, cache.network_layer()->transaction_count());
|
| + EXPECT_EQ(1, transaction_count());
|
|
|
| // Send the request again and check that Resource-Freshness header is absent.
|
| - stale_while_revalidate_transaction.handler = CheckResourceFreshnessAbsent;
|
| + transaction_.handler = CheckResourceFreshnessAbsent;
|
|
|
| - RunTransactionTest(cache.http_cache(), stale_while_revalidate_transaction);
|
| + RunFixtureTransactionTest();
|
|
|
| - EXPECT_EQ(2, cache.network_layer()->transaction_count());
|
| + EXPECT_EQ(2, transaction_count());
|
| +}
|
| +
|
| +// Verify that when stale-while-revalidate applies the response is read from
|
| +// cache.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, ReadFromCache) {
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(0, open_count());
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Read back from the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, open_count());
|
| + EXPECT_EQ(1, transaction_count());
|
| +}
|
| +
|
| +// Verify that when stale-while-revalidate applies an asynchronous request is
|
| +// sent.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncRequestSent) {
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Read back from the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Let the async request execute.
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_EQ(2, transaction_count());
|
| +}
|
| +
|
| +// Verify that tearing down the HttpCache with an async revalidation in progress
|
| +// does not break anything (this test is most likely to find problems when run
|
| +// with a memory checker such as AddressSanitizer).
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncTearDown) {
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + // Read back from the cache.
|
| + RunFixtureTransactionTest();
|
| +}
|
| +
|
| +static void CheckIfModifiedSinceHeader(const net::HttpRequestInfo* request,
|
| + std::string* response_status,
|
| + std::string* response_headers,
|
| + std::string* response_data) {
|
| + std::string value;
|
| + EXPECT_TRUE(request->extra_headers.GetHeader("If-Modified-Since", &value));
|
| + EXPECT_EQ("Sat, 18 Apr 2007 01:10:43 GMT", value);
|
| +}
|
| +
|
| +// Verify that the async revalidation contains an If-Modified-Since header.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncRequestIfModifiedSince) {
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + transaction_.handler = CheckIfModifiedSinceHeader;
|
| +
|
| + // Read back from the cache.
|
| + RunFixtureTransactionTest();
|
| +}
|
| +
|
| +static void CheckIfNoneMatchHeader(const net::HttpRequestInfo* request,
|
| + std::string* response_status,
|
| + std::string* response_headers,
|
| + std::string* response_data) {
|
| + std::string value;
|
| + EXPECT_TRUE(request->extra_headers.GetHeader("If-None-Match", &value));
|
| + EXPECT_EQ("\"40a1-1320-4f6adefa22a40\"", value);
|
| +}
|
| +
|
| +// If the response had ETag rather than Last-Modified, then that is used to
|
| +// conditionalise the response.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncRequestIfNoneMatch) {
|
| + validator_ = "Etag: \"40a1-1320-4f6adefa22a40\"";
|
| +
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + transaction_.handler = CheckIfNoneMatchHeader;
|
| +
|
| + // Read back from the cache.
|
| + RunFixtureTransactionTest();
|
| +}
|
| +
|
| +static void CheckResourceFreshnessHeaderPresent(
|
| + const net::HttpRequestInfo* request,
|
| + std::string* response_status,
|
| + std::string* response_headers,
|
| + std::string* response_data) {
|
| + EXPECT_TRUE(request->extra_headers.HasHeader("Resource-Freshness"));
|
| +}
|
| +
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncRequestHasResourceFreshness) {
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + transaction_.handler = CheckResourceFreshnessHeaderPresent;
|
| +
|
| + // Read back from the cache.
|
| + RunFixtureTransactionTest();
|
| +}
|
| +
|
| +// Verify that when age > max-age + stale-while-revalidate stale results are
|
| +// not returned.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, NotAppliedIfTooStale) {
|
| + age_ = 10801;
|
| +
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(0, open_count());
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Reading back reads from the network.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, open_count());
|
| + EXPECT_EQ(2, transaction_count());
|
| +}
|
| +
|
| +// HEAD requests should be able to take advantage of stale-while-revalidate.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, WorksForHeadMethod) {
|
| + // Write to the cache. This has to be a GET request; HEAD requests don't
|
| + // create new cache entries.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(0, open_count());
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Read back from the cache, and trigger an asynchronous HEAD request.
|
| + transaction_.method = "HEAD";
|
| + transaction_.data = "";
|
| +
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, open_count());
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Let the network request proceed.
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + EXPECT_EQ(2, transaction_count());
|
| +}
|
| +
|
| +// POST requests should not use stale-while-revalidate.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, NotAppliedToPost) {
|
| + transaction_ = ScopedMockTransaction(kSimplePOST_Transaction);
|
| +
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(0, open_count());
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Reading back reads from the network.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(0, open_count());
|
| + EXPECT_EQ(2, transaction_count());
|
| +}
|
| +
|
| +static void CheckUrlMatches(const net::HttpRequestInfo* request,
|
| + std::string* response_status,
|
| + std::string* response_headers,
|
| + std::string* response_data) {
|
| + EXPECT_EQ("http://www.google.com/", request->url.spec());
|
| +}
|
| +
|
| +// Async revalidation is issued to the original URL.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncRequestUrlMatches) {
|
| + transaction_.url = "http://www.google.com/";
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + // Read back from the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + transaction_.handler = CheckUrlMatches;
|
| +
|
| + // Let the async request execute and perform the check.
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_EQ(2, transaction_count());
|
| +}
|
| +
|
| +class SyncLoadFlagTest : public HttpCacheStaleWhileRevalidateTest,
|
| + public ::testing::WithParamInterface<int> {};
|
| +
|
| +// Flags which should always cause the request to be synchronous.
|
| +TEST_P(SyncLoadFlagTest, MustBeSynchronous) {
|
| + transaction_.load_flags |= GetParam();
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Reading back reads from the network.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(2, transaction_count());
|
| +}
|
| +
|
| +INSTANTIATE_TEST_CASE_P(HttpCacheStaleWhileRevalidate,
|
| + SyncLoadFlagTest,
|
| + ::testing::Values(net::LOAD_VALIDATE_CACHE,
|
| + net::LOAD_BYPASS_CACHE,
|
| + net::LOAD_DISABLE_CACHE));
|
| +
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest,
|
| + PreferringCacheDoesNotTriggerAsyncRequest) {
|
| + transaction_.load_flags |= net::LOAD_PREFERRING_CACHE;
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Reading back reads from the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // If there was an async transaction created, it would run now.
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + // There was no async transaction.
|
| + EXPECT_EQ(1, transaction_count());
|
| +}
|
| +
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, NotUsedWhenDisabled) {
|
| + cache_.http_cache()->set_use_stale_while_revalidate_for_testing(false);
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // A synchronous revalidation is performed.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(2, transaction_count());
|
| +}
|
| +
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest,
|
| + OnlyFromCacheDoesNotTriggerAsyncRequest) {
|
| + transaction_.load_flags |= net::LOAD_ONLY_FROM_CACHE;
|
| + transaction_.return_code = net::ERR_CACHE_MISS;
|
| +
|
| + // Writing to the cache should fail, because we are avoiding the network.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(0, transaction_count());
|
| +
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + // Still nothing.
|
| + EXPECT_EQ(0, transaction_count());
|
| +}
|
| +
|
| +// A certificate error during an asynchronous fetch should cause the next fetch
|
| +// to proceed synchronously.
|
| +// TODO(ricea): In future, only certificate errors which require user
|
| +// interaction should fail the asynchronous revalidation, and they should cause
|
| +// the next revalidation to be synchronous rather than requiring a total
|
| +// refetch. This test will need to be updated appropriately.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, CertificateErrorCausesRefetch) {
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Now read back. RunTransactionTestBase() expects to receive the network
|
| + // error back from the HttpCache::Transaction, but since the cache request
|
| + // will return OK we need to duplicate some of its implementation here.
|
| + transaction_.return_code = net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
|
| + net::TestCompletionCallback callback;
|
| + scoped_ptr<net::HttpTransaction> trans;
|
| + int rv =
|
| + cache_.http_cache()->CreateTransaction(net::DEFAULT_PRIORITY, &trans);
|
| + EXPECT_EQ(net::OK, rv);
|
| + ASSERT_TRUE(trans.get());
|
| +
|
| + MockHttpRequest request(transaction_);
|
| + rv = trans->Start(&request, callback.callback(), net::BoundNetLog());
|
| + ASSERT_EQ(net::ERR_IO_PENDING, rv);
|
| + ASSERT_EQ(net::OK, callback.WaitForResult());
|
| + ReadAndVerifyTransaction(trans.get(), transaction_);
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Allow the asynchronous fetch to run.
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + EXPECT_EQ(2, transaction_count());
|
| +
|
| + // Now run the transaction again. It should run synchronously.
|
| + transaction_.return_code = net::OK;
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(3, transaction_count());
|
| +}
|
| +
|
| +// Ensure that the response cached by the asynchronous request is not truncated,
|
| +// even if the server is slow.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, EntireResponseCached) {
|
| + transaction_.test_mode = TEST_MODE_SLOW_READ;
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + // Read back from the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + // Let the async request execute.
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + // The cache entry should still be complete.
|
| + transaction_.load_flags = net::LOAD_ONLY_FROM_CACHE;
|
| + RunFixtureTransactionTest();
|
| +}
|
| +
|
| +// Verify that there are no race conditions in the completely synchronous case.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, SynchronousCaseWorks) {
|
| + transaction_.test_mode = TEST_MODE_SYNC_ALL;
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Read back from the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Let the async request execute.
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_EQ(2, transaction_count());
|
| +}
|
| +
|
| +static void CheckLoadFlagsAsyncRevalidation(const net::HttpRequestInfo* request,
|
| + std::string* response_status,
|
| + std::string* response_headers,
|
| + std::string* response_data) {
|
| + EXPECT_EQ(net::LOAD_ASYNC_REVALIDATION, request->load_flags);
|
| +}
|
| +
|
| +// Check that the load flags on the async request are the same as the load flags
|
| +// on the original request, plus LOAD_ASYNC_REVALIDATION.
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, LoadFlagsAsyncRevalidation) {
|
| + transaction_.load_flags = net::LOAD_NORMAL;
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Read back from the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + transaction_.handler = CheckLoadFlagsAsyncRevalidation;
|
| + // Let the async request execute.
|
| + base::RunLoop().RunUntilIdle();
|
| + EXPECT_EQ(2, transaction_count());
|
| +}
|
| +
|
| +static void SimpleMockAuthHandler(const net::HttpRequestInfo* request,
|
| + std::string* response_status,
|
| + std::string* response_headers,
|
| + std::string* response_data) {
|
| + if (request->extra_headers.HasHeader("X-Require-Mock-Auth") &&
|
| + !request->extra_headers.HasHeader("Authorization")) {
|
| + response_status->assign("HTTP/1.1 401 Unauthorized");
|
| + response_headers->assign("WWW-Authenticate: Basic realm=\"mars\"\n");
|
| + return;
|
| + }
|
| + response_status->assign("HTTP/1.1 200 OK");
|
| +}
|
| +
|
| +TEST_F(HttpCacheStaleWhileRevalidateTest, RestartForAuth) {
|
| + // Write to the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Now make the transaction require auth.
|
| + transaction_.request_headers = "X-Require-Mock-Auth: dummy\r\n\r\n";
|
| + transaction_.handler = SimpleMockAuthHandler;
|
| +
|
| + // Read back from the cache.
|
| + RunFixtureTransactionTest();
|
| +
|
| + EXPECT_EQ(1, transaction_count());
|
| +
|
| + // Let the async request execute.
|
| + base::RunLoop().RunUntilIdle();
|
| +
|
| + EXPECT_EQ(2, transaction_count());
|
| }
|
|
|
| // Tests that we allow multiple simultaneous, non-overlapping transactions to
|
|
|