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

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

Issue 2763393002: Remove stale-while-revalidate from net (Closed)
Patch Set: fixes Created 3 years, 8 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
« no previous file with comments | « net/http/http_cache_transaction.h ('k') | net/http/http_cache_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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" // For OS_POSIX 7 #include "build/build_config.h" // For OS_POSIX
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/auto_reset.h" 16 #include "base/auto_reset.h"
17 #include "base/bind.h" 17 #include "base/bind.h"
18 #include "base/callback_helpers.h" 18 #include "base/callback_helpers.h"
19 #include "base/compiler_specific.h" 19 #include "base/compiler_specific.h"
20 #include "base/format_macros.h"
21 #include "base/location.h" 20 #include "base/location.h"
22 #include "base/macros.h" 21 #include "base/macros.h"
23 #include "base/metrics/histogram_macros.h" 22 #include "base/metrics/histogram_macros.h"
24 #include "base/metrics/sparse_histogram.h" 23 #include "base/metrics/sparse_histogram.h"
25 #include "base/single_thread_task_runner.h" 24 #include "base/single_thread_task_runner.h"
26 #include "base/strings/string_number_conversions.h" // For HexEncode. 25 #include "base/strings/string_number_conversions.h" // For HexEncode.
27 #include "base/strings/string_piece.h"
28 #include "base/strings/string_util.h" // For LowerCaseEqualsASCII. 26 #include "base/strings/string_util.h" // For LowerCaseEqualsASCII.
29 #include "base/strings/stringprintf.h"
30 #include "base/threading/thread_task_runner_handle.h" 27 #include "base/threading/thread_task_runner_handle.h"
31 #include "base/time/clock.h" 28 #include "base/time/clock.h"
32 #include "base/trace_event/trace_event.h" 29 #include "base/trace_event/trace_event.h"
33 #include "base/values.h"
34 #include "net/base/auth.h" 30 #include "net/base/auth.h"
35 #include "net/base/load_flags.h" 31 #include "net/base/load_flags.h"
36 #include "net/base/load_timing_info.h" 32 #include "net/base/load_timing_info.h"
37 #include "net/base/trace_constants.h" 33 #include "net/base/trace_constants.h"
38 #include "net/base/upload_data_stream.h" 34 #include "net/base/upload_data_stream.h"
39 #include "net/cert/cert_status_flags.h" 35 #include "net/cert/cert_status_flags.h"
40 #include "net/cert/x509_certificate.h" 36 #include "net/cert/x509_certificate.h"
41 #include "net/disk_cache/disk_cache.h" 37 #include "net/disk_cache/disk_cache.h"
42 #include "net/http/http_network_session.h" 38 #include "net/http/http_network_session.h"
43 #include "net/http/http_request_info.h" 39 #include "net/http/http_request_info.h"
44 #include "net/http/http_util.h" 40 #include "net/http/http_util.h"
45 #include "net/log/net_log_event_type.h" 41 #include "net/log/net_log_event_type.h"
46 #include "net/ssl/ssl_cert_request_info.h" 42 #include "net/ssl/ssl_cert_request_info.h"
47 #include "net/ssl/ssl_config_service.h" 43 #include "net/ssl/ssl_config_service.h"
48 44
49 using base::Time; 45 using base::Time;
50 using base::TimeDelta; 46 using base::TimeDelta;
51 using base::TimeTicks; 47 using base::TimeTicks;
52 48
53 namespace net { 49 namespace net {
54 50
55 using CacheEntryStatus = HttpResponseInfo::CacheEntryStatus; 51 using CacheEntryStatus = HttpResponseInfo::CacheEntryStatus;
56 52
57 namespace { 53 namespace {
58 54
59 // TODO(ricea): Move this to HttpResponseHeaders once it is standardised.
60 static const char kFreshnessHeader[] = "Resource-Freshness";
61
62 // From http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-21#section-6 55 // From http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-21#section-6
63 // a "non-error response" is one with a 2xx (Successful) or 3xx 56 // a "non-error response" is one with a 2xx (Successful) or 3xx
64 // (Redirection) status code. 57 // (Redirection) status code.
65 bool NonErrorResponse(int status_code) { 58 bool NonErrorResponse(int status_code) {
66 int status_code_range = status_code / 100; 59 int status_code_range = status_code / 100;
67 return status_code_range == 2 || status_code_range == 3; 60 return status_code_range == 2 || status_code_range == 3;
68 } 61 }
69 62
70 void RecordNoStoreHeaderHistogram(int load_flags, 63 void RecordNoStoreHeaderHistogram(int load_flags,
71 const HttpResponseInfo* response) { 64 const HttpResponseInfo* response) {
72 if (load_flags & LOAD_MAIN_FRAME_DEPRECATED) { 65 if (load_flags & LOAD_MAIN_FRAME_DEPRECATED) {
73 UMA_HISTOGRAM_BOOLEAN( 66 UMA_HISTOGRAM_BOOLEAN(
74 "Net.MainFrameNoStore", 67 "Net.MainFrameNoStore",
75 response->headers->HasHeaderValue("cache-control", "no-store")); 68 response->headers->HasHeaderValue("cache-control", "no-store"));
76 } 69 }
77 } 70 }
78 71
79 enum ExternallyConditionalizedType {
80 EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION,
81 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE,
82 EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS,
83 EXTERNALLY_CONDITIONALIZED_MAX
84 };
85
86 } // namespace 72 } // namespace
87 73
88 #define CACHE_STATUS_HISTOGRAMS(type) \ 74 #define CACHE_STATUS_HISTOGRAMS(type) \
89 do { \ 75 do { \
90 UMA_HISTOGRAM_ENUMERATION("HttpCache.Pattern" type, cache_entry_status_, \ 76 UMA_HISTOGRAM_ENUMERATION("HttpCache.Pattern" type, cache_entry_status_, \
91 CacheEntryStatus::ENTRY_MAX); \ 77 CacheEntryStatus::ENTRY_MAX); \
92 if (validation_request) { \ 78 if (validation_request) { \
93 UMA_HISTOGRAM_ENUMERATION("HttpCache.ValidationCause" type, \ 79 UMA_HISTOGRAM_ENUMERATION("HttpCache.ValidationCause" type, \
94 validation_cause_, VALIDATION_CAUSE_MAX); \ 80 validation_cause_, VALIDATION_CAUSE_MAX); \
95 } \ 81 } \
(...skipping 2008 matching lines...) Expand 10 before | Expand all | Expand 10 after
2104 TransitionToState(STATE_NONE); 2090 TransitionToState(STATE_NONE);
2105 return ERR_CACHE_MISS; 2091 return ERR_CACHE_MISS;
2106 } 2092 }
2107 2093
2108 // We don't have the whole resource. 2094 // We don't have the whole resource.
2109 if (truncated_) { 2095 if (truncated_) {
2110 TransitionToState(STATE_NONE); 2096 TransitionToState(STATE_NONE);
2111 return ERR_CACHE_MISS; 2097 return ERR_CACHE_MISS;
2112 } 2098 }
2113 2099
2114 if (RequiresValidation() != VALIDATION_NONE) { 2100 if (RequiresValidation()) {
2115 TransitionToState(STATE_NONE); 2101 TransitionToState(STATE_NONE);
2116 return ERR_CACHE_MISS; 2102 return ERR_CACHE_MISS;
2117 } 2103 }
2118 2104
2119 if (request_->method == "HEAD") 2105 if (request_->method == "HEAD")
2120 FixHeadersForHead(); 2106 FixHeadersForHead();
2121 2107
2122 if (entry_->disk_entry->GetDataSize(kMetadataIndex)) 2108 if (entry_->disk_entry->GetDataSize(kMetadataIndex))
2123 TransitionToState(STATE_CACHE_READ_METADATA); 2109 TransitionToState(STATE_CACHE_READ_METADATA);
2124 else 2110 else
2125 TransitionToState(STATE_NONE); 2111 TransitionToState(STATE_NONE);
2126 2112
2127 return OK; 2113 return OK;
2128 } 2114 }
2129 2115
2130 int HttpCache::Transaction::BeginCacheValidation() { 2116 int HttpCache::Transaction::BeginCacheValidation() {
2131 DCHECK_EQ(mode_, READ_WRITE); 2117 DCHECK_EQ(mode_, READ_WRITE);
2132 2118
2133 ValidationType required_validation = RequiresValidation(); 2119 bool skip_validation = !RequiresValidation();
2134
2135 bool skip_validation = (required_validation == VALIDATION_NONE);
2136
2137 if ((effective_load_flags_ & LOAD_SUPPORT_ASYNC_REVALIDATION) &&
2138 required_validation == VALIDATION_ASYNCHRONOUS) {
2139 DCHECK_EQ(request_->method, "GET");
2140 skip_validation = true;
2141 response_.async_revalidation_required = true;
2142 }
2143 2120
2144 if (request_->method == "HEAD" && 2121 if (request_->method == "HEAD" &&
2145 (truncated_ || response_.headers->response_code() == 206)) { 2122 (truncated_ || response_.headers->response_code() == 206)) {
2146 DCHECK(!partial_); 2123 DCHECK(!partial_);
2147 if (skip_validation) 2124 if (skip_validation)
2148 return SetupEntryForRead(); 2125 return SetupEntryForRead();
2149 2126
2150 // Bail out! 2127 // Bail out!
2151 TransitionToState(STATE_SEND_REQUEST); 2128 TransitionToState(STATE_SEND_REQUEST);
2152 mode_ = NONE; 2129 mode_ = NONE;
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
2254 2231
2255 if (response_.headers->response_code() != 200 || truncated_ || 2232 if (response_.headers->response_code() != 200 || truncated_ ||
2256 validator.empty() || validator != external_validation_.values[i]) { 2233 validator.empty() || validator != external_validation_.values[i]) {
2257 // The externally conditionalized request is not a validation request 2234 // The externally conditionalized request is not a validation request
2258 // for our existing cache entry. Proceed with caching disabled. 2235 // for our existing cache entry. Proceed with caching disabled.
2259 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER); 2236 UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
2260 DoneWritingToEntry(true); 2237 DoneWritingToEntry(true);
2261 } 2238 }
2262 } 2239 }
2263 2240
2264 // TODO(ricea): This calculation is expensive to perform just to collect
2265 // statistics. Either remove it or use the result, depending on the result of
2266 // the experiment.
2267 ExternallyConditionalizedType type =
2268 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE;
2269 if (mode_ == NONE)
2270 type = EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS;
2271 else if (RequiresValidation() != VALIDATION_NONE)
2272 type = EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION;
2273
2274 // TODO(ricea): Add CACHE_USABLE_STALE once stale-while-revalidate CL landed.
2275 // TODO(ricea): Either remove this histogram or make it permanent by M40.
2276 UMA_HISTOGRAM_ENUMERATION("HttpCache.ExternallyConditionalized",
2277 type,
2278 EXTERNALLY_CONDITIONALIZED_MAX);
2279
2280 TransitionToState(STATE_SEND_REQUEST); 2241 TransitionToState(STATE_SEND_REQUEST);
2281 return OK; 2242 return OK;
2282 } 2243 }
2283 2244
2284 int HttpCache::Transaction::RestartNetworkRequest() { 2245 int HttpCache::Transaction::RestartNetworkRequest() {
2285 DCHECK(mode_ & WRITE || mode_ == NONE); 2246 DCHECK(mode_ & WRITE || mode_ == NONE);
2286 DCHECK(network_trans_.get()); 2247 DCHECK(network_trans_.get());
2287 DCHECK_EQ(STATE_NONE, next_state_); 2248 DCHECK_EQ(STATE_NONE, next_state_);
2288 2249
2289 next_state_ = STATE_SEND_REQUEST_COMPLETE; 2250 next_state_ = STATE_SEND_REQUEST_COMPLETE;
(...skipping 24 matching lines...) Expand all
2314 DCHECK(network_trans_.get()); 2275 DCHECK(network_trans_.get());
2315 DCHECK_EQ(STATE_NONE, next_state_); 2276 DCHECK_EQ(STATE_NONE, next_state_);
2316 2277
2317 next_state_ = STATE_SEND_REQUEST_COMPLETE; 2278 next_state_ = STATE_SEND_REQUEST_COMPLETE;
2318 int rv = network_trans_->RestartWithAuth(credentials, io_callback_); 2279 int rv = network_trans_->RestartWithAuth(credentials, io_callback_);
2319 if (rv != ERR_IO_PENDING) 2280 if (rv != ERR_IO_PENDING)
2320 return DoLoop(rv); 2281 return DoLoop(rv);
2321 return rv; 2282 return rv;
2322 } 2283 }
2323 2284
2324 ValidationType HttpCache::Transaction::RequiresValidation() { 2285 bool HttpCache::Transaction::RequiresValidation() {
2325 // TODO(darin): need to do more work here: 2286 // TODO(darin): need to do more work here:
2326 // - make sure we have a matching request method 2287 // - make sure we have a matching request method
2327 // - watch out for cached responses that depend on authentication 2288 // - watch out for cached responses that depend on authentication
2328 2289
2329 if (!(effective_load_flags_ & LOAD_SKIP_VARY_CHECK) && 2290 if (!(effective_load_flags_ & LOAD_SKIP_VARY_CHECK) &&
2330 response_.vary_data.is_valid() && 2291 response_.vary_data.is_valid() &&
2331 !response_.vary_data.MatchesRequest(*request_, 2292 !response_.vary_data.MatchesRequest(*request_,
2332 *response_.headers.get())) { 2293 *response_.headers.get())) {
2333 vary_mismatch_ = true; 2294 vary_mismatch_ = true;
2334 validation_cause_ = VALIDATION_CAUSE_VARY_MISMATCH; 2295 validation_cause_ = VALIDATION_CAUSE_VARY_MISMATCH;
2335 return VALIDATION_SYNCHRONOUS; 2296 return true;
2336 } 2297 }
2337 2298
2338 if (effective_load_flags_ & LOAD_SKIP_CACHE_VALIDATION) 2299 if (effective_load_flags_ & LOAD_SKIP_CACHE_VALIDATION)
2339 return VALIDATION_NONE; 2300 return false;
2340 2301
2341 if (response_.unused_since_prefetch && 2302 if (response_.unused_since_prefetch &&
2342 !(effective_load_flags_ & LOAD_PREFETCH) && 2303 !(effective_load_flags_ & LOAD_PREFETCH) &&
2343 response_.headers->GetCurrentAge( 2304 response_.headers->GetCurrentAge(
2344 response_.request_time, response_.response_time, 2305 response_.request_time, response_.response_time,
2345 cache_->clock_->Now()) < TimeDelta::FromMinutes(kPrefetchReuseMins)) { 2306 cache_->clock_->Now()) < TimeDelta::FromMinutes(kPrefetchReuseMins)) {
2346 // The first use of a resource after prefetch within a short window skips 2307 // The first use of a resource after prefetch within a short window skips
2347 // validation. 2308 // validation.
2348 return VALIDATION_NONE; 2309 return false;
2349 } 2310 }
2350 2311
2351 if (effective_load_flags_ & LOAD_VALIDATE_CACHE) { 2312 if (effective_load_flags_ & LOAD_VALIDATE_CACHE) {
2352 validation_cause_ = VALIDATION_CAUSE_VALIDATE_FLAG; 2313 validation_cause_ = VALIDATION_CAUSE_VALIDATE_FLAG;
2353 return VALIDATION_SYNCHRONOUS; 2314 return true;
2354 } 2315 }
2355 2316
2356 if (request_->method == "PUT" || request_->method == "DELETE") 2317 if (request_->method == "PUT" || request_->method == "DELETE")
2357 return VALIDATION_SYNCHRONOUS; 2318 return true;
2358 2319
2359 ValidationType validation_required_by_headers = 2320 bool validation_required_by_headers = response_.headers->RequiresValidation(
2360 response_.headers->RequiresValidation(response_.request_time, 2321 response_.request_time, response_.response_time, cache_->clock_->Now());
2361 response_.response_time,
2362 cache_->clock_->Now());
2363 2322
2364 if (validation_required_by_headers != VALIDATION_NONE) { 2323 if (validation_required_by_headers) {
2365 HttpResponseHeaders::FreshnessLifetimes lifetimes = 2324 HttpResponseHeaders::FreshnessLifetimes lifetimes =
2366 response_.headers->GetFreshnessLifetimes(response_.response_time); 2325 response_.headers->GetFreshnessLifetimes(response_.response_time);
2367 if (lifetimes.freshness == base::TimeDelta()) { 2326 if (lifetimes.freshness == base::TimeDelta()) {
2368 validation_cause_ = VALIDATION_CAUSE_ZERO_FRESHNESS; 2327 validation_cause_ = VALIDATION_CAUSE_ZERO_FRESHNESS;
2369 } else { 2328 } else {
2370 validation_cause_ = VALIDATION_CAUSE_STALE; 2329 validation_cause_ = VALIDATION_CAUSE_STALE;
2371 stale_entry_freshness_ = lifetimes.freshness; 2330 stale_entry_freshness_ = lifetimes.freshness;
2372 stale_entry_age_ = response_.headers->GetCurrentAge( 2331 stale_entry_age_ = response_.headers->GetCurrentAge(
2373 response_.request_time, response_.response_time, 2332 response_.request_time, response_.response_time,
2374 cache_->clock_->Now()); 2333 cache_->clock_->Now());
2375 } 2334 }
2376 } 2335 }
2377 2336
2378 if (validation_required_by_headers == VALIDATION_ASYNCHRONOUS) {
2379 // Asynchronous revalidation is only supported for GET methods.
2380 if (request_->method != "GET")
2381 return VALIDATION_SYNCHRONOUS;
2382 }
2383
2384 return validation_required_by_headers; 2337 return validation_required_by_headers;
2385 } 2338 }
2386 2339
2387 bool HttpCache::Transaction::ConditionalizeRequest() { 2340 bool HttpCache::Transaction::ConditionalizeRequest() {
2388 DCHECK(response_.headers.get()); 2341 DCHECK(response_.headers.get());
2389 2342
2390 if (request_->method == "PUT" || request_->method == "DELETE") 2343 if (request_->method == "PUT" || request_->method == "DELETE")
2391 return false; 2344 return false;
2392 2345
2393 // This only makes sense for cached 200 or 206 responses. 2346 // This only makes sense for cached 200 or 206 responses.
(...skipping 27 matching lines...) Expand all
2421 if (!partial_) { 2374 if (!partial_) {
2422 // Need to customize the request, so this forces us to allocate :( 2375 // Need to customize the request, so this forces us to allocate :(
2423 custom_request_.reset(new HttpRequestInfo(*request_)); 2376 custom_request_.reset(new HttpRequestInfo(*request_));
2424 request_ = custom_request_.get(); 2377 request_ = custom_request_.get();
2425 } 2378 }
2426 DCHECK(custom_request_.get()); 2379 DCHECK(custom_request_.get());
2427 2380
2428 bool use_if_range = 2381 bool use_if_range =
2429 partial_ && !partial_->IsCurrentRangeCached() && !invalid_range_; 2382 partial_ && !partial_->IsCurrentRangeCached() && !invalid_range_;
2430 2383
2431 if (!use_if_range) {
2432 // stale-while-revalidate is not useful when we only have a partial response
2433 // cached, so don't set the header in that case.
2434 HttpResponseHeaders::FreshnessLifetimes lifetimes =
2435 response_.headers->GetFreshnessLifetimes(response_.response_time);
2436 if (lifetimes.staleness > TimeDelta()) {
2437 TimeDelta current_age = response_.headers->GetCurrentAge(
2438 response_.request_time, response_.response_time,
2439 cache_->clock_->Now());
2440
2441 custom_request_->extra_headers.SetHeader(
2442 kFreshnessHeader,
2443 base::StringPrintf("max-age=%" PRId64
2444 ",stale-while-revalidate=%" PRId64 ",age=%" PRId64,
2445 lifetimes.freshness.InSeconds(),
2446 lifetimes.staleness.InSeconds(),
2447 current_age.InSeconds()));
2448 }
2449 }
2450
2451 if (!etag_value.empty()) { 2384 if (!etag_value.empty()) {
2452 if (use_if_range) { 2385 if (use_if_range) {
2453 // We don't want to switch to WRITE mode if we don't have this block of a 2386 // We don't want to switch to WRITE mode if we don't have this block of a
2454 // byte-range request because we may have other parts cached. 2387 // byte-range request because we may have other parts cached.
2455 custom_request_->extra_headers.SetHeader( 2388 custom_request_->extra_headers.SetHeader(
2456 HttpRequestHeaders::kIfRange, etag_value); 2389 HttpRequestHeaders::kIfRange, etag_value);
2457 } else { 2390 } else {
2458 custom_request_->extra_headers.SetHeader( 2391 custom_request_->extra_headers.SetHeader(
2459 HttpRequestHeaders::kIfNoneMatch, etag_value); 2392 HttpRequestHeaders::kIfNoneMatch, etag_value);
2460 } 2393 }
(...skipping 623 matching lines...) Expand 10 before | Expand all | Expand 10 after
3084 } 3017 }
3085 3018
3086 void HttpCache::Transaction::TransitionToState(State state) { 3019 void HttpCache::Transaction::TransitionToState(State state) {
3087 // Ensure that the state is only set once per Do* state. 3020 // Ensure that the state is only set once per Do* state.
3088 DCHECK(in_do_loop_); 3021 DCHECK(in_do_loop_);
3089 DCHECK_EQ(STATE_UNSET, next_state_) << "Next state is " << state; 3022 DCHECK_EQ(STATE_UNSET, next_state_) << "Next state is " << state;
3090 next_state_ = state; 3023 next_state_ = state;
3091 } 3024 }
3092 3025
3093 } // namespace net 3026 } // namespace net
OLDNEW
« no previous file with comments | « net/http/http_cache_transaction.h ('k') | net/http/http_cache_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698