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/bind.h" | 16 #include "base/bind.h" |
17 #include "base/callback_helpers.h" | 17 #include "base/callback_helpers.h" |
18 #include "base/compiler_specific.h" | 18 #include "base/compiler_specific.h" |
19 #include "base/format_macros.h" | |
20 #include "base/location.h" | 19 #include "base/location.h" |
21 #include "base/macros.h" | 20 #include "base/macros.h" |
22 #include "base/metrics/histogram_macros.h" | 21 #include "base/metrics/histogram_macros.h" |
23 #include "base/metrics/sparse_histogram.h" | 22 #include "base/metrics/sparse_histogram.h" |
24 #include "base/single_thread_task_runner.h" | 23 #include "base/single_thread_task_runner.h" |
25 #include "base/strings/string_number_conversions.h" // For HexEncode. | 24 #include "base/strings/string_number_conversions.h" // For HexEncode. |
26 #include "base/strings/string_piece.h" | |
27 #include "base/strings/string_util.h" // For LowerCaseEqualsASCII. | 25 #include "base/strings/string_util.h" // For LowerCaseEqualsASCII. |
28 #include "base/strings/stringprintf.h" | |
29 #include "base/threading/thread_task_runner_handle.h" | 26 #include "base/threading/thread_task_runner_handle.h" |
30 #include "base/time/clock.h" | 27 #include "base/time/clock.h" |
31 #include "base/trace_event/trace_event.h" | 28 #include "base/trace_event/trace_event.h" |
32 #include "base/values.h" | 29 #include "base/values.h" |
mmenke
2017/03/23 21:39:35
Tiny nit: Is this still used in this file? It wa
scottmg
2017/03/24 22:46:00
Done.
| |
33 #include "net/base/auth.h" | 30 #include "net/base/auth.h" |
34 #include "net/base/load_flags.h" | 31 #include "net/base/load_flags.h" |
35 #include "net/base/load_timing_info.h" | 32 #include "net/base/load_timing_info.h" |
36 #include "net/base/trace_constants.h" | 33 #include "net/base/trace_constants.h" |
37 #include "net/base/upload_data_stream.h" | 34 #include "net/base/upload_data_stream.h" |
38 #include "net/cert/cert_status_flags.h" | 35 #include "net/cert/cert_status_flags.h" |
39 #include "net/cert/x509_certificate.h" | 36 #include "net/cert/x509_certificate.h" |
40 #include "net/disk_cache/disk_cache.h" | 37 #include "net/disk_cache/disk_cache.h" |
41 #include "net/http/http_network_session.h" | 38 #include "net/http/http_network_session.h" |
42 #include "net/http/http_request_info.h" | 39 #include "net/http/http_request_info.h" |
43 #include "net/http/http_util.h" | 40 #include "net/http/http_util.h" |
44 #include "net/log/net_log_event_type.h" | 41 #include "net/log/net_log_event_type.h" |
45 #include "net/ssl/ssl_cert_request_info.h" | 42 #include "net/ssl/ssl_cert_request_info.h" |
46 #include "net/ssl/ssl_config_service.h" | 43 #include "net/ssl/ssl_config_service.h" |
47 | 44 |
48 using base::Time; | 45 using base::Time; |
49 using base::TimeDelta; | 46 using base::TimeDelta; |
50 using base::TimeTicks; | 47 using base::TimeTicks; |
51 | 48 |
52 namespace net { | 49 namespace net { |
53 | 50 |
54 using CacheEntryStatus = HttpResponseInfo::CacheEntryStatus; | 51 using CacheEntryStatus = HttpResponseInfo::CacheEntryStatus; |
55 | 52 |
56 namespace { | 53 namespace { |
57 | 54 |
58 // TODO(ricea): Move this to HttpResponseHeaders once it is standardised. | |
59 static const char kFreshnessHeader[] = "Resource-Freshness"; | |
60 | |
61 // 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 |
62 // 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 |
63 // (Redirection) status code. | 57 // (Redirection) status code. |
64 bool NonErrorResponse(int status_code) { | 58 bool NonErrorResponse(int status_code) { |
65 int status_code_range = status_code / 100; | 59 int status_code_range = status_code / 100; |
66 return status_code_range == 2 || status_code_range == 3; | 60 return status_code_range == 2 || status_code_range == 3; |
67 } | 61 } |
68 | 62 |
69 void RecordNoStoreHeaderHistogram(int load_flags, | 63 void RecordNoStoreHeaderHistogram(int load_flags, |
70 const HttpResponseInfo* response) { | 64 const HttpResponseInfo* response) { |
71 if (load_flags & LOAD_MAIN_FRAME_DEPRECATED) { | 65 if (load_flags & LOAD_MAIN_FRAME_DEPRECATED) { |
72 UMA_HISTOGRAM_BOOLEAN( | 66 UMA_HISTOGRAM_BOOLEAN( |
73 "Net.MainFrameNoStore", | 67 "Net.MainFrameNoStore", |
74 response->headers->HasHeaderValue("cache-control", "no-store")); | 68 response->headers->HasHeaderValue("cache-control", "no-store")); |
75 } | 69 } |
76 } | 70 } |
77 | 71 |
78 enum ExternallyConditionalizedType { | 72 enum ExternallyConditionalizedType { |
79 EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION, | 73 EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION, |
80 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE, | 74 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE, |
81 EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS, | 75 EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS, |
82 EXTERNALLY_CONDITIONALIZED_MAX | 76 EXTERNALLY_CONDITIONALIZED_MAX |
83 }; | 77 }; |
mmenke
2017/03/23 21:39:35
This enum (And the related histogram) look to have
scottmg
2017/03/24 22:45:59
Aha, good catch. Done.
| |
84 | 78 |
85 } // namespace | 79 } // namespace |
86 | 80 |
87 #define CACHE_STATUS_HISTOGRAMS(type) \ | 81 #define CACHE_STATUS_HISTOGRAMS(type) \ |
88 do { \ | 82 do { \ |
89 UMA_HISTOGRAM_ENUMERATION("HttpCache.Pattern" type, cache_entry_status_, \ | 83 UMA_HISTOGRAM_ENUMERATION("HttpCache.Pattern" type, cache_entry_status_, \ |
90 CacheEntryStatus::ENTRY_MAX); \ | 84 CacheEntryStatus::ENTRY_MAX); \ |
91 if (validation_request) { \ | 85 if (validation_request) { \ |
92 UMA_HISTOGRAM_ENUMERATION("HttpCache.ValidationCause" type, \ | 86 UMA_HISTOGRAM_ENUMERATION("HttpCache.ValidationCause" type, \ |
93 validation_cause_, VALIDATION_CAUSE_MAX); \ | 87 validation_cause_, VALIDATION_CAUSE_MAX); \ |
(...skipping 2000 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2094 next_state_ = STATE_NONE; | 2088 next_state_ = STATE_NONE; |
2095 return ERR_CACHE_MISS; | 2089 return ERR_CACHE_MISS; |
2096 } | 2090 } |
2097 | 2091 |
2098 // We don't have the whole resource. | 2092 // We don't have the whole resource. |
2099 if (truncated_) { | 2093 if (truncated_) { |
2100 next_state_ = STATE_NONE; | 2094 next_state_ = STATE_NONE; |
2101 return ERR_CACHE_MISS; | 2095 return ERR_CACHE_MISS; |
2102 } | 2096 } |
2103 | 2097 |
2104 if (RequiresValidation() != VALIDATION_NONE) { | 2098 if (RequiresValidation()) { |
2105 next_state_ = STATE_NONE; | 2099 next_state_ = STATE_NONE; |
2106 return ERR_CACHE_MISS; | 2100 return ERR_CACHE_MISS; |
2107 } | 2101 } |
2108 | 2102 |
2109 if (request_->method == "HEAD") | 2103 if (request_->method == "HEAD") |
2110 FixHeadersForHead(); | 2104 FixHeadersForHead(); |
2111 | 2105 |
2112 if (entry_->disk_entry->GetDataSize(kMetadataIndex)) | 2106 if (entry_->disk_entry->GetDataSize(kMetadataIndex)) |
2113 next_state_ = STATE_CACHE_READ_METADATA; | 2107 next_state_ = STATE_CACHE_READ_METADATA; |
2114 else | 2108 else |
2115 next_state_ = STATE_NONE; | 2109 next_state_ = STATE_NONE; |
2116 | 2110 |
2117 return OK; | 2111 return OK; |
2118 } | 2112 } |
2119 | 2113 |
2120 int HttpCache::Transaction::BeginCacheValidation() { | 2114 int HttpCache::Transaction::BeginCacheValidation() { |
2121 DCHECK_EQ(mode_, READ_WRITE); | 2115 DCHECK_EQ(mode_, READ_WRITE); |
2122 | 2116 |
2123 ValidationType required_validation = RequiresValidation(); | 2117 bool skip_validation = !RequiresValidation(); |
2124 | |
2125 bool skip_validation = (required_validation == VALIDATION_NONE); | |
2126 | |
2127 if ((effective_load_flags_ & LOAD_SUPPORT_ASYNC_REVALIDATION) && | |
2128 required_validation == VALIDATION_ASYNCHRONOUS) { | |
2129 DCHECK_EQ(request_->method, "GET"); | |
2130 skip_validation = true; | |
2131 response_.async_revalidation_required = true; | |
2132 } | |
2133 | 2118 |
2134 if (request_->method == "HEAD" && | 2119 if (request_->method == "HEAD" && |
2135 (truncated_ || response_.headers->response_code() == 206)) { | 2120 (truncated_ || response_.headers->response_code() == 206)) { |
2136 DCHECK(!partial_); | 2121 DCHECK(!partial_); |
2137 if (skip_validation) | 2122 if (skip_validation) |
2138 return SetupEntryForRead(); | 2123 return SetupEntryForRead(); |
2139 | 2124 |
2140 // Bail out! | 2125 // Bail out! |
2141 next_state_ = STATE_SEND_REQUEST; | 2126 next_state_ = STATE_SEND_REQUEST; |
2142 mode_ = NONE; | 2127 mode_ = NONE; |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2251 } | 2236 } |
2252 } | 2237 } |
2253 | 2238 |
2254 // TODO(ricea): This calculation is expensive to perform just to collect | 2239 // TODO(ricea): This calculation is expensive to perform just to collect |
2255 // statistics. Either remove it or use the result, depending on the result of | 2240 // statistics. Either remove it or use the result, depending on the result of |
2256 // the experiment. | 2241 // the experiment. |
2257 ExternallyConditionalizedType type = | 2242 ExternallyConditionalizedType type = |
2258 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE; | 2243 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE; |
2259 if (mode_ == NONE) | 2244 if (mode_ == NONE) |
2260 type = EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS; | 2245 type = EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS; |
2261 else if (RequiresValidation() != VALIDATION_NONE) | 2246 else if (RequiresValidation()) |
2262 type = EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION; | 2247 type = EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION; |
2263 | 2248 |
2264 // TODO(ricea): Add CACHE_USABLE_STALE once stale-while-revalidate CL landed. | |
2265 // TODO(ricea): Either remove this histogram or make it permanent by M40. | 2249 // TODO(ricea): Either remove this histogram or make it permanent by M40. |
2266 UMA_HISTOGRAM_ENUMERATION("HttpCache.ExternallyConditionalized", | 2250 UMA_HISTOGRAM_ENUMERATION("HttpCache.ExternallyConditionalized", |
2267 type, | 2251 type, |
2268 EXTERNALLY_CONDITIONALIZED_MAX); | 2252 EXTERNALLY_CONDITIONALIZED_MAX); |
2269 | 2253 |
2270 next_state_ = STATE_SEND_REQUEST; | 2254 next_state_ = STATE_SEND_REQUEST; |
2271 return OK; | 2255 return OK; |
2272 } | 2256 } |
2273 | 2257 |
2274 int HttpCache::Transaction::RestartNetworkRequest() { | 2258 int HttpCache::Transaction::RestartNetworkRequest() { |
(...skipping 29 matching lines...) Expand all Loading... | |
2304 DCHECK(network_trans_.get()); | 2288 DCHECK(network_trans_.get()); |
2305 DCHECK_EQ(STATE_NONE, next_state_); | 2289 DCHECK_EQ(STATE_NONE, next_state_); |
2306 | 2290 |
2307 next_state_ = STATE_SEND_REQUEST_COMPLETE; | 2291 next_state_ = STATE_SEND_REQUEST_COMPLETE; |
2308 int rv = network_trans_->RestartWithAuth(credentials, io_callback_); | 2292 int rv = network_trans_->RestartWithAuth(credentials, io_callback_); |
2309 if (rv != ERR_IO_PENDING) | 2293 if (rv != ERR_IO_PENDING) |
2310 return DoLoop(rv); | 2294 return DoLoop(rv); |
2311 return rv; | 2295 return rv; |
2312 } | 2296 } |
2313 | 2297 |
2314 ValidationType HttpCache::Transaction::RequiresValidation() { | 2298 bool HttpCache::Transaction::RequiresValidation() { |
2315 // TODO(darin): need to do more work here: | 2299 // TODO(darin): need to do more work here: |
2316 // - make sure we have a matching request method | 2300 // - make sure we have a matching request method |
2317 // - watch out for cached responses that depend on authentication | 2301 // - watch out for cached responses that depend on authentication |
2318 | 2302 |
2319 if (!(effective_load_flags_ & LOAD_SKIP_VARY_CHECK) && | 2303 if (!(effective_load_flags_ & LOAD_SKIP_VARY_CHECK) && |
2320 response_.vary_data.is_valid() && | 2304 response_.vary_data.is_valid() && |
2321 !response_.vary_data.MatchesRequest(*request_, | 2305 !response_.vary_data.MatchesRequest(*request_, |
2322 *response_.headers.get())) { | 2306 *response_.headers.get())) { |
2323 vary_mismatch_ = true; | 2307 vary_mismatch_ = true; |
2324 validation_cause_ = VALIDATION_CAUSE_VARY_MISMATCH; | 2308 validation_cause_ = VALIDATION_CAUSE_VARY_MISMATCH; |
2325 return VALIDATION_SYNCHRONOUS; | 2309 return true; |
2326 } | 2310 } |
2327 | 2311 |
2328 if (effective_load_flags_ & LOAD_SKIP_CACHE_VALIDATION) | 2312 if (effective_load_flags_ & LOAD_SKIP_CACHE_VALIDATION) |
2329 return VALIDATION_NONE; | 2313 return false; |
2330 | 2314 |
2331 if (response_.unused_since_prefetch && | 2315 if (response_.unused_since_prefetch && |
2332 !(effective_load_flags_ & LOAD_PREFETCH) && | 2316 !(effective_load_flags_ & LOAD_PREFETCH) && |
2333 response_.headers->GetCurrentAge( | 2317 response_.headers->GetCurrentAge( |
2334 response_.request_time, response_.response_time, | 2318 response_.request_time, response_.response_time, |
2335 cache_->clock_->Now()) < TimeDelta::FromMinutes(kPrefetchReuseMins)) { | 2319 cache_->clock_->Now()) < TimeDelta::FromMinutes(kPrefetchReuseMins)) { |
2336 // The first use of a resource after prefetch within a short window skips | 2320 // The first use of a resource after prefetch within a short window skips |
2337 // validation. | 2321 // validation. |
2338 return VALIDATION_NONE; | 2322 return false; |
2339 } | 2323 } |
2340 | 2324 |
2341 if (effective_load_flags_ & LOAD_VALIDATE_CACHE) { | 2325 if (effective_load_flags_ & LOAD_VALIDATE_CACHE) { |
2342 validation_cause_ = VALIDATION_CAUSE_VALIDATE_FLAG; | 2326 validation_cause_ = VALIDATION_CAUSE_VALIDATE_FLAG; |
2343 return VALIDATION_SYNCHRONOUS; | 2327 return true; |
2344 } | 2328 } |
2345 | 2329 |
2346 if (request_->method == "PUT" || request_->method == "DELETE") | 2330 if (request_->method == "PUT" || request_->method == "DELETE") |
2347 return VALIDATION_SYNCHRONOUS; | 2331 return true; |
2348 | 2332 |
2349 ValidationType validation_required_by_headers = | 2333 bool validation_required_by_headers = response_.headers->RequiresValidation( |
2350 response_.headers->RequiresValidation(response_.request_time, | 2334 response_.request_time, response_.response_time, cache_->clock_->Now()); |
2351 response_.response_time, | |
2352 cache_->clock_->Now()); | |
2353 | 2335 |
2354 if (validation_required_by_headers != VALIDATION_NONE) { | 2336 if (validation_required_by_headers) { |
2355 HttpResponseHeaders::FreshnessLifetimes lifetimes = | 2337 HttpResponseHeaders::FreshnessLifetimes lifetimes = |
2356 response_.headers->GetFreshnessLifetimes(response_.response_time); | 2338 response_.headers->GetFreshnessLifetimes(response_.response_time); |
2357 if (lifetimes.freshness == base::TimeDelta()) { | 2339 if (lifetimes.freshness == base::TimeDelta()) { |
2358 validation_cause_ = VALIDATION_CAUSE_ZERO_FRESHNESS; | 2340 validation_cause_ = VALIDATION_CAUSE_ZERO_FRESHNESS; |
2359 } else { | 2341 } else { |
2360 validation_cause_ = VALIDATION_CAUSE_STALE; | 2342 validation_cause_ = VALIDATION_CAUSE_STALE; |
2361 stale_entry_freshness_ = lifetimes.freshness; | 2343 stale_entry_freshness_ = lifetimes.freshness; |
2362 stale_entry_age_ = response_.headers->GetCurrentAge( | 2344 stale_entry_age_ = response_.headers->GetCurrentAge( |
2363 response_.request_time, response_.response_time, | 2345 response_.request_time, response_.response_time, |
2364 cache_->clock_->Now()); | 2346 cache_->clock_->Now()); |
2365 } | 2347 } |
2366 } | 2348 } |
2367 | 2349 |
2368 if (validation_required_by_headers == VALIDATION_ASYNCHRONOUS) { | |
2369 // Asynchronous revalidation is only supported for GET methods. | |
2370 if (request_->method != "GET") | |
2371 return VALIDATION_SYNCHRONOUS; | |
2372 } | |
2373 | |
2374 return validation_required_by_headers; | 2350 return validation_required_by_headers; |
2375 } | 2351 } |
2376 | 2352 |
2377 bool HttpCache::Transaction::ConditionalizeRequest() { | 2353 bool HttpCache::Transaction::ConditionalizeRequest() { |
2378 DCHECK(response_.headers.get()); | 2354 DCHECK(response_.headers.get()); |
2379 | 2355 |
2380 if (request_->method == "PUT" || request_->method == "DELETE") | 2356 if (request_->method == "PUT" || request_->method == "DELETE") |
2381 return false; | 2357 return false; |
2382 | 2358 |
2383 // This only makes sense for cached 200 or 206 responses. | 2359 // This only makes sense for cached 200 or 206 responses. |
(...skipping 27 matching lines...) Expand all Loading... | |
2411 if (!partial_) { | 2387 if (!partial_) { |
2412 // Need to customize the request, so this forces us to allocate :( | 2388 // Need to customize the request, so this forces us to allocate :( |
2413 custom_request_.reset(new HttpRequestInfo(*request_)); | 2389 custom_request_.reset(new HttpRequestInfo(*request_)); |
2414 request_ = custom_request_.get(); | 2390 request_ = custom_request_.get(); |
2415 } | 2391 } |
2416 DCHECK(custom_request_.get()); | 2392 DCHECK(custom_request_.get()); |
2417 | 2393 |
2418 bool use_if_range = | 2394 bool use_if_range = |
2419 partial_ && !partial_->IsCurrentRangeCached() && !invalid_range_; | 2395 partial_ && !partial_->IsCurrentRangeCached() && !invalid_range_; |
2420 | 2396 |
2421 if (!use_if_range) { | |
2422 // stale-while-revalidate is not useful when we only have a partial response | |
2423 // cached, so don't set the header in that case. | |
2424 HttpResponseHeaders::FreshnessLifetimes lifetimes = | |
2425 response_.headers->GetFreshnessLifetimes(response_.response_time); | |
2426 if (lifetimes.staleness > TimeDelta()) { | |
2427 TimeDelta current_age = response_.headers->GetCurrentAge( | |
2428 response_.request_time, response_.response_time, | |
2429 cache_->clock_->Now()); | |
2430 | |
2431 custom_request_->extra_headers.SetHeader( | |
2432 kFreshnessHeader, | |
2433 base::StringPrintf("max-age=%" PRId64 | |
2434 ",stale-while-revalidate=%" PRId64 ",age=%" PRId64, | |
2435 lifetimes.freshness.InSeconds(), | |
2436 lifetimes.staleness.InSeconds(), | |
2437 current_age.InSeconds())); | |
2438 } | |
2439 } | |
2440 | |
2441 if (!etag_value.empty()) { | 2397 if (!etag_value.empty()) { |
2442 if (use_if_range) { | 2398 if (use_if_range) { |
2443 // We don't want to switch to WRITE mode if we don't have this block of a | 2399 // We don't want to switch to WRITE mode if we don't have this block of a |
2444 // byte-range request because we may have other parts cached. | 2400 // byte-range request because we may have other parts cached. |
2445 custom_request_->extra_headers.SetHeader( | 2401 custom_request_->extra_headers.SetHeader( |
2446 HttpRequestHeaders::kIfRange, etag_value); | 2402 HttpRequestHeaders::kIfRange, etag_value); |
2447 } else { | 2403 } else { |
2448 custom_request_->extra_headers.SetHeader( | 2404 custom_request_->extra_headers.SetHeader( |
2449 HttpRequestHeaders::kIfNoneMatch, etag_value); | 2405 HttpRequestHeaders::kIfNoneMatch, etag_value); |
2450 } | 2406 } |
(...skipping 616 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3067 default: | 3023 default: |
3068 NOTREACHED(); | 3024 NOTREACHED(); |
3069 } | 3025 } |
3070 } | 3026 } |
3071 | 3027 |
3072 void HttpCache::Transaction::OnIOComplete(int result) { | 3028 void HttpCache::Transaction::OnIOComplete(int result) { |
3073 DoLoop(result); | 3029 DoLoop(result); |
3074 } | 3030 } |
3075 | 3031 |
3076 } // namespace net | 3032 } // namespace net |
OLD | NEW |