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

Side by Side Diff: net/http/http_cache_transaction.cc

Issue 455623003: stale-while-revalidate experimental implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 6 years, 2 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/http/http_cache_transaction.h" 5 #include "net/http/http_cache_transaction.h"
6 6
7 #include "build/build_config.h" 7 #include "build/build_config.h"
8 8
9 #if defined(OS_POSIX) 9 #if defined(OS_POSIX)
10 #include <unistd.h> 10 #include <unistd.h>
11 #endif 11 #endif
12 12
13 #include <algorithm> 13 #include <algorithm>
14 #include <string> 14 #include <string>
15 15
16 #include "base/bind.h" 16 #include "base/bind.h"
17 #include "base/compiler_specific.h" 17 #include "base/compiler_specific.h"
18 #include "base/format_macros.h" 18 #include "base/format_macros.h"
19 #include "base/memory/ref_counted.h" 19 #include "base/memory/ref_counted.h"
20 #include "base/memory/scoped_ptr.h"
20 #include "base/metrics/field_trial.h" 21 #include "base/metrics/field_trial.h"
21 #include "base/metrics/histogram.h" 22 #include "base/metrics/histogram.h"
22 #include "base/metrics/sparse_histogram.h" 23 #include "base/metrics/sparse_histogram.h"
23 #include "base/rand_util.h" 24 #include "base/rand_util.h"
24 #include "base/strings/string_number_conversions.h" 25 #include "base/strings/string_number_conversions.h"
25 #include "base/strings/string_piece.h" 26 #include "base/strings/string_piece.h"
26 #include "base/strings/string_util.h" 27 #include "base/strings/string_util.h"
27 #include "base/strings/stringprintf.h" 28 #include "base/strings/stringprintf.h"
28 #include "base/time/time.h" 29 #include "base/time/time.h"
30 #include "base/values.h"
29 #include "net/base/completion_callback.h" 31 #include "net/base/completion_callback.h"
30 #include "net/base/io_buffer.h" 32 #include "net/base/io_buffer.h"
31 #include "net/base/load_flags.h" 33 #include "net/base/load_flags.h"
32 #include "net/base/load_timing_info.h" 34 #include "net/base/load_timing_info.h"
33 #include "net/base/net_errors.h" 35 #include "net/base/net_errors.h"
34 #include "net/base/net_log.h" 36 #include "net/base/net_log.h"
35 #include "net/base/upload_data_stream.h" 37 #include "net/base/upload_data_stream.h"
36 #include "net/cert/cert_status_flags.h" 38 #include "net/cert/cert_status_flags.h"
37 #include "net/disk_cache/disk_cache.h" 39 #include "net/disk_cache/disk_cache.h"
38 #include "net/http/disk_based_cert_cache.h" 40 #include "net/http/disk_based_cert_cache.h"
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after
231 233
232 void RecordNoStoreHeaderHistogram(int load_flags, 234 void RecordNoStoreHeaderHistogram(int load_flags,
233 const net::HttpResponseInfo* response) { 235 const net::HttpResponseInfo* response) {
234 if (load_flags & net::LOAD_MAIN_FRAME) { 236 if (load_flags & net::LOAD_MAIN_FRAME) {
235 UMA_HISTOGRAM_BOOLEAN( 237 UMA_HISTOGRAM_BOOLEAN(
236 "Net.MainFrameNoStore", 238 "Net.MainFrameNoStore",
237 response->headers->HasHeaderValue("cache-control", "no-store")); 239 response->headers->HasHeaderValue("cache-control", "no-store"));
238 } 240 }
239 } 241 }
240 242
243 base::Value* NetLogAsyncRevalidationInfoCallback(
244 const net::NetLog::Source& source,
245 const net::HttpRequestInfo* request,
246 net::NetLog::LogLevel log_level) {
247 base::DictionaryValue* dict = new base::DictionaryValue();
248 source.AddToEventParameters(dict);
249
250 dict->SetString("url", request->url.possibly_invalid_spec());
251 dict->SetString("method", request->method);
252 return dict;
253 }
254
241 enum ExternallyConditionalizedType { 255 enum ExternallyConditionalizedType {
242 EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION, 256 EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION,
243 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE, 257 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE,
244 EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS, 258 EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS,
245 EXTERNALLY_CONDITIONALIZED_MAX 259 EXTERNALLY_CONDITIONALIZED_MAX
246 }; 260 };
247 261
248 } // namespace 262 } // namespace
249 263
250 namespace net { 264 namespace net {
(...skipping 1852 matching lines...) Expand 10 before | Expand all | Expand 10 after
2103 2117
2104 if (entry_->disk_entry->GetDataSize(kMetadataIndex)) 2118 if (entry_->disk_entry->GetDataSize(kMetadataIndex))
2105 next_state_ = STATE_CACHE_READ_METADATA; 2119 next_state_ = STATE_CACHE_READ_METADATA;
2106 2120
2107 return OK; 2121 return OK;
2108 } 2122 }
2109 2123
2110 int HttpCache::Transaction::BeginCacheValidation() { 2124 int HttpCache::Transaction::BeginCacheValidation() {
2111 DCHECK(mode_ == READ_WRITE); 2125 DCHECK(mode_ == READ_WRITE);
2112 2126
2113 bool skip_validation = !RequiresValidation(); 2127 ValidationType required_validation = RequiresValidation();
2128
2129 bool skip_validation = (required_validation == VALIDATION_NONE);
2130
2131 if (required_validation == VALIDATION_ASYNCHRONOUS &&
2132 !(request_->method == "GET" && (truncated_ || partial_)) && cache_ &&
2133 cache_->use_stale_while_revalidate()) {
2134 TriggerAsyncValidation();
2135 skip_validation = true;
2136 }
2114 2137
2115 if (request_->method == "HEAD" && 2138 if (request_->method == "HEAD" &&
2116 (truncated_ || response_.headers->response_code() == 206)) { 2139 (truncated_ || response_.headers->response_code() == 206)) {
2117 DCHECK(!partial_); 2140 DCHECK(!partial_);
2118 if (skip_validation) 2141 if (skip_validation)
2119 return SetupEntryForRead(); 2142 return SetupEntryForRead();
2120 2143
2121 // Bail out! 2144 // Bail out!
2122 next_state_ = STATE_SEND_REQUEST; 2145 next_state_ = STATE_SEND_REQUEST;
2123 mode_ = NONE; 2146 mode_ = NONE;
2124 return OK; 2147 return OK;
2125 } 2148 }
2126 2149
2127 if (truncated_) { 2150 if (truncated_) {
2128 // Truncated entries can cause partial gets, so we shouldn't record this 2151 // Truncated entries can cause partial gets, so we shouldn't record this
2129 // load in histograms. 2152 // load in histograms.
2130 UpdateTransactionPattern(PATTERN_NOT_COVERED); 2153 UpdateTransactionPattern(PATTERN_NOT_COVERED);
2131 skip_validation = !partial_->initial_validation(); 2154 skip_validation = !partial_->initial_validation();
2132 } 2155 }
2133 2156
2134 if (partial_.get() && (is_sparse_ || truncated_) && 2157 if (partial_.get() && (is_sparse_ || truncated_) &&
2135 (!partial_->IsCurrentRangeCached() || invalid_range_)) { 2158 (!partial_->IsCurrentRangeCached() || invalid_range_)) {
2136 // Force revalidation for sparse or truncated entries. Note that we don't 2159 // Force revalidation for sparse or truncated entries. Note that we don't
2137 // want to ignore the regular validation logic just because a byte range was 2160 // want to ignore the regular validation logic just because a byte range was
2138 // part of the request. 2161 // part of the request.
2139 skip_validation = false; 2162 skip_validation = false;
2140 } 2163 }
2141 2164
2142 if (skip_validation) { 2165 if (skip_validation) {
2166 // TODO(ricea): Is this pattern okay for asynchronous revalidations?
2143 UpdateTransactionPattern(PATTERN_ENTRY_USED); 2167 UpdateTransactionPattern(PATTERN_ENTRY_USED);
2144 RecordOfflineStatus(effective_load_flags_, OFFLINE_STATUS_FRESH_CACHE); 2168 RecordOfflineStatus(effective_load_flags_, OFFLINE_STATUS_FRESH_CACHE);
2145 return SetupEntryForRead(); 2169 return SetupEntryForRead();
2146 } else { 2170 } else {
2147 // Make the network request conditional, to see if we may reuse our cached 2171 // Make the network request conditional, to see if we may reuse our cached
2148 // response. If we cannot do so, then we just resort to a normal fetch. 2172 // response. If we cannot do so, then we just resort to a normal fetch.
2149 // Our mode remains READ_WRITE for a conditional request. Even if the 2173 // Our mode remains READ_WRITE for a conditional request. Even if the
2150 // conditionalization fails, we don't switch to WRITE mode until we 2174 // conditionalization fails, we don't switch to WRITE mode until we
2151 // know we won't be falling back to using the cache entry in the 2175 // know we won't be falling back to using the cache entry in the
2152 // LOAD_FROM_CACHE_IF_OFFLINE case. 2176 // LOAD_FROM_CACHE_IF_OFFLINE case.
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
2288 DCHECK(network_trans_.get()); 2312 DCHECK(network_trans_.get());
2289 DCHECK_EQ(STATE_NONE, next_state_); 2313 DCHECK_EQ(STATE_NONE, next_state_);
2290 2314
2291 next_state_ = STATE_SEND_REQUEST_COMPLETE; 2315 next_state_ = STATE_SEND_REQUEST_COMPLETE;
2292 int rv = network_trans_->RestartWithAuth(credentials, io_callback_); 2316 int rv = network_trans_->RestartWithAuth(credentials, io_callback_);
2293 if (rv != ERR_IO_PENDING) 2317 if (rv != ERR_IO_PENDING)
2294 return DoLoop(rv); 2318 return DoLoop(rv);
2295 return rv; 2319 return rv;
2296 } 2320 }
2297 2321
2298 bool HttpCache::Transaction::RequiresValidation() { 2322 ValidationType HttpCache::Transaction::RequiresValidation() {
2299 // TODO(darin): need to do more work here: 2323 // TODO(darin): need to do more work here:
2300 // - make sure we have a matching request method 2324 // - make sure we have a matching request method
2301 // - watch out for cached responses that depend on authentication 2325 // - watch out for cached responses that depend on authentication
2302 2326
2303 // In playback mode, nothing requires validation. 2327 // In playback mode, nothing requires validation.
2304 if (cache_->mode() == net::HttpCache::PLAYBACK) 2328 if (cache_->mode() == net::HttpCache::PLAYBACK)
2305 return false; 2329 return VALIDATION_NONE;
2306 2330
2307 if (response_.vary_data.is_valid() && 2331 if (response_.vary_data.is_valid() &&
2308 !response_.vary_data.MatchesRequest(*request_, 2332 !response_.vary_data.MatchesRequest(*request_,
2309 *response_.headers.get())) { 2333 *response_.headers.get())) {
2310 vary_mismatch_ = true; 2334 vary_mismatch_ = true;
2311 return true; 2335 return VALIDATION_SYNCHRONOUS;
2312 } 2336 }
2313 2337
2314 if (effective_load_flags_ & LOAD_PREFERRING_CACHE) 2338 if (effective_load_flags_ & LOAD_PREFERRING_CACHE)
2315 return false; 2339 return VALIDATION_NONE;
2316 2340
2317 if (effective_load_flags_ & LOAD_VALIDATE_CACHE) 2341 if (effective_load_flags_ & (LOAD_VALIDATE_CACHE | LOAD_ASYNC_REVALIDATION))
2318 return true; 2342 return VALIDATION_SYNCHRONOUS;
2319 2343
2320 if (request_->method == "PUT" || request_->method == "DELETE") 2344 if (request_->method == "PUT" || request_->method == "DELETE")
2321 return true; 2345 return VALIDATION_SYNCHRONOUS;
2322 2346
2323 if (response_.headers->RequiresValidation( 2347 ValidationType validation_required_by_headers =
2324 response_.request_time, response_.response_time, Time::Now())) { 2348 response_.headers->RequiresValidation(
2325 return true; 2349 response_.request_time, response_.response_time, Time::Now());
2350
2351 if (validation_required_by_headers == VALIDATION_ASYNCHRONOUS) {
2352 // Asynchronous revalidation is only supported for GET and HEAD methods.
2353 if (request_->method != "GET" && request_->method != "HEAD")
2354 return VALIDATION_SYNCHRONOUS;
2326 } 2355 }
2327 2356
2328 return false; 2357 return validation_required_by_headers;
2329 } 2358 }
2330 2359
2331 bool HttpCache::Transaction::ConditionalizeRequest() { 2360 bool HttpCache::Transaction::ConditionalizeRequest() {
2332 DCHECK(response_.headers.get()); 2361 DCHECK(response_.headers.get());
2333 2362
2334 if (request_->method == "PUT" || request_->method == "DELETE") 2363 if (request_->method == "PUT" || request_->method == "DELETE")
2335 return false; 2364 return false;
2336 2365
2337 // This only makes sense for cached 200 or 206 responses. 2366 // This only makes sense for cached 200 or 206 responses.
2338 if (response_.headers->response_code() != 200 && 2367 if (response_.headers->response_code() != 200 &&
(...skipping 28 matching lines...) Expand all
2367 request_ = custom_request_.get(); 2396 request_ = custom_request_.get();
2368 } 2397 }
2369 DCHECK(custom_request_.get()); 2398 DCHECK(custom_request_.get());
2370 2399
2371 bool use_if_range = partial_.get() && !partial_->IsCurrentRangeCached() && 2400 bool use_if_range = partial_.get() && !partial_->IsCurrentRangeCached() &&
2372 !invalid_range_; 2401 !invalid_range_;
2373 2402
2374 if (!use_if_range) { 2403 if (!use_if_range) {
2375 // stale-while-revalidate is not useful when we only have a partial response 2404 // stale-while-revalidate is not useful when we only have a partial response
2376 // cached, so don't set the header in that case. 2405 // cached, so don't set the header in that case.
2377 TimeDelta stale_while_revalidate; 2406 HttpResponseHeaders::FreshnessLifetimes lifetime =
2378 if (response_.headers->GetStaleWhileRevalidateValue( 2407 response_.headers->GetFreshnessLifetimes(response_.response_time);
2379 &stale_while_revalidate) && 2408 if (lifetime.stale > TimeDelta()) {
2380 stale_while_revalidate > TimeDelta()) {
2381 TimeDelta max_age =
2382 response_.headers->GetFreshnessLifetime(response_.response_time);
2383 TimeDelta current_age = response_.headers->GetCurrentAge( 2409 TimeDelta current_age = response_.headers->GetCurrentAge(
2384 response_.request_time, response_.response_time, Time::Now()); 2410 response_.request_time, response_.response_time, Time::Now());
2385 2411
2386 custom_request_->extra_headers.SetHeader( 2412 custom_request_->extra_headers.SetHeader(
2387 kFreshnessHeader, 2413 kFreshnessHeader,
2388 base::StringPrintf("max-age=%" PRId64 2414 base::StringPrintf("max-age=%" PRId64
2389 ",stale-while-revalidate=%" PRId64 ",age=%" PRId64, 2415 ",stale-while-revalidate=%" PRId64 ",age=%" PRId64,
2390 max_age.InSeconds(), 2416 lifetime.fresh.InSeconds(),
2391 stale_while_revalidate.InSeconds(), 2417 lifetime.stale.InSeconds(),
2392 current_age.InSeconds())); 2418 current_age.InSeconds()));
2393 } 2419 }
2394 } 2420 }
2395 2421
2396 if (!etag_value.empty()) { 2422 if (!etag_value.empty()) {
2397 if (use_if_range) { 2423 if (use_if_range) {
2398 // We don't want to switch to WRITE mode if we don't have this block of a 2424 // We don't want to switch to WRITE mode if we don't have this block of a
2399 // byte-range request because we may have other parts cached. 2425 // byte-range request because we may have other parts cached.
2400 custom_request_->extra_headers.SetHeader( 2426 custom_request_->extra_headers.SetHeader(
2401 HttpRequestHeaders::kIfRange, etag_value); 2427 HttpRequestHeaders::kIfRange, etag_value);
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
2550 } 2576 }
2551 2577
2552 void HttpCache::Transaction::FixHeadersForHead() { 2578 void HttpCache::Transaction::FixHeadersForHead() {
2553 if (response_.headers->response_code() == 206) { 2579 if (response_.headers->response_code() == 206) {
2554 response_.headers->RemoveHeader("Content-Length"); 2580 response_.headers->RemoveHeader("Content-Length");
2555 response_.headers->RemoveHeader("Content-Range"); 2581 response_.headers->RemoveHeader("Content-Range");
2556 response_.headers->ReplaceStatusLine("HTTP/1.1 200 OK"); 2582 response_.headers->ReplaceStatusLine("HTTP/1.1 200 OK");
2557 } 2583 }
2558 } 2584 }
2559 2585
2586 void HttpCache::Transaction::TriggerAsyncValidation() {
2587 DCHECK(!request_->upload_data_stream);
2588 BoundNetLog async_revalidation_net_log(
2589 BoundNetLog::Make(net_log_.net_log(), NetLog::SOURCE_ASYNC_REVALIDATION));
2590 net_log_.AddEvent(
2591 NetLog::TYPE_HTTP_CACHE_VALIDATE_RESOURCE_ASYNC,
2592 async_revalidation_net_log.source().ToEventParametersCallback());
2593 async_revalidation_net_log.BeginEvent(
2594 NetLog::TYPE_ASYNC_REVALIDATION,
2595 base::Bind(
2596 &NetLogAsyncRevalidationInfoCallback, net_log_.source(), request_));
2597 base::MessageLoop::current()->PostTask(
2598 FROM_HERE,
2599 base::Bind(&HttpCache::PerformAsyncValidation,
2600 cache_, // cache_ is a weak pointer.
2601 *request_,
2602 async_revalidation_net_log));
2603 }
2604
2560 void HttpCache::Transaction::FailRangeRequest() { 2605 void HttpCache::Transaction::FailRangeRequest() {
2561 response_ = *new_response_; 2606 response_ = *new_response_;
2562 partial_->FixResponseHeaders(response_.headers.get(), false); 2607 partial_->FixResponseHeaders(response_.headers.get(), false);
2563 } 2608 }
2564 2609
2565 int HttpCache::Transaction::SetupEntryForRead() { 2610 int HttpCache::Transaction::SetupEntryForRead() {
2566 if (network_trans_) 2611 if (network_trans_)
2567 ResetNetworkTransaction(); 2612 ResetNetworkTransaction();
2568 if (partial_.get()) { 2613 if (partial_.get()) {
2569 if (truncated_ || is_sparse_ || !invalid_range_) { 2614 if (truncated_ || is_sparse_ || !invalid_range_) {
(...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after
2896 default: 2941 default:
2897 NOTREACHED(); 2942 NOTREACHED();
2898 } 2943 }
2899 } 2944 }
2900 2945
2901 void HttpCache::Transaction::OnIOComplete(int result) { 2946 void HttpCache::Transaction::OnIOComplete(int result) {
2902 DoLoop(result); 2947 DoLoop(result);
2903 } 2948 }
2904 2949
2905 } // namespace net 2950 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698