Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(325)

Unified Diff: net/http/http_cache_unittest.cc

Issue 455623003: stale-while-revalidate experimental implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove --disable-stale-while-revalidate flag and strengthen must-revalidate. Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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());
}

Powered by Google App Engine
This is Rietveld 408576698