Commit b17cf903 authored by Dave Tapuska's avatar Dave Tapuska Committed by Commit Bot

Compute the revalidation based on the stale-while-revalidate attribute.

Add additional API to determine if we should revalidate if we are allowing
a stale response. Add a test to cover the different intervals of what
should be returned by the API.

BUG=348877

Change-Id: I69be1c768e4896362406bb80a3dde307edae27d2
Reviewed-on: https://chromium-review.googlesource.com/1102352Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Commit-Queue: Dave Tapuska <dtapuska@chromium.org>
Cr-Commit-Position: refs/heads/master@{#569263}
parent 3f6b6de6
......@@ -470,6 +470,7 @@ static double FreshnessLifetime(const ResourceResponse& response,
}
static bool CanUseResponse(const ResourceResponse& response,
bool allow_stale,
double response_timestamp) {
if (response.IsNull())
return false;
......@@ -492,8 +493,11 @@ static bool CanUseResponse(const ResourceResponse& response,
return false;
}
return CurrentAge(response, response_timestamp) <=
FreshnessLifetime(response, response_timestamp);
double max_life = FreshnessLifetime(response, response_timestamp);
if (allow_stale)
max_life += response.CacheControlStaleWhileRevalidate();
return CurrentAge(response, response_timestamp) <= max_life;
}
const ResourceRequest& Resource::LastResourceRequest() const {
......@@ -1034,7 +1038,8 @@ bool Resource::MatchPreload(const FetchParameters& params,
bool Resource::CanReuseRedirectChain() const {
for (auto& redirect : redirect_chain_) {
if (!CanUseResponse(redirect.redirect_response_, response_timestamp_))
if (!CanUseResponse(redirect.redirect_response_, false /*allow_stale*/,
response_timestamp_))
return false;
if (redirect.request_.CacheControlContainsNoCache() ||
redirect.request_.CacheControlContainsNoStore())
......@@ -1068,12 +1073,49 @@ bool Resource::MustReloadDueToVaryHeader(
return false;
}
bool Resource::MustRevalidateDueToCacheHeaders() const {
return !CanUseResponse(GetResponse(), response_timestamp_) ||
bool Resource::MustRevalidateDueToCacheHeaders(bool allow_stale) const {
return !CanUseResponse(GetResponse(), allow_stale, response_timestamp_) ||
GetResourceRequest().CacheControlContainsNoCache() ||
GetResourceRequest().CacheControlContainsNoStore();
}
static bool ShouldRevalidateStaleResponse(const ResourceRequest& request,
const ResourceResponse& response,
double response_timestamp) {
double staleness = response.CacheControlStaleWhileRevalidate();
if (staleness == 0)
return false;
return CurrentAge(response, response_timestamp) >
FreshnessLifetime(response, response_timestamp);
}
bool Resource::ShouldRevalidateStaleResponse() const {
for (auto& redirect : redirect_chain_) {
// Use |response_timestamp_| since we don't store the timestamp
// of each redirect response.
if (blink::ShouldRevalidateStaleResponse(redirect.request_,
redirect.redirect_response_,
response_timestamp_)) {
return true;
}
}
return blink::ShouldRevalidateStaleResponse(
GetResourceRequest(), GetResponse(), response_timestamp_);
}
bool Resource::AsyncRevalidationRequested() const {
if (GetResponse().AsyncRevalidationRequested())
return true;
for (auto& redirect : redirect_chain_) {
if (redirect.redirect_response_.AsyncRevalidationRequested())
return true;
}
return false;
}
bool Resource::CanUseCacheValidator() const {
if (IsLoading() || ErrorOccurred())
return false;
......
......@@ -261,12 +261,17 @@ class PLATFORM_EXPORT Resource : public GarbageCollectedFinalized<Resource>,
base::SingleThreadTaskRunner*);
bool CanReuseRedirectChain() const;
bool MustRevalidateDueToCacheHeaders() const;
bool MustRevalidateDueToCacheHeaders(bool allow_stale) const;
bool ShouldRevalidateStaleResponse() const;
virtual bool CanUseCacheValidator() const;
bool IsCacheValidator() const { return is_revalidating_; }
bool HasCacheControlNoStoreHeader() const;
bool MustReloadDueToVaryHeader(const ResourceRequest& new_request) const;
// Returns true if any resource in the request chain has revalidation
// requested.
bool AsyncRevalidationRequested() const;
const IntegrityMetadataSet& IntegrityMetadata() const {
return options_.integrity_metadata;
}
......
......@@ -1267,7 +1267,8 @@ ResourceFetcher::DetermineRevalidationPolicyInternal(
// Check if the cache headers requires us to revalidate (cache expiration for
// example).
if (request.GetCacheMode() == mojom::FetchCacheMode::kValidateCache ||
existing_resource.MustRevalidateDueToCacheHeaders() ||
existing_resource.MustRevalidateDueToCacheHeaders(
false /* allow_stale */) ||
request.CacheControlContainsNoCache()) {
// Revalidation is harmful for non-matched preloads because it may lead to
// sharing one preloaded resource among multiple ResourceFetchers.
......
......@@ -410,6 +410,10 @@ double ResourceResponse::CacheControlStaleWhileRevalidate() const {
http_header_fields_.Get(kCacheControlHeader),
http_header_fields_.Get(kPragmaHeader));
}
if (!std::isfinite(cache_control_header_.stale_while_revalidate) ||
cache_control_header_.stale_while_revalidate < 0) {
return 0;
}
return cache_control_header_.stale_while_revalidate;
}
......
......@@ -218,11 +218,12 @@ class PLATFORM_EXPORT ResourceResponse final {
bool CacheControlContainsMustRevalidate() const;
bool HasCacheValidatorFields() const;
double CacheControlMaxAge() const;
double CacheControlStaleWhileRevalidate() const;
double Date() const;
double Age() const;
double Expires() const;
double LastModified() const;
// Will always return values >= 0.
double CacheControlStaleWhileRevalidate() const;
unsigned ConnectionID() const;
void SetConnectionID(unsigned);
......
......@@ -421,4 +421,70 @@ TEST(ResourceTest, RedirectDuringRevalidation) {
EXPECT_FALSE(resource->IsAlive());
}
TEST(ResourceTest, StaleWhileRevalidateCacheControl) {
ScopedTestingPlatformSupport<MockPlatform> mock;
const KURL url("http://127.0.0.1:8000/foo.html");
ResourceResponse response(url);
response.SetHTTPStatusCode(200);
response.SetHTTPHeaderField(HTTPNames::Cache_Control,
"max-age=0, stale-while-revalidate=40");
MockResource* resource = MockResource::Create(url);
resource->ResponseReceived(response, nullptr);
resource->FinishForTest();
EXPECT_FALSE(resource->MustRevalidateDueToCacheHeaders(false));
EXPECT_FALSE(resource->MustRevalidateDueToCacheHeaders(true));
EXPECT_FALSE(resource->ShouldRevalidateStaleResponse());
mock->AdvanceClockSeconds(1);
EXPECT_TRUE(resource->MustRevalidateDueToCacheHeaders(false));
EXPECT_FALSE(resource->MustRevalidateDueToCacheHeaders(true));
EXPECT_TRUE(resource->ShouldRevalidateStaleResponse());
mock->AdvanceClockSeconds(40);
EXPECT_TRUE(resource->MustRevalidateDueToCacheHeaders(false));
EXPECT_TRUE(resource->MustRevalidateDueToCacheHeaders(true));
EXPECT_TRUE(resource->ShouldRevalidateStaleResponse());
}
TEST(ResourceTest, StaleWhileRevalidateCacheControlWithRedirect) {
ScopedTestingPlatformSupport<MockPlatform> mock;
const KURL url("http://127.0.0.1:8000/foo.html");
const KURL redirect_target_url("http://127.0.0.1:8000/food.html");
ResourceResponse response(url);
response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=50");
response.SetHTTPStatusCode(200);
// The revalidating request is redirected.
ResourceResponse redirect_response(url);
redirect_response.SetHTTPHeaderField(
"location", AtomicString(redirect_target_url.GetString()));
redirect_response.SetHTTPStatusCode(302);
redirect_response.SetHTTPHeaderField(HTTPNames::Cache_Control,
"max-age=0, stale-while-revalidate=40");
redirect_response.SetAsyncRevalidationRequested(true);
ResourceRequest redirected_revalidating_request(redirect_target_url);
MockResource* resource = MockResource::Create(url);
resource->WillFollowRedirect(redirected_revalidating_request,
redirect_response);
resource->ResponseReceived(response, nullptr);
resource->FinishForTest();
EXPECT_FALSE(resource->MustRevalidateDueToCacheHeaders(false));
EXPECT_FALSE(resource->MustRevalidateDueToCacheHeaders(true));
EXPECT_FALSE(resource->ShouldRevalidateStaleResponse());
mock->AdvanceClockSeconds(41);
// MustRevalidateDueToCacheHeaders only looks at the stored response not
// any redirects but ShouldRevalidate and AsyncRevalidationRequest look
// at the entire redirect chain.
EXPECT_FALSE(resource->MustRevalidateDueToCacheHeaders(false));
EXPECT_FALSE(resource->MustRevalidateDueToCacheHeaders(true));
EXPECT_TRUE(resource->ShouldRevalidateStaleResponse());
EXPECT_TRUE(resource->AsyncRevalidationRequested());
}
} // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment