Chromium Code Reviews| Index: net/http/http_cache_unittest.cc |
| diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc |
| index 4987c37b025a6aa89f8279586e4b811ba6ad9c30..90679238a39892b714c0cff0b5833d2a9905caa3 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" |
| @@ -38,6 +39,8 @@ |
| #include "testing/gtest/include/gtest/gtest.h" |
| using base::Time; |
| +using base::RunLoop; |
|
rvargas (doing something else)
2014/09/17 04:32:19
nit: we should probably avoid this and be explicit
Adam Rice
2014/09/30 13:44:50
Done. I originally cleaned up every call to RunUnt
|
| +using base::MessageLoop; |
|
rvargas (doing something else)
2014/09/17 04:32:19
not used
Adam Rice
2014/09/30 13:44:50
Removed.
|
| namespace { |
| @@ -6725,6 +6728,43 @@ TEST(HttpCache, ReceivedBytesRange) { |
| RemoveMockTransaction(&kRangeGET_TransactionOK); |
| } |
| +class HttpCacheStaleWhileRevalidateTest : public ::testing::Test { |
|
rvargas (doing something else)
2014/09/17 04:32:19
Add a comment about how to use this class (what ar
Adam Rice
2014/09/30 13:44:50
Done.
|
| + 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 fixure. |
| + 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 = ""; |
| + } |
| + |
| + int transaction_count() { |
| + return cache_.network_layer()->transaction_count(); |
| + } |
| + |
| + 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, |
| @@ -6736,27 +6776,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, |
| @@ -6768,25 +6801,416 @@ 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()); |
| + |
| + // Let the async request complete. |
| + RunLoop().RunUntilIdle(); |
|
rvargas (doing something else)
2014/09/17 04:32:19
What happens if this line is not here? (being the
Adam Rice
2014/09/30 13:44:50
I was concerned about the callbacks interfering wi
|
| +} |
| + |
| +// 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. |
| + 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(); |
| + |
| + RunLoop().RunUntilIdle(); |
| +} |
| + |
| +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(); |
| + |
| + RunLoop().RunUntilIdle(); |
| +} |
| + |
| +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(); |
| + |
| + RunLoop().RunUntilIdle(); |
| +} |
| + |
| +// 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 (as of August 2014). |
|
rvargas (doing something else)
2014/09/17 04:32:20
nit: remove the date?
Adam Rice
2014/09/30 13:44:50
Done.
|
| + 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. |
| + 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()); |
| +} |
| + |
| +// Async revalidation is issued to the original URL. |
| +TEST_F(HttpCacheStaleWhileRevalidateTest, AsyncRequestUrlMatches) { |
| + net::CapturingBoundNetLog log; |
| + net::LoadTimingInfo load_timing_info; |
| + |
| + // Write to the cache. |
| + RunFixtureTransactionTest(); |
| + |
| + // Read back from the cache. |
| + RunTransactionTestAndGetTiming( |
| + cache_.http_cache(), transaction_, log.bound(), &load_timing_info); |
| + |
| + // Let the async request execute. |
| + RunLoop().RunUntilIdle(); |
| + |
| + net::CapturingNetLog::CapturedEntryList entries; |
|
rvargas (doing something else)
2014/09/17 04:32:19
using a handler seems better to me (ie, looking at
Adam Rice
2014/09/30 13:44:50
Done.
|
| + log.GetEntries(&entries); |
| + size_t index = |
| + net::ExpectLogContainsSomewhere(entries, |
| + 0, |
| + net::NetLog::TYPE_ASYNC_REVALIDATION, |
| + net::NetLog::PHASE_BEGIN); |
| + ASSERT_LT(index, entries.size()); |
| + std::string url_value; |
| + EXPECT_TRUE(entries[index].GetStringValue("url", &url_value)); |
| + EXPECT_EQ(transaction_.url, url_value); |
| +} |
| + |
| +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. |
| + 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()); |
| + |
| + RunLoop().RunUntilIdle(); |
| + |
| + // Still nothing. |
| + EXPECT_EQ(0, transaction_count()); |
| +} |
| + |
| +// A certificate error during an asynchronous fetch should cause the next fetch |
| +// to proceed synchronously. |
| +TEST_F(HttpCacheStaleWhileRevalidateTest, CertificateErrorCausesRefetch) { |
|
rvargas (doing something else)
2014/09/17 04:32:19
hmmm. Is this testing for... an incomplete impleme
Adam Rice
2014/09/30 13:44:50
Yes. I am just verifying the current behaviour. I
|
| + // 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. |
| + 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. |
| + 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. |
| + 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. |
| + RunLoop().RunUntilIdle(); |
| + EXPECT_EQ(2, transaction_count()); |
| +} |
| + |
| +// Cache-Control: must-revalidate directive should override the |
| +// stale-while-revalidate directive and cause a synchronous revalidation. |
| +TEST_F(HttpCacheStaleWhileRevalidateTest, MustRevalidateOverrides) { |
|
rvargas (doing something else)
2014/09/17 04:32:19
This seems like a test that belongs to response he
Adam Rice
2014/09/30 13:44:50
Yes. Thank you. I have moved it there.
|
| + transaction_.response_headers = |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT" |
| + "Age: 3601\n" |
| + "Cache-Control: " |
| + "max-age=3600,stale-while-revalidate=3600,must-revalidate\n"; |
| + |
| + // Write to the cache. This test uses custom response headers, so it cannot |
| + // use RunFixtureTransactionTest(). |
| + RunTransactionTest(cache_.http_cache(), transaction_); |
| + |
| + EXPECT_EQ(1, transaction_count()); |
| + |
| + // A synchronous revalidation is performed. |
| + RunTransactionTest(cache_.http_cache(), transaction_); |
| + |
| + EXPECT_EQ(2, transaction_count()); |
| } |