OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |