| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/http/http_cache_transaction.h" | |
| 6 | |
| 7 #include "build/build_config.h" | |
| 8 | |
| 9 #if defined(OS_POSIX) | |
| 10 #include <unistd.h> | |
| 11 #endif | |
| 12 | |
| 13 #include <algorithm> | |
| 14 #include <string> | |
| 15 | |
| 16 #include "base/bind.h" | |
| 17 #include "base/compiler_specific.h" | |
| 18 #include "base/format_macros.h" | |
| 19 #include "base/memory/ref_counted.h" | |
| 20 #include "base/memory/scoped_ptr.h" | |
| 21 #include "base/metrics/field_trial.h" | |
| 22 #include "base/metrics/histogram.h" | |
| 23 #include "base/metrics/sparse_histogram.h" | |
| 24 #include "base/profiler/scoped_tracker.h" | |
| 25 #include "base/rand_util.h" | |
| 26 #include "base/strings/string_number_conversions.h" | |
| 27 #include "base/strings/string_piece.h" | |
| 28 #include "base/strings/string_util.h" | |
| 29 #include "base/strings/stringprintf.h" | |
| 30 #include "base/time/clock.h" | |
| 31 #include "base/time/time.h" | |
| 32 #include "base/values.h" | |
| 33 #include "net/base/completion_callback.h" | |
| 34 #include "net/base/io_buffer.h" | |
| 35 #include "net/base/load_flags.h" | |
| 36 #include "net/base/load_timing_info.h" | |
| 37 #include "net/base/net_errors.h" | |
| 38 #include "net/base/net_log.h" | |
| 39 #include "net/base/upload_data_stream.h" | |
| 40 #include "net/cert/cert_status_flags.h" | |
| 41 #include "net/disk_cache/disk_cache.h" | |
| 42 #include "net/http/disk_based_cert_cache.h" | |
| 43 #include "net/http/http_network_session.h" | |
| 44 #include "net/http/http_request_info.h" | |
| 45 #include "net/http/http_response_headers.h" | |
| 46 #include "net/http/http_transaction.h" | |
| 47 #include "net/http/http_util.h" | |
| 48 #include "net/http/partial_data.h" | |
| 49 #include "net/ssl/ssl_cert_request_info.h" | |
| 50 #include "net/ssl/ssl_config_service.h" | |
| 51 | |
| 52 using base::Time; | |
| 53 using base::TimeDelta; | |
| 54 using base::TimeTicks; | |
| 55 | |
| 56 namespace { | |
| 57 | |
| 58 // TODO(ricea): Move this to HttpResponseHeaders once it is standardised. | |
| 59 static const char kFreshnessHeader[] = "Resource-Freshness"; | |
| 60 | |
| 61 // Stores data relevant to the statistics of writing and reading entire | |
| 62 // certificate chains using DiskBasedCertCache. |num_pending_ops| is the number | |
| 63 // of certificates in the chain that have pending operations in the | |
| 64 // DiskBasedCertCache. |start_time| is the time that the read and write | |
| 65 // commands began being issued to the DiskBasedCertCache. | |
| 66 // TODO(brandonsalmon): Remove this when it is no longer necessary to | |
| 67 // collect data. | |
| 68 class SharedChainData : public base::RefCounted<SharedChainData> { | |
| 69 public: | |
| 70 SharedChainData(int num_ops, TimeTicks start) | |
| 71 : num_pending_ops(num_ops), start_time(start) {} | |
| 72 | |
| 73 int num_pending_ops; | |
| 74 TimeTicks start_time; | |
| 75 | |
| 76 private: | |
| 77 friend class base::RefCounted<SharedChainData>; | |
| 78 ~SharedChainData() {} | |
| 79 DISALLOW_COPY_AND_ASSIGN(SharedChainData); | |
| 80 }; | |
| 81 | |
| 82 // Used to obtain a cache entry key for an OSCertHandle. | |
| 83 // TODO(brandonsalmon): Remove this when cache keys are stored | |
| 84 // and no longer have to be recomputed to retrieve the OSCertHandle | |
| 85 // from the disk. | |
| 86 std::string GetCacheKeyForCert(net::X509Certificate::OSCertHandle cert_handle) { | |
| 87 net::SHA1HashValue fingerprint = | |
| 88 net::X509Certificate::CalculateFingerprint(cert_handle); | |
| 89 | |
| 90 return "cert:" + | |
| 91 base::HexEncode(fingerprint.data, arraysize(fingerprint.data)); | |
| 92 } | |
| 93 | |
| 94 // |dist_from_root| indicates the position of the read certificate in the | |
| 95 // certificate chain, 0 indicating it is the root. |is_leaf| indicates | |
| 96 // whether or not the read certificate was the leaf of the chain. | |
| 97 // |shared_chain_data| contains data shared by each certificate in | |
| 98 // the chain. | |
| 99 void OnCertReadIOComplete( | |
| 100 int dist_from_root, | |
| 101 bool is_leaf, | |
| 102 const scoped_refptr<SharedChainData>& shared_chain_data, | |
| 103 net::X509Certificate::OSCertHandle cert_handle) { | |
| 104 // If |num_pending_ops| is one, this was the last pending read operation | |
| 105 // for this chain of certificates. The total time used to read the chain | |
| 106 // can be calculated by subtracting the starting time from Now(). | |
| 107 shared_chain_data->num_pending_ops--; | |
| 108 if (!shared_chain_data->num_pending_ops) { | |
| 109 const TimeDelta read_chain_wait = | |
| 110 TimeTicks::Now() - shared_chain_data->start_time; | |
| 111 UMA_HISTOGRAM_CUSTOM_TIMES("DiskBasedCertCache.ChainReadTime", | |
| 112 read_chain_wait, | |
| 113 base::TimeDelta::FromMilliseconds(1), | |
| 114 base::TimeDelta::FromMinutes(10), | |
| 115 50); | |
| 116 } | |
| 117 | |
| 118 bool success = (cert_handle != NULL); | |
| 119 if (is_leaf) | |
| 120 UMA_HISTOGRAM_BOOLEAN("DiskBasedCertCache.CertIoReadSuccessLeaf", success); | |
| 121 | |
| 122 if (success) | |
| 123 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 124 "DiskBasedCertCache.CertIoReadSuccess", dist_from_root, 0, 10, 7); | |
| 125 else | |
| 126 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 127 "DiskBasedCertCache.CertIoReadFailure", dist_from_root, 0, 10, 7); | |
| 128 } | |
| 129 | |
| 130 // |dist_from_root| indicates the position of the written certificate in the | |
| 131 // certificate chain, 0 indicating it is the root. |is_leaf| indicates | |
| 132 // whether or not the written certificate was the leaf of the chain. | |
| 133 // |shared_chain_data| contains data shared by each certificate in | |
| 134 // the chain. | |
| 135 void OnCertWriteIOComplete( | |
| 136 int dist_from_root, | |
| 137 bool is_leaf, | |
| 138 const scoped_refptr<SharedChainData>& shared_chain_data, | |
| 139 const std::string& key) { | |
| 140 // If |num_pending_ops| is one, this was the last pending write operation | |
| 141 // for this chain of certificates. The total time used to write the chain | |
| 142 // can be calculated by subtracting the starting time from Now(). | |
| 143 shared_chain_data->num_pending_ops--; | |
| 144 if (!shared_chain_data->num_pending_ops) { | |
| 145 const TimeDelta write_chain_wait = | |
| 146 TimeTicks::Now() - shared_chain_data->start_time; | |
| 147 UMA_HISTOGRAM_CUSTOM_TIMES("DiskBasedCertCache.ChainWriteTime", | |
| 148 write_chain_wait, | |
| 149 base::TimeDelta::FromMilliseconds(1), | |
| 150 base::TimeDelta::FromMinutes(10), | |
| 151 50); | |
| 152 } | |
| 153 | |
| 154 bool success = !key.empty(); | |
| 155 if (is_leaf) | |
| 156 UMA_HISTOGRAM_BOOLEAN("DiskBasedCertCache.CertIoWriteSuccessLeaf", success); | |
| 157 | |
| 158 if (success) | |
| 159 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 160 "DiskBasedCertCache.CertIoWriteSuccess", dist_from_root, 0, 10, 7); | |
| 161 else | |
| 162 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 163 "DiskBasedCertCache.CertIoWriteFailure", dist_from_root, 0, 10, 7); | |
| 164 } | |
| 165 | |
| 166 // From http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-21#section-6 | |
| 167 // a "non-error response" is one with a 2xx (Successful) or 3xx | |
| 168 // (Redirection) status code. | |
| 169 bool NonErrorResponse(int status_code) { | |
| 170 int status_code_range = status_code / 100; | |
| 171 return status_code_range == 2 || status_code_range == 3; | |
| 172 } | |
| 173 | |
| 174 // Error codes that will be considered indicative of a page being offline/ | |
| 175 // unreachable for LOAD_FROM_CACHE_IF_OFFLINE. | |
| 176 bool IsOfflineError(int error) { | |
| 177 return (error == net::ERR_NAME_NOT_RESOLVED || | |
| 178 error == net::ERR_INTERNET_DISCONNECTED || | |
| 179 error == net::ERR_ADDRESS_UNREACHABLE || | |
| 180 error == net::ERR_CONNECTION_TIMED_OUT); | |
| 181 } | |
| 182 | |
| 183 // Enum for UMA, indicating the status (with regard to offline mode) of | |
| 184 // a particular request. | |
| 185 enum RequestOfflineStatus { | |
| 186 // A cache transaction hit in cache (data was present and not stale) | |
| 187 // and returned it. | |
| 188 OFFLINE_STATUS_FRESH_CACHE, | |
| 189 | |
| 190 // A network request was required for a cache entry, and it succeeded. | |
| 191 OFFLINE_STATUS_NETWORK_SUCCEEDED, | |
| 192 | |
| 193 // A network request was required for a cache entry, and it failed with | |
| 194 // a non-offline error. | |
| 195 OFFLINE_STATUS_NETWORK_FAILED, | |
| 196 | |
| 197 // A network request was required for a cache entry, it failed with an | |
| 198 // offline error, and we could serve stale data if | |
| 199 // LOAD_FROM_CACHE_IF_OFFLINE was set. | |
| 200 OFFLINE_STATUS_DATA_AVAILABLE_OFFLINE, | |
| 201 | |
| 202 // A network request was required for a cache entry, it failed with | |
| 203 // an offline error, and there was no servable data in cache (even | |
| 204 // stale data). | |
| 205 OFFLINE_STATUS_DATA_UNAVAILABLE_OFFLINE, | |
| 206 | |
| 207 OFFLINE_STATUS_MAX_ENTRIES | |
| 208 }; | |
| 209 | |
| 210 void RecordOfflineStatus(int load_flags, RequestOfflineStatus status) { | |
| 211 // Restrict to main frame to keep statistics close to | |
| 212 // "would have shown them something useful if offline mode was enabled". | |
| 213 if (load_flags & net::LOAD_MAIN_FRAME) { | |
| 214 UMA_HISTOGRAM_ENUMERATION("HttpCache.OfflineStatus", status, | |
| 215 OFFLINE_STATUS_MAX_ENTRIES); | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 void RecordNoStoreHeaderHistogram(int load_flags, | |
| 220 const net::HttpResponseInfo* response) { | |
| 221 if (load_flags & net::LOAD_MAIN_FRAME) { | |
| 222 UMA_HISTOGRAM_BOOLEAN( | |
| 223 "Net.MainFrameNoStore", | |
| 224 response->headers->HasHeaderValue("cache-control", "no-store")); | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 base::Value* NetLogAsyncRevalidationInfoCallback( | |
| 229 const net::NetLog::Source& source, | |
| 230 const net::HttpRequestInfo* request, | |
| 231 net::NetLog::LogLevel log_level) { | |
| 232 base::DictionaryValue* dict = new base::DictionaryValue(); | |
| 233 source.AddToEventParameters(dict); | |
| 234 | |
| 235 dict->SetString("url", request->url.possibly_invalid_spec()); | |
| 236 dict->SetString("method", request->method); | |
| 237 return dict; | |
| 238 } | |
| 239 | |
| 240 enum ExternallyConditionalizedType { | |
| 241 EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION, | |
| 242 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE, | |
| 243 EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS, | |
| 244 EXTERNALLY_CONDITIONALIZED_MAX | |
| 245 }; | |
| 246 | |
| 247 } // namespace | |
| 248 | |
| 249 namespace net { | |
| 250 | |
| 251 struct HeaderNameAndValue { | |
| 252 const char* name; | |
| 253 const char* value; | |
| 254 }; | |
| 255 | |
| 256 // If the request includes one of these request headers, then avoid caching | |
| 257 // to avoid getting confused. | |
| 258 static const HeaderNameAndValue kPassThroughHeaders[] = { | |
| 259 { "if-unmodified-since", NULL }, // causes unexpected 412s | |
| 260 { "if-match", NULL }, // causes unexpected 412s | |
| 261 { "if-range", NULL }, | |
| 262 { NULL, NULL } | |
| 263 }; | |
| 264 | |
| 265 struct ValidationHeaderInfo { | |
| 266 const char* request_header_name; | |
| 267 const char* related_response_header_name; | |
| 268 }; | |
| 269 | |
| 270 static const ValidationHeaderInfo kValidationHeaders[] = { | |
| 271 { "if-modified-since", "last-modified" }, | |
| 272 { "if-none-match", "etag" }, | |
| 273 }; | |
| 274 | |
| 275 // If the request includes one of these request headers, then avoid reusing | |
| 276 // our cached copy if any. | |
| 277 static const HeaderNameAndValue kForceFetchHeaders[] = { | |
| 278 { "cache-control", "no-cache" }, | |
| 279 { "pragma", "no-cache" }, | |
| 280 { NULL, NULL } | |
| 281 }; | |
| 282 | |
| 283 // If the request includes one of these request headers, then force our | |
| 284 // cached copy (if any) to be revalidated before reusing it. | |
| 285 static const HeaderNameAndValue kForceValidateHeaders[] = { | |
| 286 { "cache-control", "max-age=0" }, | |
| 287 { NULL, NULL } | |
| 288 }; | |
| 289 | |
| 290 static bool HeaderMatches(const HttpRequestHeaders& headers, | |
| 291 const HeaderNameAndValue* search) { | |
| 292 for (; search->name; ++search) { | |
| 293 std::string header_value; | |
| 294 if (!headers.GetHeader(search->name, &header_value)) | |
| 295 continue; | |
| 296 | |
| 297 if (!search->value) | |
| 298 return true; | |
| 299 | |
| 300 HttpUtil::ValuesIterator v(header_value.begin(), header_value.end(), ','); | |
| 301 while (v.GetNext()) { | |
| 302 if (LowerCaseEqualsASCII(v.value_begin(), v.value_end(), search->value)) | |
| 303 return true; | |
| 304 } | |
| 305 } | |
| 306 return false; | |
| 307 } | |
| 308 | |
| 309 //----------------------------------------------------------------------------- | |
| 310 | |
| 311 HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) | |
| 312 : next_state_(STATE_NONE), | |
| 313 request_(NULL), | |
| 314 priority_(priority), | |
| 315 cache_(cache->GetWeakPtr()), | |
| 316 entry_(NULL), | |
| 317 new_entry_(NULL), | |
| 318 new_response_(NULL), | |
| 319 mode_(NONE), | |
| 320 target_state_(STATE_NONE), | |
| 321 reading_(false), | |
| 322 invalid_range_(false), | |
| 323 truncated_(false), | |
| 324 is_sparse_(false), | |
| 325 range_requested_(false), | |
| 326 handling_206_(false), | |
| 327 cache_pending_(false), | |
| 328 done_reading_(false), | |
| 329 vary_mismatch_(false), | |
| 330 couldnt_conditionalize_request_(false), | |
| 331 bypass_lock_for_test_(false), | |
| 332 fail_conditionalization_for_test_(false), | |
| 333 io_buf_len_(0), | |
| 334 read_offset_(0), | |
| 335 effective_load_flags_(0), | |
| 336 write_len_(0), | |
| 337 transaction_pattern_(PATTERN_UNDEFINED), | |
| 338 total_received_bytes_(0), | |
| 339 websocket_handshake_stream_base_create_helper_(NULL), | |
| 340 weak_factory_(this) { | |
| 341 static_assert(HttpCache::Transaction::kNumValidationHeaders == | |
| 342 arraysize(kValidationHeaders), | |
| 343 "invalid number of validation headers"); | |
| 344 | |
| 345 io_callback_ = base::Bind(&Transaction::OnIOComplete, | |
| 346 weak_factory_.GetWeakPtr()); | |
| 347 } | |
| 348 | |
| 349 HttpCache::Transaction::~Transaction() { | |
| 350 // We may have to issue another IO, but we should never invoke the callback_ | |
| 351 // after this point. | |
| 352 callback_.Reset(); | |
| 353 | |
| 354 if (cache_) { | |
| 355 if (entry_) { | |
| 356 bool cancel_request = reading_ && response_.headers.get(); | |
| 357 if (cancel_request) { | |
| 358 if (partial_) { | |
| 359 entry_->disk_entry->CancelSparseIO(); | |
| 360 } else { | |
| 361 cancel_request &= (response_.headers->response_code() == 200); | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 cache_->DoneWithEntry(entry_, this, cancel_request); | |
| 366 } else if (cache_pending_) { | |
| 367 cache_->RemovePendingTransaction(this); | |
| 368 } | |
| 369 } | |
| 370 } | |
| 371 | |
| 372 int HttpCache::Transaction::WriteMetadata(IOBuffer* buf, int buf_len, | |
| 373 const CompletionCallback& callback) { | |
| 374 DCHECK(buf); | |
| 375 DCHECK_GT(buf_len, 0); | |
| 376 DCHECK(!callback.is_null()); | |
| 377 if (!cache_.get() || !entry_) | |
| 378 return ERR_UNEXPECTED; | |
| 379 | |
| 380 // We don't need to track this operation for anything. | |
| 381 // It could be possible to check if there is something already written and | |
| 382 // avoid writing again (it should be the same, right?), but let's allow the | |
| 383 // caller to "update" the contents with something new. | |
| 384 return entry_->disk_entry->WriteData(kMetadataIndex, 0, buf, buf_len, | |
| 385 callback, true); | |
| 386 } | |
| 387 | |
| 388 bool HttpCache::Transaction::AddTruncatedFlag() { | |
| 389 DCHECK(mode_ & WRITE || mode_ == NONE); | |
| 390 | |
| 391 // Don't set the flag for sparse entries. | |
| 392 if (partial_.get() && !truncated_) | |
| 393 return true; | |
| 394 | |
| 395 if (!CanResume(true)) | |
| 396 return false; | |
| 397 | |
| 398 // We may have received the whole resource already. | |
| 399 if (done_reading_) | |
| 400 return true; | |
| 401 | |
| 402 truncated_ = true; | |
| 403 target_state_ = STATE_NONE; | |
| 404 next_state_ = STATE_CACHE_WRITE_TRUNCATED_RESPONSE; | |
| 405 DoLoop(OK); | |
| 406 return true; | |
| 407 } | |
| 408 | |
| 409 LoadState HttpCache::Transaction::GetWriterLoadState() const { | |
| 410 if (network_trans_.get()) | |
| 411 return network_trans_->GetLoadState(); | |
| 412 if (entry_ || !request_) | |
| 413 return LOAD_STATE_IDLE; | |
| 414 return LOAD_STATE_WAITING_FOR_CACHE; | |
| 415 } | |
| 416 | |
| 417 const BoundNetLog& HttpCache::Transaction::net_log() const { | |
| 418 return net_log_; | |
| 419 } | |
| 420 | |
| 421 int HttpCache::Transaction::Start(const HttpRequestInfo* request, | |
| 422 const CompletionCallback& callback, | |
| 423 const BoundNetLog& net_log) { | |
| 424 DCHECK(request); | |
| 425 DCHECK(!callback.is_null()); | |
| 426 | |
| 427 // Ensure that we only have one asynchronous call at a time. | |
| 428 DCHECK(callback_.is_null()); | |
| 429 DCHECK(!reading_); | |
| 430 DCHECK(!network_trans_.get()); | |
| 431 DCHECK(!entry_); | |
| 432 | |
| 433 if (!cache_.get()) | |
| 434 return ERR_UNEXPECTED; | |
| 435 | |
| 436 SetRequest(net_log, request); | |
| 437 | |
| 438 // We have to wait until the backend is initialized so we start the SM. | |
| 439 next_state_ = STATE_GET_BACKEND; | |
| 440 int rv = DoLoop(OK); | |
| 441 | |
| 442 // Setting this here allows us to check for the existence of a callback_ to | |
| 443 // determine if we are still inside Start. | |
| 444 if (rv == ERR_IO_PENDING) { | |
| 445 callback_ = tracked_objects::ScopedTracker::TrackCallback( | |
| 446 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 447 "422516 HttpCache::Transaction::Start"), | |
| 448 callback); | |
| 449 } | |
| 450 | |
| 451 return rv; | |
| 452 } | |
| 453 | |
| 454 int HttpCache::Transaction::RestartIgnoringLastError( | |
| 455 const CompletionCallback& callback) { | |
| 456 DCHECK(!callback.is_null()); | |
| 457 | |
| 458 // Ensure that we only have one asynchronous call at a time. | |
| 459 DCHECK(callback_.is_null()); | |
| 460 | |
| 461 if (!cache_.get()) | |
| 462 return ERR_UNEXPECTED; | |
| 463 | |
| 464 int rv = RestartNetworkRequest(); | |
| 465 | |
| 466 if (rv == ERR_IO_PENDING) { | |
| 467 callback_ = tracked_objects::ScopedTracker::TrackCallback( | |
| 468 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 469 "422516 HttpCache::Transaction::RestartIgnoringLastError"), | |
| 470 callback); | |
| 471 } | |
| 472 | |
| 473 return rv; | |
| 474 } | |
| 475 | |
| 476 int HttpCache::Transaction::RestartWithCertificate( | |
| 477 X509Certificate* client_cert, | |
| 478 const CompletionCallback& callback) { | |
| 479 DCHECK(!callback.is_null()); | |
| 480 | |
| 481 // Ensure that we only have one asynchronous call at a time. | |
| 482 DCHECK(callback_.is_null()); | |
| 483 | |
| 484 if (!cache_.get()) | |
| 485 return ERR_UNEXPECTED; | |
| 486 | |
| 487 int rv = RestartNetworkRequestWithCertificate(client_cert); | |
| 488 | |
| 489 if (rv == ERR_IO_PENDING) { | |
| 490 callback_ = tracked_objects::ScopedTracker::TrackCallback( | |
| 491 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 492 "422516 HttpCache::Transaction::RestartWithCertificate"), | |
| 493 callback); | |
| 494 } | |
| 495 | |
| 496 return rv; | |
| 497 } | |
| 498 | |
| 499 int HttpCache::Transaction::RestartWithAuth( | |
| 500 const AuthCredentials& credentials, | |
| 501 const CompletionCallback& callback) { | |
| 502 DCHECK(auth_response_.headers.get()); | |
| 503 DCHECK(!callback.is_null()); | |
| 504 | |
| 505 // Ensure that we only have one asynchronous call at a time. | |
| 506 DCHECK(callback_.is_null()); | |
| 507 | |
| 508 if (!cache_.get()) | |
| 509 return ERR_UNEXPECTED; | |
| 510 | |
| 511 // Clear the intermediate response since we are going to start over. | |
| 512 auth_response_ = HttpResponseInfo(); | |
| 513 | |
| 514 int rv = RestartNetworkRequestWithAuth(credentials); | |
| 515 | |
| 516 if (rv == ERR_IO_PENDING) { | |
| 517 callback_ = tracked_objects::ScopedTracker::TrackCallback( | |
| 518 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 519 "422516 HttpCache::Transaction::RestartWithAuth"), | |
| 520 callback); | |
| 521 } | |
| 522 | |
| 523 return rv; | |
| 524 } | |
| 525 | |
| 526 bool HttpCache::Transaction::IsReadyToRestartForAuth() { | |
| 527 if (!network_trans_.get()) | |
| 528 return false; | |
| 529 return network_trans_->IsReadyToRestartForAuth(); | |
| 530 } | |
| 531 | |
| 532 int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len, | |
| 533 const CompletionCallback& callback) { | |
| 534 DCHECK(buf); | |
| 535 DCHECK_GT(buf_len, 0); | |
| 536 DCHECK(!callback.is_null()); | |
| 537 | |
| 538 DCHECK(callback_.is_null()); | |
| 539 | |
| 540 if (!cache_.get()) | |
| 541 return ERR_UNEXPECTED; | |
| 542 | |
| 543 // If we have an intermediate auth response at this point, then it means the | |
| 544 // user wishes to read the network response (the error page). If there is a | |
| 545 // previous response in the cache then we should leave it intact. | |
| 546 if (auth_response_.headers.get() && mode_ != NONE) { | |
| 547 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 548 DCHECK(mode_ & WRITE); | |
| 549 DoneWritingToEntry(mode_ == READ_WRITE); | |
| 550 mode_ = NONE; | |
| 551 } | |
| 552 | |
| 553 reading_ = true; | |
| 554 int rv; | |
| 555 | |
| 556 switch (mode_) { | |
| 557 case READ_WRITE: | |
| 558 DCHECK(partial_.get()); | |
| 559 if (!network_trans_.get()) { | |
| 560 // We are just reading from the cache, but we may be writing later. | |
| 561 rv = ReadFromEntry(buf, buf_len); | |
| 562 break; | |
| 563 } | |
| 564 case NONE: | |
| 565 case WRITE: | |
| 566 DCHECK(network_trans_.get()); | |
| 567 rv = ReadFromNetwork(buf, buf_len); | |
| 568 break; | |
| 569 case READ: | |
| 570 rv = ReadFromEntry(buf, buf_len); | |
| 571 break; | |
| 572 default: | |
| 573 NOTREACHED(); | |
| 574 rv = ERR_FAILED; | |
| 575 } | |
| 576 | |
| 577 if (rv == ERR_IO_PENDING) { | |
| 578 DCHECK(callback_.is_null()); | |
| 579 callback_ = tracked_objects::ScopedTracker::TrackCallback( | |
| 580 FROM_HERE_WITH_EXPLICIT_FUNCTION("422516 HttpCache::Transaction::Read"), | |
| 581 callback); | |
| 582 } | |
| 583 return rv; | |
| 584 } | |
| 585 | |
| 586 void HttpCache::Transaction::StopCaching() { | |
| 587 // We really don't know where we are now. Hopefully there is no operation in | |
| 588 // progress, but nothing really prevents this method to be called after we | |
| 589 // returned ERR_IO_PENDING. We cannot attempt to truncate the entry at this | |
| 590 // point because we need the state machine for that (and even if we are really | |
| 591 // free, that would be an asynchronous operation). In other words, keep the | |
| 592 // entry how it is (it will be marked as truncated at destruction), and let | |
| 593 // the next piece of code that executes know that we are now reading directly | |
| 594 // from the net. | |
| 595 // TODO(mmenke): This doesn't release the lock on the cache entry, so a | |
| 596 // future request for the resource will be blocked on this one. | |
| 597 // Fix this. | |
| 598 if (cache_.get() && entry_ && (mode_ & WRITE) && network_trans_.get() && | |
| 599 !is_sparse_ && !range_requested_) { | |
| 600 mode_ = NONE; | |
| 601 } | |
| 602 } | |
| 603 | |
| 604 bool HttpCache::Transaction::GetFullRequestHeaders( | |
| 605 HttpRequestHeaders* headers) const { | |
| 606 if (network_trans_) | |
| 607 return network_trans_->GetFullRequestHeaders(headers); | |
| 608 | |
| 609 // TODO(ttuttle): Read headers from cache. | |
| 610 return false; | |
| 611 } | |
| 612 | |
| 613 int64 HttpCache::Transaction::GetTotalReceivedBytes() const { | |
| 614 int64 total_received_bytes = total_received_bytes_; | |
| 615 if (network_trans_) | |
| 616 total_received_bytes += network_trans_->GetTotalReceivedBytes(); | |
| 617 return total_received_bytes; | |
| 618 } | |
| 619 | |
| 620 void HttpCache::Transaction::DoneReading() { | |
| 621 if (cache_.get() && entry_) { | |
| 622 DCHECK_NE(mode_, UPDATE); | |
| 623 if (mode_ & WRITE) { | |
| 624 DoneWritingToEntry(true); | |
| 625 } else if (mode_ & READ) { | |
| 626 // It is necessary to check mode_ & READ because it is possible | |
| 627 // for mode_ to be NONE and entry_ non-NULL with a write entry | |
| 628 // if StopCaching was called. | |
| 629 cache_->DoneReadingFromEntry(entry_, this); | |
| 630 entry_ = NULL; | |
| 631 } | |
| 632 } | |
| 633 } | |
| 634 | |
| 635 const HttpResponseInfo* HttpCache::Transaction::GetResponseInfo() const { | |
| 636 // Null headers means we encountered an error or haven't a response yet | |
| 637 if (auth_response_.headers.get()) | |
| 638 return &auth_response_; | |
| 639 return (response_.headers.get() || response_.ssl_info.cert.get() || | |
| 640 response_.cert_request_info.get()) | |
| 641 ? &response_ | |
| 642 : NULL; | |
| 643 } | |
| 644 | |
| 645 LoadState HttpCache::Transaction::GetLoadState() const { | |
| 646 LoadState state = GetWriterLoadState(); | |
| 647 if (state != LOAD_STATE_WAITING_FOR_CACHE) | |
| 648 return state; | |
| 649 | |
| 650 if (cache_.get()) | |
| 651 return cache_->GetLoadStateForPendingTransaction(this); | |
| 652 | |
| 653 return LOAD_STATE_IDLE; | |
| 654 } | |
| 655 | |
| 656 UploadProgress HttpCache::Transaction::GetUploadProgress() const { | |
| 657 if (network_trans_.get()) | |
| 658 return network_trans_->GetUploadProgress(); | |
| 659 return final_upload_progress_; | |
| 660 } | |
| 661 | |
| 662 void HttpCache::Transaction::SetQuicServerInfo( | |
| 663 QuicServerInfo* quic_server_info) {} | |
| 664 | |
| 665 bool HttpCache::Transaction::GetLoadTimingInfo( | |
| 666 LoadTimingInfo* load_timing_info) const { | |
| 667 if (network_trans_) | |
| 668 return network_trans_->GetLoadTimingInfo(load_timing_info); | |
| 669 | |
| 670 if (old_network_trans_load_timing_) { | |
| 671 *load_timing_info = *old_network_trans_load_timing_; | |
| 672 return true; | |
| 673 } | |
| 674 | |
| 675 if (first_cache_access_since_.is_null()) | |
| 676 return false; | |
| 677 | |
| 678 // If the cache entry was opened, return that time. | |
| 679 load_timing_info->send_start = first_cache_access_since_; | |
| 680 // This time doesn't make much sense when reading from the cache, so just use | |
| 681 // the same time as send_start. | |
| 682 load_timing_info->send_end = first_cache_access_since_; | |
| 683 return true; | |
| 684 } | |
| 685 | |
| 686 void HttpCache::Transaction::SetPriority(RequestPriority priority) { | |
| 687 priority_ = priority; | |
| 688 if (network_trans_) | |
| 689 network_trans_->SetPriority(priority_); | |
| 690 } | |
| 691 | |
| 692 void HttpCache::Transaction::SetWebSocketHandshakeStreamCreateHelper( | |
| 693 WebSocketHandshakeStreamBase::CreateHelper* create_helper) { | |
| 694 websocket_handshake_stream_base_create_helper_ = create_helper; | |
| 695 if (network_trans_) | |
| 696 network_trans_->SetWebSocketHandshakeStreamCreateHelper(create_helper); | |
| 697 } | |
| 698 | |
| 699 void HttpCache::Transaction::SetBeforeNetworkStartCallback( | |
| 700 const BeforeNetworkStartCallback& callback) { | |
| 701 DCHECK(!network_trans_); | |
| 702 before_network_start_callback_ = callback; | |
| 703 } | |
| 704 | |
| 705 void HttpCache::Transaction::SetBeforeProxyHeadersSentCallback( | |
| 706 const BeforeProxyHeadersSentCallback& callback) { | |
| 707 DCHECK(!network_trans_); | |
| 708 before_proxy_headers_sent_callback_ = callback; | |
| 709 } | |
| 710 | |
| 711 int HttpCache::Transaction::ResumeNetworkStart() { | |
| 712 if (network_trans_) | |
| 713 return network_trans_->ResumeNetworkStart(); | |
| 714 return ERR_UNEXPECTED; | |
| 715 } | |
| 716 | |
| 717 //----------------------------------------------------------------------------- | |
| 718 | |
| 719 void HttpCache::Transaction::DoCallback(int rv) { | |
| 720 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 721 tracked_objects::ScopedTracker tracking_profile( | |
| 722 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 723 "422516 HttpCache::Transaction::DoCallback")); | |
| 724 | |
| 725 DCHECK(rv != ERR_IO_PENDING); | |
| 726 DCHECK(!callback_.is_null()); | |
| 727 | |
| 728 read_buf_ = NULL; // Release the buffer before invoking the callback. | |
| 729 | |
| 730 // Since Run may result in Read being called, clear callback_ up front. | |
| 731 CompletionCallback c = callback_; | |
| 732 callback_.Reset(); | |
| 733 c.Run(rv); | |
| 734 } | |
| 735 | |
| 736 int HttpCache::Transaction::HandleResult(int rv) { | |
| 737 DCHECK(rv != ERR_IO_PENDING); | |
| 738 if (!callback_.is_null()) | |
| 739 DoCallback(rv); | |
| 740 | |
| 741 return rv; | |
| 742 } | |
| 743 | |
| 744 // A few common patterns: (Foo* means Foo -> FooComplete) | |
| 745 // | |
| 746 // 1. Not-cached entry: | |
| 747 // Start(): | |
| 748 // GetBackend* -> InitEntry -> OpenEntry* -> CreateEntry* -> AddToEntry* -> | |
| 749 // SendRequest* -> SuccessfulSendRequest -> OverwriteCachedResponse -> | |
| 750 // CacheWriteResponse* -> TruncateCachedData* -> TruncateCachedMetadata* -> | |
| 751 // PartialHeadersReceived | |
| 752 // | |
| 753 // Read(): | |
| 754 // NetworkRead* -> CacheWriteData* | |
| 755 // | |
| 756 // 2. Cached entry, no validation: | |
| 757 // Start(): | |
| 758 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
| 759 // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> | |
| 760 // BeginCacheValidation() -> SetupEntryForRead() | |
| 761 // | |
| 762 // Read(): | |
| 763 // CacheReadData* | |
| 764 // | |
| 765 // 3. Cached entry, validation (304): | |
| 766 // Start(): | |
| 767 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
| 768 // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> | |
| 769 // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest -> | |
| 770 // UpdateCachedResponse -> CacheWriteResponse* -> UpdateCachedResponseComplete | |
| 771 // -> OverwriteCachedResponse -> PartialHeadersReceived | |
| 772 // | |
| 773 // Read(): | |
| 774 // CacheReadData* | |
| 775 // | |
| 776 // 4. Cached entry, validation and replace (200): | |
| 777 // Start(): | |
| 778 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
| 779 // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> | |
| 780 // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest -> | |
| 781 // OverwriteCachedResponse -> CacheWriteResponse* -> DoTruncateCachedData* -> | |
| 782 // TruncateCachedMetadata* -> PartialHeadersReceived | |
| 783 // | |
| 784 // Read(): | |
| 785 // NetworkRead* -> CacheWriteData* | |
| 786 // | |
| 787 // 5. Sparse entry, partially cached, byte range request: | |
| 788 // Start(): | |
| 789 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
| 790 // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> | |
| 791 // CacheQueryData* -> ValidateEntryHeadersAndContinue() -> | |
| 792 // StartPartialCacheValidation -> CompletePartialCacheValidation -> | |
| 793 // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest -> | |
| 794 // UpdateCachedResponse -> CacheWriteResponse* -> UpdateCachedResponseComplete | |
| 795 // -> OverwriteCachedResponse -> PartialHeadersReceived | |
| 796 // | |
| 797 // Read() 1: | |
| 798 // NetworkRead* -> CacheWriteData* | |
| 799 // | |
| 800 // Read() 2: | |
| 801 // NetworkRead* -> CacheWriteData* -> StartPartialCacheValidation -> | |
| 802 // CompletePartialCacheValidation -> CacheReadData* -> | |
| 803 // | |
| 804 // Read() 3: | |
| 805 // CacheReadData* -> StartPartialCacheValidation -> | |
| 806 // CompletePartialCacheValidation -> BeginCacheValidation() -> SendRequest* -> | |
| 807 // SuccessfulSendRequest -> UpdateCachedResponse* -> OverwriteCachedResponse | |
| 808 // -> PartialHeadersReceived -> NetworkRead* -> CacheWriteData* | |
| 809 // | |
| 810 // 6. HEAD. Not-cached entry: | |
| 811 // Pass through. Don't save a HEAD by itself. | |
| 812 // Start(): | |
| 813 // GetBackend* -> InitEntry -> OpenEntry* -> SendRequest* | |
| 814 // | |
| 815 // 7. HEAD. Cached entry, no validation: | |
| 816 // Start(): | |
| 817 // The same flow as for a GET request (example #2) | |
| 818 // | |
| 819 // Read(): | |
| 820 // CacheReadData (returns 0) | |
| 821 // | |
| 822 // 8. HEAD. Cached entry, validation (304): | |
| 823 // The request updates the stored headers. | |
| 824 // Start(): Same as for a GET request (example #3) | |
| 825 // | |
| 826 // Read(): | |
| 827 // CacheReadData (returns 0) | |
| 828 // | |
| 829 // 9. HEAD. Cached entry, validation and replace (200): | |
| 830 // Pass through. The request dooms the old entry, as a HEAD won't be stored by | |
| 831 // itself. | |
| 832 // Start(): | |
| 833 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
| 834 // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> | |
| 835 // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest -> | |
| 836 // OverwriteCachedResponse | |
| 837 // | |
| 838 // 10. HEAD. Sparse entry, partially cached: | |
| 839 // Serve the request from the cache, as long as it doesn't require | |
| 840 // revalidation. Ignore missing ranges when deciding to revalidate. If the | |
| 841 // entry requires revalidation, ignore the whole request and go to full pass | |
| 842 // through (the result of the HEAD request will NOT update the entry). | |
| 843 // | |
| 844 // Start(): Basically the same as example 7, as we never create a partial_ | |
| 845 // object for this request. | |
| 846 // | |
| 847 // 11. Prefetch, not-cached entry: | |
| 848 // The same as example 1. The "unused_since_prefetch" bit is stored as true in | |
| 849 // UpdateCachedResponse. | |
| 850 // | |
| 851 // 12. Prefetch, cached entry: | |
| 852 // Like examples 2-4, only CacheToggleUnusedSincePrefetch* is inserted between | |
| 853 // CacheReadResponse* and CacheDispatchValidation if the unused_since_prefetch | |
| 854 // bit is unset. | |
| 855 // | |
| 856 // 13. Cached entry less than 5 minutes old, unused_since_prefetch is true: | |
| 857 // Skip validation, similar to example 2. | |
| 858 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
| 859 // -> CacheToggleUnusedSincePrefetch* -> CacheDispatchValidation -> | |
| 860 // BeginPartialCacheValidation() -> BeginCacheValidation() -> | |
| 861 // SetupEntryForRead() | |
| 862 // | |
| 863 // Read(): | |
| 864 // CacheReadData* | |
| 865 // | |
| 866 // 14. Cached entry more than 5 minutes old, unused_since_prefetch is true: | |
| 867 // Like examples 2-4, only CacheToggleUnusedSincePrefetch* is inserted between | |
| 868 // CacheReadResponse* and CacheDispatchValidation. | |
| 869 int HttpCache::Transaction::DoLoop(int result) { | |
| 870 DCHECK(next_state_ != STATE_NONE); | |
| 871 | |
| 872 int rv = result; | |
| 873 do { | |
| 874 State state = next_state_; | |
| 875 next_state_ = STATE_NONE; | |
| 876 switch (state) { | |
| 877 case STATE_GET_BACKEND: | |
| 878 DCHECK_EQ(OK, rv); | |
| 879 rv = DoGetBackend(); | |
| 880 break; | |
| 881 case STATE_GET_BACKEND_COMPLETE: | |
| 882 rv = DoGetBackendComplete(rv); | |
| 883 break; | |
| 884 case STATE_SEND_REQUEST: | |
| 885 DCHECK_EQ(OK, rv); | |
| 886 rv = DoSendRequest(); | |
| 887 break; | |
| 888 case STATE_SEND_REQUEST_COMPLETE: | |
| 889 rv = DoSendRequestComplete(rv); | |
| 890 break; | |
| 891 case STATE_SUCCESSFUL_SEND_REQUEST: | |
| 892 DCHECK_EQ(OK, rv); | |
| 893 rv = DoSuccessfulSendRequest(); | |
| 894 break; | |
| 895 case STATE_NETWORK_READ: | |
| 896 DCHECK_EQ(OK, rv); | |
| 897 rv = DoNetworkRead(); | |
| 898 break; | |
| 899 case STATE_NETWORK_READ_COMPLETE: | |
| 900 rv = DoNetworkReadComplete(rv); | |
| 901 break; | |
| 902 case STATE_INIT_ENTRY: | |
| 903 DCHECK_EQ(OK, rv); | |
| 904 rv = DoInitEntry(); | |
| 905 break; | |
| 906 case STATE_OPEN_ENTRY: | |
| 907 DCHECK_EQ(OK, rv); | |
| 908 rv = DoOpenEntry(); | |
| 909 break; | |
| 910 case STATE_OPEN_ENTRY_COMPLETE: | |
| 911 rv = DoOpenEntryComplete(rv); | |
| 912 break; | |
| 913 case STATE_CREATE_ENTRY: | |
| 914 DCHECK_EQ(OK, rv); | |
| 915 rv = DoCreateEntry(); | |
| 916 break; | |
| 917 case STATE_CREATE_ENTRY_COMPLETE: | |
| 918 rv = DoCreateEntryComplete(rv); | |
| 919 break; | |
| 920 case STATE_DOOM_ENTRY: | |
| 921 DCHECK_EQ(OK, rv); | |
| 922 rv = DoDoomEntry(); | |
| 923 break; | |
| 924 case STATE_DOOM_ENTRY_COMPLETE: | |
| 925 rv = DoDoomEntryComplete(rv); | |
| 926 break; | |
| 927 case STATE_ADD_TO_ENTRY: | |
| 928 DCHECK_EQ(OK, rv); | |
| 929 rv = DoAddToEntry(); | |
| 930 break; | |
| 931 case STATE_ADD_TO_ENTRY_COMPLETE: | |
| 932 rv = DoAddToEntryComplete(rv); | |
| 933 break; | |
| 934 case STATE_START_PARTIAL_CACHE_VALIDATION: | |
| 935 DCHECK_EQ(OK, rv); | |
| 936 rv = DoStartPartialCacheValidation(); | |
| 937 break; | |
| 938 case STATE_COMPLETE_PARTIAL_CACHE_VALIDATION: | |
| 939 rv = DoCompletePartialCacheValidation(rv); | |
| 940 break; | |
| 941 case STATE_UPDATE_CACHED_RESPONSE: | |
| 942 DCHECK_EQ(OK, rv); | |
| 943 rv = DoUpdateCachedResponse(); | |
| 944 break; | |
| 945 case STATE_UPDATE_CACHED_RESPONSE_COMPLETE: | |
| 946 rv = DoUpdateCachedResponseComplete(rv); | |
| 947 break; | |
| 948 case STATE_OVERWRITE_CACHED_RESPONSE: | |
| 949 DCHECK_EQ(OK, rv); | |
| 950 rv = DoOverwriteCachedResponse(); | |
| 951 break; | |
| 952 case STATE_TRUNCATE_CACHED_DATA: | |
| 953 DCHECK_EQ(OK, rv); | |
| 954 rv = DoTruncateCachedData(); | |
| 955 break; | |
| 956 case STATE_TRUNCATE_CACHED_DATA_COMPLETE: | |
| 957 rv = DoTruncateCachedDataComplete(rv); | |
| 958 break; | |
| 959 case STATE_TRUNCATE_CACHED_METADATA: | |
| 960 DCHECK_EQ(OK, rv); | |
| 961 rv = DoTruncateCachedMetadata(); | |
| 962 break; | |
| 963 case STATE_TRUNCATE_CACHED_METADATA_COMPLETE: | |
| 964 rv = DoTruncateCachedMetadataComplete(rv); | |
| 965 break; | |
| 966 case STATE_PARTIAL_HEADERS_RECEIVED: | |
| 967 DCHECK_EQ(OK, rv); | |
| 968 rv = DoPartialHeadersReceived(); | |
| 969 break; | |
| 970 case STATE_CACHE_READ_RESPONSE: | |
| 971 DCHECK_EQ(OK, rv); | |
| 972 rv = DoCacheReadResponse(); | |
| 973 break; | |
| 974 case STATE_CACHE_READ_RESPONSE_COMPLETE: | |
| 975 rv = DoCacheReadResponseComplete(rv); | |
| 976 break; | |
| 977 case STATE_CACHE_DISPATCH_VALIDATION: | |
| 978 DCHECK_EQ(OK, rv); | |
| 979 rv = DoCacheDispatchValidation(); | |
| 980 break; | |
| 981 case STATE_TOGGLE_UNUSED_SINCE_PREFETCH: | |
| 982 DCHECK_EQ(OK, rv); | |
| 983 rv = DoCacheToggleUnusedSincePrefetch(); | |
| 984 break; | |
| 985 case STATE_TOGGLE_UNUSED_SINCE_PREFETCH_COMPLETE: | |
| 986 rv = DoCacheToggleUnusedSincePrefetchComplete(rv); | |
| 987 break; | |
| 988 case STATE_CACHE_WRITE_RESPONSE: | |
| 989 DCHECK_EQ(OK, rv); | |
| 990 rv = DoCacheWriteResponse(); | |
| 991 break; | |
| 992 case STATE_CACHE_WRITE_TRUNCATED_RESPONSE: | |
| 993 DCHECK_EQ(OK, rv); | |
| 994 rv = DoCacheWriteTruncatedResponse(); | |
| 995 break; | |
| 996 case STATE_CACHE_WRITE_RESPONSE_COMPLETE: | |
| 997 rv = DoCacheWriteResponseComplete(rv); | |
| 998 break; | |
| 999 case STATE_CACHE_READ_METADATA: | |
| 1000 DCHECK_EQ(OK, rv); | |
| 1001 rv = DoCacheReadMetadata(); | |
| 1002 break; | |
| 1003 case STATE_CACHE_READ_METADATA_COMPLETE: | |
| 1004 rv = DoCacheReadMetadataComplete(rv); | |
| 1005 break; | |
| 1006 case STATE_CACHE_QUERY_DATA: | |
| 1007 DCHECK_EQ(OK, rv); | |
| 1008 rv = DoCacheQueryData(); | |
| 1009 break; | |
| 1010 case STATE_CACHE_QUERY_DATA_COMPLETE: | |
| 1011 rv = DoCacheQueryDataComplete(rv); | |
| 1012 break; | |
| 1013 case STATE_CACHE_READ_DATA: | |
| 1014 DCHECK_EQ(OK, rv); | |
| 1015 rv = DoCacheReadData(); | |
| 1016 break; | |
| 1017 case STATE_CACHE_READ_DATA_COMPLETE: | |
| 1018 rv = DoCacheReadDataComplete(rv); | |
| 1019 break; | |
| 1020 case STATE_CACHE_WRITE_DATA: | |
| 1021 rv = DoCacheWriteData(rv); | |
| 1022 break; | |
| 1023 case STATE_CACHE_WRITE_DATA_COMPLETE: | |
| 1024 rv = DoCacheWriteDataComplete(rv); | |
| 1025 break; | |
| 1026 default: | |
| 1027 NOTREACHED() << "bad state"; | |
| 1028 rv = ERR_FAILED; | |
| 1029 break; | |
| 1030 } | |
| 1031 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
| 1032 | |
| 1033 if (rv != ERR_IO_PENDING) | |
| 1034 HandleResult(rv); | |
| 1035 | |
| 1036 return rv; | |
| 1037 } | |
| 1038 | |
| 1039 int HttpCache::Transaction::DoGetBackend() { | |
| 1040 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1041 tracked_objects::ScopedTracker tracking_profile( | |
| 1042 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1043 "422516 HttpCache::Transaction::DoGetBackend")); | |
| 1044 | |
| 1045 cache_pending_ = true; | |
| 1046 next_state_ = STATE_GET_BACKEND_COMPLETE; | |
| 1047 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_GET_BACKEND); | |
| 1048 return cache_->GetBackendForTransaction(this); | |
| 1049 } | |
| 1050 | |
| 1051 int HttpCache::Transaction::DoGetBackendComplete(int result) { | |
| 1052 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1053 tracked_objects::ScopedTracker tracking_profile( | |
| 1054 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1055 "422516 HttpCache::Transaction::DoGetBackendComplete")); | |
| 1056 | |
| 1057 DCHECK(result == OK || result == ERR_FAILED); | |
| 1058 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_GET_BACKEND, | |
| 1059 result); | |
| 1060 cache_pending_ = false; | |
| 1061 | |
| 1062 if (!ShouldPassThrough()) { | |
| 1063 cache_key_ = cache_->GenerateCacheKey(request_); | |
| 1064 | |
| 1065 // Requested cache access mode. | |
| 1066 if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) { | |
| 1067 mode_ = READ; | |
| 1068 } else if (effective_load_flags_ & LOAD_BYPASS_CACHE) { | |
| 1069 mode_ = WRITE; | |
| 1070 } else { | |
| 1071 mode_ = READ_WRITE; | |
| 1072 } | |
| 1073 | |
| 1074 // Downgrade to UPDATE if the request has been externally conditionalized. | |
| 1075 if (external_validation_.initialized) { | |
| 1076 if (mode_ & WRITE) { | |
| 1077 // Strip off the READ_DATA bit (and maybe add back a READ_META bit | |
| 1078 // in case READ was off). | |
| 1079 mode_ = UPDATE; | |
| 1080 } else { | |
| 1081 mode_ = NONE; | |
| 1082 } | |
| 1083 } | |
| 1084 } | |
| 1085 | |
| 1086 // Use PUT and DELETE only to invalidate existing stored entries. | |
| 1087 if ((request_->method == "PUT" || request_->method == "DELETE") && | |
| 1088 mode_ != READ_WRITE && mode_ != WRITE) { | |
| 1089 mode_ = NONE; | |
| 1090 } | |
| 1091 | |
| 1092 // Note that if mode_ == UPDATE (which is tied to external_validation_), the | |
| 1093 // transaction behaves the same for GET and HEAD requests at this point: if it | |
| 1094 // was not modified, the entry is updated and a response is not returned from | |
| 1095 // the cache. If we receive 200, it doesn't matter if there was a validation | |
| 1096 // header or not. | |
| 1097 if (request_->method == "HEAD" && mode_ == WRITE) | |
| 1098 mode_ = NONE; | |
| 1099 | |
| 1100 // If must use cache, then we must fail. This can happen for back/forward | |
| 1101 // navigations to a page generated via a form post. | |
| 1102 if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE) | |
| 1103 return ERR_CACHE_MISS; | |
| 1104 | |
| 1105 if (mode_ == NONE) { | |
| 1106 if (partial_.get()) { | |
| 1107 partial_->RestoreHeaders(&custom_request_->extra_headers); | |
| 1108 partial_.reset(); | |
| 1109 } | |
| 1110 next_state_ = STATE_SEND_REQUEST; | |
| 1111 } else { | |
| 1112 next_state_ = STATE_INIT_ENTRY; | |
| 1113 } | |
| 1114 | |
| 1115 // This is only set if we have something to do with the response. | |
| 1116 range_requested_ = (partial_.get() != NULL); | |
| 1117 | |
| 1118 return OK; | |
| 1119 } | |
| 1120 | |
| 1121 int HttpCache::Transaction::DoSendRequest() { | |
| 1122 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1123 tracked_objects::ScopedTracker tracking_profile( | |
| 1124 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1125 "422516 HttpCache::Transaction::DoSendRequest")); | |
| 1126 | |
| 1127 DCHECK(mode_ & WRITE || mode_ == NONE); | |
| 1128 DCHECK(!network_trans_.get()); | |
| 1129 | |
| 1130 send_request_since_ = TimeTicks::Now(); | |
| 1131 | |
| 1132 // Create a network transaction. | |
| 1133 int rv = cache_->network_layer_->CreateTransaction(priority_, | |
| 1134 &network_trans_); | |
| 1135 if (rv != OK) | |
| 1136 return rv; | |
| 1137 network_trans_->SetBeforeNetworkStartCallback(before_network_start_callback_); | |
| 1138 network_trans_->SetBeforeProxyHeadersSentCallback( | |
| 1139 before_proxy_headers_sent_callback_); | |
| 1140 | |
| 1141 // Old load timing information, if any, is now obsolete. | |
| 1142 old_network_trans_load_timing_.reset(); | |
| 1143 | |
| 1144 if (websocket_handshake_stream_base_create_helper_) | |
| 1145 network_trans_->SetWebSocketHandshakeStreamCreateHelper( | |
| 1146 websocket_handshake_stream_base_create_helper_); | |
| 1147 | |
| 1148 next_state_ = STATE_SEND_REQUEST_COMPLETE; | |
| 1149 rv = network_trans_->Start(request_, io_callback_, net_log_); | |
| 1150 return rv; | |
| 1151 } | |
| 1152 | |
| 1153 int HttpCache::Transaction::DoSendRequestComplete(int result) { | |
| 1154 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1155 tracked_objects::ScopedTracker tracking_profile( | |
| 1156 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1157 "422516 HttpCache::Transaction::DoSendRequestComplete")); | |
| 1158 | |
| 1159 if (!cache_.get()) | |
| 1160 return ERR_UNEXPECTED; | |
| 1161 | |
| 1162 // If requested, and we have a readable cache entry, and we have | |
| 1163 // an error indicating that we're offline as opposed to in contact | |
| 1164 // with a bad server, read from cache anyway. | |
| 1165 if (IsOfflineError(result)) { | |
| 1166 if (mode_ == READ_WRITE && entry_ && !partial_) { | |
| 1167 RecordOfflineStatus(effective_load_flags_, | |
| 1168 OFFLINE_STATUS_DATA_AVAILABLE_OFFLINE); | |
| 1169 if (effective_load_flags_ & LOAD_FROM_CACHE_IF_OFFLINE) { | |
| 1170 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 1171 response_.server_data_unavailable = true; | |
| 1172 return SetupEntryForRead(); | |
| 1173 } | |
| 1174 } else { | |
| 1175 RecordOfflineStatus(effective_load_flags_, | |
| 1176 OFFLINE_STATUS_DATA_UNAVAILABLE_OFFLINE); | |
| 1177 } | |
| 1178 } else { | |
| 1179 RecordOfflineStatus(effective_load_flags_, | |
| 1180 (result == OK ? OFFLINE_STATUS_NETWORK_SUCCEEDED : | |
| 1181 OFFLINE_STATUS_NETWORK_FAILED)); | |
| 1182 } | |
| 1183 | |
| 1184 // If we tried to conditionalize the request and failed, we know | |
| 1185 // we won't be reading from the cache after this point. | |
| 1186 if (couldnt_conditionalize_request_) | |
| 1187 mode_ = WRITE; | |
| 1188 | |
| 1189 if (result == OK) { | |
| 1190 next_state_ = STATE_SUCCESSFUL_SEND_REQUEST; | |
| 1191 return OK; | |
| 1192 } | |
| 1193 | |
| 1194 // Do not record requests that have network errors or restarts. | |
| 1195 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 1196 if (IsCertificateError(result)) { | |
| 1197 const HttpResponseInfo* response = network_trans_->GetResponseInfo(); | |
| 1198 // If we get a certificate error, then there is a certificate in ssl_info, | |
| 1199 // so GetResponseInfo() should never return NULL here. | |
| 1200 DCHECK(response); | |
| 1201 response_.ssl_info = response->ssl_info; | |
| 1202 } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { | |
| 1203 const HttpResponseInfo* response = network_trans_->GetResponseInfo(); | |
| 1204 DCHECK(response); | |
| 1205 response_.cert_request_info = response->cert_request_info; | |
| 1206 } else if (response_.was_cached) { | |
| 1207 DoneWritingToEntry(true); | |
| 1208 } | |
| 1209 return result; | |
| 1210 } | |
| 1211 | |
| 1212 // We received the response headers and there is no error. | |
| 1213 int HttpCache::Transaction::DoSuccessfulSendRequest() { | |
| 1214 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1215 tracked_objects::ScopedTracker tracking_profile( | |
| 1216 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1217 "422516 HttpCache::Transaction::DoSuccessfulSendRequest")); | |
| 1218 | |
| 1219 DCHECK(!new_response_); | |
| 1220 const HttpResponseInfo* new_response = network_trans_->GetResponseInfo(); | |
| 1221 bool authentication_failure = false; | |
| 1222 | |
| 1223 if (new_response->headers->response_code() == 401 || | |
| 1224 new_response->headers->response_code() == 407) { | |
| 1225 auth_response_ = *new_response; | |
| 1226 if (!reading_) | |
| 1227 return OK; | |
| 1228 | |
| 1229 // We initiated a second request the caller doesn't know about. We should be | |
| 1230 // able to authenticate this request because we should have authenticated | |
| 1231 // this URL moments ago. | |
| 1232 if (IsReadyToRestartForAuth()) { | |
| 1233 DCHECK(!response_.auth_challenge.get()); | |
| 1234 next_state_ = STATE_SEND_REQUEST_COMPLETE; | |
| 1235 // In theory we should check to see if there are new cookies, but there | |
| 1236 // is no way to do that from here. | |
| 1237 return network_trans_->RestartWithAuth(AuthCredentials(), io_callback_); | |
| 1238 } | |
| 1239 | |
| 1240 // We have to perform cleanup at this point so that at least the next | |
| 1241 // request can succeed. | |
| 1242 authentication_failure = true; | |
| 1243 if (entry_) | |
| 1244 DoomPartialEntry(false); | |
| 1245 mode_ = NONE; | |
| 1246 partial_.reset(); | |
| 1247 } | |
| 1248 | |
| 1249 new_response_ = new_response; | |
| 1250 if (authentication_failure || | |
| 1251 (!ValidatePartialResponse() && !auth_response_.headers.get())) { | |
| 1252 // Something went wrong with this request and we have to restart it. | |
| 1253 // If we have an authentication response, we are exposed to weird things | |
| 1254 // hapenning if the user cancels the authentication before we receive | |
| 1255 // the new response. | |
| 1256 net_log_.AddEvent(NetLog::TYPE_HTTP_CACHE_RE_SEND_PARTIAL_REQUEST); | |
| 1257 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 1258 response_ = HttpResponseInfo(); | |
| 1259 ResetNetworkTransaction(); | |
| 1260 new_response_ = NULL; | |
| 1261 next_state_ = STATE_SEND_REQUEST; | |
| 1262 return OK; | |
| 1263 } | |
| 1264 | |
| 1265 if (handling_206_ && mode_ == READ_WRITE && !truncated_ && !is_sparse_) { | |
| 1266 // We have stored the full entry, but it changed and the server is | |
| 1267 // sending a range. We have to delete the old entry. | |
| 1268 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 1269 DoneWritingToEntry(false); | |
| 1270 } | |
| 1271 | |
| 1272 if (mode_ == WRITE && | |
| 1273 transaction_pattern_ != PATTERN_ENTRY_CANT_CONDITIONALIZE) { | |
| 1274 UpdateTransactionPattern(PATTERN_ENTRY_NOT_CACHED); | |
| 1275 } | |
| 1276 | |
| 1277 // Invalidate any cached GET with a successful PUT or DELETE. | |
| 1278 if (mode_ == WRITE && | |
| 1279 (request_->method == "PUT" || request_->method == "DELETE")) { | |
| 1280 if (NonErrorResponse(new_response->headers->response_code())) { | |
| 1281 int ret = cache_->DoomEntry(cache_key_, NULL); | |
| 1282 DCHECK_EQ(OK, ret); | |
| 1283 } | |
| 1284 cache_->DoneWritingToEntry(entry_, true); | |
| 1285 entry_ = NULL; | |
| 1286 mode_ = NONE; | |
| 1287 } | |
| 1288 | |
| 1289 // Invalidate any cached GET with a successful POST. | |
| 1290 if (!(effective_load_flags_ & LOAD_DISABLE_CACHE) && | |
| 1291 request_->method == "POST" && | |
| 1292 NonErrorResponse(new_response->headers->response_code())) { | |
| 1293 cache_->DoomMainEntryForUrl(request_->url); | |
| 1294 } | |
| 1295 | |
| 1296 RecordNoStoreHeaderHistogram(request_->load_flags, new_response); | |
| 1297 | |
| 1298 if (new_response_->headers->response_code() == 416 && | |
| 1299 (request_->method == "GET" || request_->method == "POST")) { | |
| 1300 // If there is an active entry it may be destroyed with this transaction. | |
| 1301 response_ = *new_response_; | |
| 1302 return OK; | |
| 1303 } | |
| 1304 | |
| 1305 // Are we expecting a response to a conditional query? | |
| 1306 if (mode_ == READ_WRITE || mode_ == UPDATE) { | |
| 1307 if (new_response->headers->response_code() == 304 || handling_206_) { | |
| 1308 UpdateTransactionPattern(PATTERN_ENTRY_VALIDATED); | |
| 1309 next_state_ = STATE_UPDATE_CACHED_RESPONSE; | |
| 1310 return OK; | |
| 1311 } | |
| 1312 UpdateTransactionPattern(PATTERN_ENTRY_UPDATED); | |
| 1313 mode_ = WRITE; | |
| 1314 } | |
| 1315 | |
| 1316 next_state_ = STATE_OVERWRITE_CACHED_RESPONSE; | |
| 1317 return OK; | |
| 1318 } | |
| 1319 | |
| 1320 int HttpCache::Transaction::DoNetworkRead() { | |
| 1321 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1322 tracked_objects::ScopedTracker tracking_profile( | |
| 1323 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1324 "422516 HttpCache::Transaction::DoNetworkRead")); | |
| 1325 | |
| 1326 next_state_ = STATE_NETWORK_READ_COMPLETE; | |
| 1327 return network_trans_->Read(read_buf_.get(), io_buf_len_, io_callback_); | |
| 1328 } | |
| 1329 | |
| 1330 int HttpCache::Transaction::DoNetworkReadComplete(int result) { | |
| 1331 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1332 tracked_objects::ScopedTracker tracking_profile( | |
| 1333 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1334 "422516 HttpCache::Transaction::DoNetworkReadComplete")); | |
| 1335 | |
| 1336 DCHECK(mode_ & WRITE || mode_ == NONE); | |
| 1337 | |
| 1338 if (!cache_.get()) | |
| 1339 return ERR_UNEXPECTED; | |
| 1340 | |
| 1341 // If there is an error or we aren't saving the data, we are done; just wait | |
| 1342 // until the destructor runs to see if we can keep the data. | |
| 1343 if (mode_ == NONE || result < 0) | |
| 1344 return result; | |
| 1345 | |
| 1346 next_state_ = STATE_CACHE_WRITE_DATA; | |
| 1347 return result; | |
| 1348 } | |
| 1349 | |
| 1350 int HttpCache::Transaction::DoInitEntry() { | |
| 1351 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1352 tracked_objects::ScopedTracker tracking_profile( | |
| 1353 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1354 "422516 HttpCache::Transaction::DoInitEntry")); | |
| 1355 | |
| 1356 DCHECK(!new_entry_); | |
| 1357 | |
| 1358 if (!cache_.get()) | |
| 1359 return ERR_UNEXPECTED; | |
| 1360 | |
| 1361 if (mode_ == WRITE) { | |
| 1362 next_state_ = STATE_DOOM_ENTRY; | |
| 1363 return OK; | |
| 1364 } | |
| 1365 | |
| 1366 next_state_ = STATE_OPEN_ENTRY; | |
| 1367 return OK; | |
| 1368 } | |
| 1369 | |
| 1370 int HttpCache::Transaction::DoOpenEntry() { | |
| 1371 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1372 tracked_objects::ScopedTracker tracking_profile( | |
| 1373 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1374 "422516 HttpCache::Transaction::DoOpenEntry")); | |
| 1375 | |
| 1376 DCHECK(!new_entry_); | |
| 1377 next_state_ = STATE_OPEN_ENTRY_COMPLETE; | |
| 1378 cache_pending_ = true; | |
| 1379 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY); | |
| 1380 first_cache_access_since_ = TimeTicks::Now(); | |
| 1381 return cache_->OpenEntry(cache_key_, &new_entry_, this); | |
| 1382 } | |
| 1383 | |
| 1384 int HttpCache::Transaction::DoOpenEntryComplete(int result) { | |
| 1385 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1386 tracked_objects::ScopedTracker tracking_profile( | |
| 1387 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1388 "422516 HttpCache::Transaction::DoOpenEntryComplete")); | |
| 1389 | |
| 1390 // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is | |
| 1391 // OK, otherwise the cache will end up with an active entry without any | |
| 1392 // transaction attached. | |
| 1393 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY, result); | |
| 1394 cache_pending_ = false; | |
| 1395 if (result == OK) { | |
| 1396 next_state_ = STATE_ADD_TO_ENTRY; | |
| 1397 return OK; | |
| 1398 } | |
| 1399 | |
| 1400 if (result == ERR_CACHE_RACE) { | |
| 1401 next_state_ = STATE_INIT_ENTRY; | |
| 1402 return OK; | |
| 1403 } | |
| 1404 | |
| 1405 if (request_->method == "PUT" || request_->method == "DELETE" || | |
| 1406 (request_->method == "HEAD" && mode_ == READ_WRITE)) { | |
| 1407 DCHECK(mode_ == READ_WRITE || mode_ == WRITE || request_->method == "HEAD"); | |
| 1408 mode_ = NONE; | |
| 1409 next_state_ = STATE_SEND_REQUEST; | |
| 1410 return OK; | |
| 1411 } | |
| 1412 | |
| 1413 if (mode_ == READ_WRITE) { | |
| 1414 mode_ = WRITE; | |
| 1415 next_state_ = STATE_CREATE_ENTRY; | |
| 1416 return OK; | |
| 1417 } | |
| 1418 if (mode_ == UPDATE) { | |
| 1419 // There is no cache entry to update; proceed without caching. | |
| 1420 mode_ = NONE; | |
| 1421 next_state_ = STATE_SEND_REQUEST; | |
| 1422 return OK; | |
| 1423 } | |
| 1424 if (cache_->mode() == PLAYBACK) | |
| 1425 DVLOG(1) << "Playback Cache Miss: " << request_->url; | |
| 1426 | |
| 1427 // The entry does not exist, and we are not permitted to create a new entry, | |
| 1428 // so we must fail. | |
| 1429 return ERR_CACHE_MISS; | |
| 1430 } | |
| 1431 | |
| 1432 int HttpCache::Transaction::DoCreateEntry() { | |
| 1433 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1434 tracked_objects::ScopedTracker tracking_profile( | |
| 1435 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1436 "422516 HttpCache::Transaction::DoCreateEntry")); | |
| 1437 | |
| 1438 DCHECK(!new_entry_); | |
| 1439 next_state_ = STATE_CREATE_ENTRY_COMPLETE; | |
| 1440 cache_pending_ = true; | |
| 1441 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY); | |
| 1442 return cache_->CreateEntry(cache_key_, &new_entry_, this); | |
| 1443 } | |
| 1444 | |
| 1445 int HttpCache::Transaction::DoCreateEntryComplete(int result) { | |
| 1446 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1447 tracked_objects::ScopedTracker tracking_profile( | |
| 1448 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1449 "422516 HttpCache::Transaction::DoCreateEntryComplete")); | |
| 1450 | |
| 1451 // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is | |
| 1452 // OK, otherwise the cache will end up with an active entry without any | |
| 1453 // transaction attached. | |
| 1454 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY, | |
| 1455 result); | |
| 1456 cache_pending_ = false; | |
| 1457 next_state_ = STATE_ADD_TO_ENTRY; | |
| 1458 | |
| 1459 if (result == ERR_CACHE_RACE) { | |
| 1460 next_state_ = STATE_INIT_ENTRY; | |
| 1461 return OK; | |
| 1462 } | |
| 1463 | |
| 1464 if (result != OK) { | |
| 1465 // We have a race here: Maybe we failed to open the entry and decided to | |
| 1466 // create one, but by the time we called create, another transaction already | |
| 1467 // created the entry. If we want to eliminate this issue, we need an atomic | |
| 1468 // OpenOrCreate() method exposed by the disk cache. | |
| 1469 DLOG(WARNING) << "Unable to create cache entry"; | |
| 1470 mode_ = NONE; | |
| 1471 if (partial_.get()) | |
| 1472 partial_->RestoreHeaders(&custom_request_->extra_headers); | |
| 1473 next_state_ = STATE_SEND_REQUEST; | |
| 1474 } | |
| 1475 return OK; | |
| 1476 } | |
| 1477 | |
| 1478 int HttpCache::Transaction::DoDoomEntry() { | |
| 1479 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1480 tracked_objects::ScopedTracker tracking_profile( | |
| 1481 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1482 "422516 HttpCache::Transaction::DoDoomEntry")); | |
| 1483 | |
| 1484 next_state_ = STATE_DOOM_ENTRY_COMPLETE; | |
| 1485 cache_pending_ = true; | |
| 1486 if (first_cache_access_since_.is_null()) | |
| 1487 first_cache_access_since_ = TimeTicks::Now(); | |
| 1488 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY); | |
| 1489 return cache_->DoomEntry(cache_key_, this); | |
| 1490 } | |
| 1491 | |
| 1492 int HttpCache::Transaction::DoDoomEntryComplete(int result) { | |
| 1493 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1494 tracked_objects::ScopedTracker tracking_profile( | |
| 1495 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1496 "422516 HttpCache::Transaction::DoDoomEntryComplete")); | |
| 1497 | |
| 1498 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY, result); | |
| 1499 next_state_ = STATE_CREATE_ENTRY; | |
| 1500 cache_pending_ = false; | |
| 1501 if (result == ERR_CACHE_RACE) | |
| 1502 next_state_ = STATE_INIT_ENTRY; | |
| 1503 return OK; | |
| 1504 } | |
| 1505 | |
| 1506 int HttpCache::Transaction::DoAddToEntry() { | |
| 1507 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1508 tracked_objects::ScopedTracker tracking_profile( | |
| 1509 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1510 "422516 HttpCache::Transaction::DoAddToEntry")); | |
| 1511 | |
| 1512 DCHECK(new_entry_); | |
| 1513 cache_pending_ = true; | |
| 1514 next_state_ = STATE_ADD_TO_ENTRY_COMPLETE; | |
| 1515 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_ADD_TO_ENTRY); | |
| 1516 DCHECK(entry_lock_waiting_since_.is_null()); | |
| 1517 entry_lock_waiting_since_ = TimeTicks::Now(); | |
| 1518 int rv = cache_->AddTransactionToEntry(new_entry_, this); | |
| 1519 if (rv == ERR_IO_PENDING) { | |
| 1520 if (bypass_lock_for_test_) { | |
| 1521 OnAddToEntryTimeout(entry_lock_waiting_since_); | |
| 1522 } else { | |
| 1523 int timeout_milliseconds = 20 * 1000; | |
| 1524 if (partial_ && new_entry_->writer && | |
| 1525 new_entry_->writer->range_requested_) { | |
| 1526 // Quickly timeout and bypass the cache if we're a range request and | |
| 1527 // we're blocked by the reader/writer lock. Doing so eliminates a long | |
| 1528 // running issue, http://crbug.com/31014, where two of the same media | |
| 1529 // resources could not be played back simultaneously due to one locking | |
| 1530 // the cache entry until the entire video was downloaded. | |
| 1531 // | |
| 1532 // Bypassing the cache is not ideal, as we are now ignoring the cache | |
| 1533 // entirely for all range requests to a resource beyond the first. This | |
| 1534 // is however a much more succinct solution than the alternatives, which | |
| 1535 // would require somewhat significant changes to the http caching logic. | |
| 1536 // | |
| 1537 // Allow some timeout slack for the entry addition to complete in case | |
| 1538 // the writer lock is imminently released; we want to avoid skipping | |
| 1539 // the cache if at all possible. See http://crbug.com/408765 | |
| 1540 timeout_milliseconds = 25; | |
| 1541 } | |
| 1542 base::MessageLoop::current()->PostDelayedTask( | |
| 1543 FROM_HERE, | |
| 1544 base::Bind(&HttpCache::Transaction::OnAddToEntryTimeout, | |
| 1545 weak_factory_.GetWeakPtr(), entry_lock_waiting_since_), | |
| 1546 TimeDelta::FromMilliseconds(timeout_milliseconds)); | |
| 1547 } | |
| 1548 } | |
| 1549 return rv; | |
| 1550 } | |
| 1551 | |
| 1552 int HttpCache::Transaction::DoAddToEntryComplete(int result) { | |
| 1553 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1554 tracked_objects::ScopedTracker tracking_profile( | |
| 1555 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1556 "422516 HttpCache::Transaction::DoAddToEntryComplete")); | |
| 1557 | |
| 1558 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_ADD_TO_ENTRY, | |
| 1559 result); | |
| 1560 const TimeDelta entry_lock_wait = | |
| 1561 TimeTicks::Now() - entry_lock_waiting_since_; | |
| 1562 UMA_HISTOGRAM_TIMES("HttpCache.EntryLockWait", entry_lock_wait); | |
| 1563 | |
| 1564 entry_lock_waiting_since_ = TimeTicks(); | |
| 1565 DCHECK(new_entry_); | |
| 1566 cache_pending_ = false; | |
| 1567 | |
| 1568 if (result == OK) | |
| 1569 entry_ = new_entry_; | |
| 1570 | |
| 1571 // If there is a failure, the cache should have taken care of new_entry_. | |
| 1572 new_entry_ = NULL; | |
| 1573 | |
| 1574 if (result == ERR_CACHE_RACE) { | |
| 1575 next_state_ = STATE_INIT_ENTRY; | |
| 1576 return OK; | |
| 1577 } | |
| 1578 | |
| 1579 if (result == ERR_CACHE_LOCK_TIMEOUT) { | |
| 1580 // The cache is busy, bypass it for this transaction. | |
| 1581 mode_ = NONE; | |
| 1582 next_state_ = STATE_SEND_REQUEST; | |
| 1583 if (partial_) { | |
| 1584 partial_->RestoreHeaders(&custom_request_->extra_headers); | |
| 1585 partial_.reset(); | |
| 1586 } | |
| 1587 return OK; | |
| 1588 } | |
| 1589 | |
| 1590 if (result != OK) { | |
| 1591 NOTREACHED(); | |
| 1592 return result; | |
| 1593 } | |
| 1594 | |
| 1595 if (mode_ == WRITE) { | |
| 1596 if (partial_.get()) | |
| 1597 partial_->RestoreHeaders(&custom_request_->extra_headers); | |
| 1598 next_state_ = STATE_SEND_REQUEST; | |
| 1599 } else { | |
| 1600 // We have to read the headers from the cached entry. | |
| 1601 DCHECK(mode_ & READ_META); | |
| 1602 next_state_ = STATE_CACHE_READ_RESPONSE; | |
| 1603 } | |
| 1604 return OK; | |
| 1605 } | |
| 1606 | |
| 1607 // We may end up here multiple times for a given request. | |
| 1608 int HttpCache::Transaction::DoStartPartialCacheValidation() { | |
| 1609 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1610 tracked_objects::ScopedTracker tracking_profile( | |
| 1611 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1612 "422516 HttpCache::Transaction::DoStartPartialCacheValidation")); | |
| 1613 | |
| 1614 if (mode_ == NONE) | |
| 1615 return OK; | |
| 1616 | |
| 1617 next_state_ = STATE_COMPLETE_PARTIAL_CACHE_VALIDATION; | |
| 1618 return partial_->ShouldValidateCache(entry_->disk_entry, io_callback_); | |
| 1619 } | |
| 1620 | |
| 1621 int HttpCache::Transaction::DoCompletePartialCacheValidation(int result) { | |
| 1622 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1623 tracked_objects::ScopedTracker tracking_profile( | |
| 1624 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1625 "422516 HttpCache::Transaction::DoCompletePartialCacheValidation")); | |
| 1626 | |
| 1627 if (!result) { | |
| 1628 // This is the end of the request. | |
| 1629 if (mode_ & WRITE) { | |
| 1630 DoneWritingToEntry(true); | |
| 1631 } else { | |
| 1632 cache_->DoneReadingFromEntry(entry_, this); | |
| 1633 entry_ = NULL; | |
| 1634 } | |
| 1635 return result; | |
| 1636 } | |
| 1637 | |
| 1638 if (result < 0) | |
| 1639 return result; | |
| 1640 | |
| 1641 partial_->PrepareCacheValidation(entry_->disk_entry, | |
| 1642 &custom_request_->extra_headers); | |
| 1643 | |
| 1644 if (reading_ && partial_->IsCurrentRangeCached()) { | |
| 1645 next_state_ = STATE_CACHE_READ_DATA; | |
| 1646 return OK; | |
| 1647 } | |
| 1648 | |
| 1649 return BeginCacheValidation(); | |
| 1650 } | |
| 1651 | |
| 1652 // We received 304 or 206 and we want to update the cached response headers. | |
| 1653 int HttpCache::Transaction::DoUpdateCachedResponse() { | |
| 1654 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1655 tracked_objects::ScopedTracker tracking_profile( | |
| 1656 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1657 "422516 HttpCache::Transaction::DoUpdateCachedResponse")); | |
| 1658 | |
| 1659 next_state_ = STATE_UPDATE_CACHED_RESPONSE_COMPLETE; | |
| 1660 int rv = OK; | |
| 1661 // Update cached response based on headers in new_response. | |
| 1662 // TODO(wtc): should we update cached certificate (response_.ssl_info), too? | |
| 1663 response_.headers->Update(*new_response_->headers.get()); | |
| 1664 response_.response_time = new_response_->response_time; | |
| 1665 response_.request_time = new_response_->request_time; | |
| 1666 response_.network_accessed = new_response_->network_accessed; | |
| 1667 response_.unused_since_prefetch = new_response_->unused_since_prefetch; | |
| 1668 | |
| 1669 if (response_.headers->HasHeaderValue("cache-control", "no-store")) { | |
| 1670 if (!entry_->doomed) { | |
| 1671 int ret = cache_->DoomEntry(cache_key_, NULL); | |
| 1672 DCHECK_EQ(OK, ret); | |
| 1673 } | |
| 1674 } else { | |
| 1675 // If we are already reading, we already updated the headers for this | |
| 1676 // request; doing it again will change Content-Length. | |
| 1677 if (!reading_) { | |
| 1678 target_state_ = STATE_UPDATE_CACHED_RESPONSE_COMPLETE; | |
| 1679 next_state_ = STATE_CACHE_WRITE_RESPONSE; | |
| 1680 rv = OK; | |
| 1681 } | |
| 1682 } | |
| 1683 return rv; | |
| 1684 } | |
| 1685 | |
| 1686 int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) { | |
| 1687 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1688 tracked_objects::ScopedTracker tracking_profile( | |
| 1689 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1690 "422516 HttpCache::Transaction::DoUpdateCachedResponseComplete")); | |
| 1691 | |
| 1692 if (mode_ == UPDATE) { | |
| 1693 DCHECK(!handling_206_); | |
| 1694 // We got a "not modified" response and already updated the corresponding | |
| 1695 // cache entry above. | |
| 1696 // | |
| 1697 // By closing the cached entry now, we make sure that the 304 rather than | |
| 1698 // the cached 200 response, is what will be returned to the user. | |
| 1699 DoneWritingToEntry(true); | |
| 1700 } else if (entry_ && !handling_206_) { | |
| 1701 DCHECK_EQ(READ_WRITE, mode_); | |
| 1702 if (!partial_.get() || partial_->IsLastRange()) { | |
| 1703 cache_->ConvertWriterToReader(entry_); | |
| 1704 mode_ = READ; | |
| 1705 } | |
| 1706 // We no longer need the network transaction, so destroy it. | |
| 1707 final_upload_progress_ = network_trans_->GetUploadProgress(); | |
| 1708 ResetNetworkTransaction(); | |
| 1709 } else if (entry_ && handling_206_ && truncated_ && | |
| 1710 partial_->initial_validation()) { | |
| 1711 // We just finished the validation of a truncated entry, and the server | |
| 1712 // is willing to resume the operation. Now we go back and start serving | |
| 1713 // the first part to the user. | |
| 1714 ResetNetworkTransaction(); | |
| 1715 new_response_ = NULL; | |
| 1716 next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION; | |
| 1717 partial_->SetRangeToStartDownload(); | |
| 1718 return OK; | |
| 1719 } | |
| 1720 next_state_ = STATE_OVERWRITE_CACHED_RESPONSE; | |
| 1721 return OK; | |
| 1722 } | |
| 1723 | |
| 1724 int HttpCache::Transaction::DoOverwriteCachedResponse() { | |
| 1725 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1726 tracked_objects::ScopedTracker tracking_profile( | |
| 1727 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1728 "422516 HttpCache::Transaction::DoOverwriteCachedResponse")); | |
| 1729 | |
| 1730 if (mode_ & READ) { | |
| 1731 next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; | |
| 1732 return OK; | |
| 1733 } | |
| 1734 | |
| 1735 // We change the value of Content-Length for partial content. | |
| 1736 if (handling_206_ && partial_.get()) | |
| 1737 partial_->FixContentLength(new_response_->headers.get()); | |
| 1738 | |
| 1739 response_ = *new_response_; | |
| 1740 | |
| 1741 if (request_->method == "HEAD") { | |
| 1742 // This response is replacing the cached one. | |
| 1743 DoneWritingToEntry(false); | |
| 1744 mode_ = NONE; | |
| 1745 new_response_ = NULL; | |
| 1746 return OK; | |
| 1747 } | |
| 1748 | |
| 1749 if (handling_206_ && !CanResume(false)) { | |
| 1750 // There is no point in storing this resource because it will never be used. | |
| 1751 // This may change if we support LOAD_ONLY_FROM_CACHE with sparse entries. | |
| 1752 DoneWritingToEntry(false); | |
| 1753 if (partial_.get()) | |
| 1754 partial_->FixResponseHeaders(response_.headers.get(), true); | |
| 1755 next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; | |
| 1756 return OK; | |
| 1757 } | |
| 1758 | |
| 1759 target_state_ = STATE_TRUNCATE_CACHED_DATA; | |
| 1760 next_state_ = truncated_ ? STATE_CACHE_WRITE_TRUNCATED_RESPONSE : | |
| 1761 STATE_CACHE_WRITE_RESPONSE; | |
| 1762 return OK; | |
| 1763 } | |
| 1764 | |
| 1765 int HttpCache::Transaction::DoTruncateCachedData() { | |
| 1766 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1767 tracked_objects::ScopedTracker tracking_profile( | |
| 1768 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1769 "422516 HttpCache::Transaction::DoTruncateCachedData")); | |
| 1770 | |
| 1771 next_state_ = STATE_TRUNCATE_CACHED_DATA_COMPLETE; | |
| 1772 if (!entry_) | |
| 1773 return OK; | |
| 1774 if (net_log_.IsLogging()) | |
| 1775 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA); | |
| 1776 // Truncate the stream. | |
| 1777 return WriteToEntry(kResponseContentIndex, 0, NULL, 0, io_callback_); | |
| 1778 } | |
| 1779 | |
| 1780 int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) { | |
| 1781 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1782 tracked_objects::ScopedTracker tracking_profile( | |
| 1783 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1784 "422516 HttpCache::Transaction::DoTruncateCachedDataComplete")); | |
| 1785 | |
| 1786 if (entry_) { | |
| 1787 if (net_log_.IsLogging()) { | |
| 1788 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA, | |
| 1789 result); | |
| 1790 } | |
| 1791 } | |
| 1792 | |
| 1793 next_state_ = STATE_TRUNCATE_CACHED_METADATA; | |
| 1794 return OK; | |
| 1795 } | |
| 1796 | |
| 1797 int HttpCache::Transaction::DoTruncateCachedMetadata() { | |
| 1798 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1799 tracked_objects::ScopedTracker tracking_profile( | |
| 1800 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1801 "422516 HttpCache::Transaction::DoTruncateCachedMetadata")); | |
| 1802 | |
| 1803 next_state_ = STATE_TRUNCATE_CACHED_METADATA_COMPLETE; | |
| 1804 if (!entry_) | |
| 1805 return OK; | |
| 1806 | |
| 1807 if (net_log_.IsLogging()) | |
| 1808 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); | |
| 1809 return WriteToEntry(kMetadataIndex, 0, NULL, 0, io_callback_); | |
| 1810 } | |
| 1811 | |
| 1812 int HttpCache::Transaction::DoTruncateCachedMetadataComplete(int result) { | |
| 1813 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1814 tracked_objects::ScopedTracker tracking_profile( | |
| 1815 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1816 "422516 HttpCache::Transaction::DoTruncateCachedMetadataComplete")); | |
| 1817 | |
| 1818 if (entry_) { | |
| 1819 if (net_log_.IsLogging()) { | |
| 1820 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO, | |
| 1821 result); | |
| 1822 } | |
| 1823 } | |
| 1824 | |
| 1825 next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; | |
| 1826 return OK; | |
| 1827 } | |
| 1828 | |
| 1829 int HttpCache::Transaction::DoPartialHeadersReceived() { | |
| 1830 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1831 tracked_objects::ScopedTracker tracking_profile( | |
| 1832 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1833 "422516 HttpCache::Transaction::DoPartialHeadersReceived")); | |
| 1834 | |
| 1835 new_response_ = NULL; | |
| 1836 if (entry_ && !partial_.get() && | |
| 1837 entry_->disk_entry->GetDataSize(kMetadataIndex)) | |
| 1838 next_state_ = STATE_CACHE_READ_METADATA; | |
| 1839 | |
| 1840 if (!partial_.get()) | |
| 1841 return OK; | |
| 1842 | |
| 1843 if (reading_) { | |
| 1844 if (network_trans_.get()) { | |
| 1845 next_state_ = STATE_NETWORK_READ; | |
| 1846 } else { | |
| 1847 next_state_ = STATE_CACHE_READ_DATA; | |
| 1848 } | |
| 1849 } else if (mode_ != NONE) { | |
| 1850 // We are about to return the headers for a byte-range request to the user, | |
| 1851 // so let's fix them. | |
| 1852 partial_->FixResponseHeaders(response_.headers.get(), true); | |
| 1853 } | |
| 1854 return OK; | |
| 1855 } | |
| 1856 | |
| 1857 int HttpCache::Transaction::DoCacheReadResponse() { | |
| 1858 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1859 tracked_objects::ScopedTracker tracking_profile( | |
| 1860 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1861 "422516 HttpCache::Transaction::DoCacheReadResponse")); | |
| 1862 | |
| 1863 DCHECK(entry_); | |
| 1864 next_state_ = STATE_CACHE_READ_RESPONSE_COMPLETE; | |
| 1865 | |
| 1866 io_buf_len_ = entry_->disk_entry->GetDataSize(kResponseInfoIndex); | |
| 1867 read_buf_ = new IOBuffer(io_buf_len_); | |
| 1868 | |
| 1869 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO); | |
| 1870 return entry_->disk_entry->ReadData(kResponseInfoIndex, 0, read_buf_.get(), | |
| 1871 io_buf_len_, io_callback_); | |
| 1872 } | |
| 1873 | |
| 1874 int HttpCache::Transaction::DoCacheReadResponseComplete(int result) { | |
| 1875 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1876 tracked_objects::ScopedTracker tracking_profile( | |
| 1877 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1878 "422516 HttpCache::Transaction::DoCacheReadResponseComplete")); | |
| 1879 | |
| 1880 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_INFO, result); | |
| 1881 if (result != io_buf_len_ || | |
| 1882 !HttpCache::ParseResponseInfo(read_buf_->data(), io_buf_len_, | |
| 1883 &response_, &truncated_)) { | |
| 1884 return OnCacheReadError(result, true); | |
| 1885 } | |
| 1886 | |
| 1887 // cert_cache() will be null if the CertCacheTrial field trial is disabled. | |
| 1888 if (cache_->cert_cache() && response_.ssl_info.is_valid()) | |
| 1889 ReadCertChain(); | |
| 1890 | |
| 1891 // Some resources may have slipped in as truncated when they're not. | |
| 1892 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); | |
| 1893 if (response_.headers->GetContentLength() == current_size) | |
| 1894 truncated_ = false; | |
| 1895 | |
| 1896 if ((response_.unused_since_prefetch && | |
| 1897 !(request_->load_flags & LOAD_PREFETCH)) || | |
| 1898 (!response_.unused_since_prefetch && | |
| 1899 (request_->load_flags & LOAD_PREFETCH))) { | |
| 1900 // Either this is the first use of an entry since it was prefetched or | |
| 1901 // this is a prefetch. The value of response.unused_since_prefetch is valid | |
| 1902 // for this transaction but the bit needs to be flipped in storage. | |
| 1903 next_state_ = STATE_TOGGLE_UNUSED_SINCE_PREFETCH; | |
| 1904 return OK; | |
| 1905 } | |
| 1906 | |
| 1907 next_state_ = STATE_CACHE_DISPATCH_VALIDATION; | |
| 1908 return OK; | |
| 1909 } | |
| 1910 | |
| 1911 int HttpCache::Transaction::DoCacheDispatchValidation() { | |
| 1912 // We now have access to the cache entry. | |
| 1913 // | |
| 1914 // o if we are a reader for the transaction, then we can start reading the | |
| 1915 // cache entry. | |
| 1916 // | |
| 1917 // o if we can read or write, then we should check if the cache entry needs | |
| 1918 // to be validated and then issue a network request if needed or just read | |
| 1919 // from the cache if the cache entry is already valid. | |
| 1920 // | |
| 1921 // o if we are set to UPDATE, then we are handling an externally | |
| 1922 // conditionalized request (if-modified-since / if-none-match). We check | |
| 1923 // if the request headers define a validation request. | |
| 1924 // | |
| 1925 int result = ERR_FAILED; | |
| 1926 switch (mode_) { | |
| 1927 case READ: | |
| 1928 UpdateTransactionPattern(PATTERN_ENTRY_USED); | |
| 1929 result = BeginCacheRead(); | |
| 1930 break; | |
| 1931 case READ_WRITE: | |
| 1932 result = BeginPartialCacheValidation(); | |
| 1933 break; | |
| 1934 case UPDATE: | |
| 1935 result = BeginExternallyConditionalizedRequest(); | |
| 1936 break; | |
| 1937 case WRITE: | |
| 1938 default: | |
| 1939 NOTREACHED(); | |
| 1940 } | |
| 1941 return result; | |
| 1942 } | |
| 1943 | |
| 1944 int HttpCache::Transaction::DoCacheToggleUnusedSincePrefetch() { | |
| 1945 // Write back the toggled value for the next use of this entry. | |
| 1946 response_.unused_since_prefetch = !response_.unused_since_prefetch; | |
| 1947 | |
| 1948 // TODO(jkarlin): If DoUpdateCachedResponse is also called for this | |
| 1949 // transaction then metadata will be written to cache twice. If prefetching | |
| 1950 // becomes more common, consider combining the writes. | |
| 1951 target_state_ = STATE_TOGGLE_UNUSED_SINCE_PREFETCH_COMPLETE; | |
| 1952 next_state_ = STATE_CACHE_WRITE_RESPONSE; | |
| 1953 return OK; | |
| 1954 } | |
| 1955 | |
| 1956 int HttpCache::Transaction::DoCacheToggleUnusedSincePrefetchComplete( | |
| 1957 int result) { | |
| 1958 // Restore the original value for this transaction. | |
| 1959 response_.unused_since_prefetch = !response_.unused_since_prefetch; | |
| 1960 next_state_ = STATE_CACHE_DISPATCH_VALIDATION; | |
| 1961 return OK; | |
| 1962 } | |
| 1963 | |
| 1964 int HttpCache::Transaction::DoCacheWriteResponse() { | |
| 1965 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1966 tracked_objects::ScopedTracker tracking_profile( | |
| 1967 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1968 "422516 HttpCache::Transaction::DoCacheWriteResponse")); | |
| 1969 | |
| 1970 if (entry_) { | |
| 1971 if (net_log_.IsLogging()) | |
| 1972 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); | |
| 1973 } | |
| 1974 return WriteResponseInfoToEntry(false); | |
| 1975 } | |
| 1976 | |
| 1977 int HttpCache::Transaction::DoCacheWriteTruncatedResponse() { | |
| 1978 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1979 tracked_objects::ScopedTracker tracking_profile( | |
| 1980 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1981 "422516 HttpCache::Transaction::DoCacheWriteTruncatedResponse")); | |
| 1982 | |
| 1983 if (entry_) { | |
| 1984 if (net_log_.IsLogging()) | |
| 1985 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); | |
| 1986 } | |
| 1987 return WriteResponseInfoToEntry(true); | |
| 1988 } | |
| 1989 | |
| 1990 int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) { | |
| 1991 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1992 tracked_objects::ScopedTracker tracking_profile( | |
| 1993 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1994 "422516 HttpCache::Transaction::DoCacheWriteResponseComplete")); | |
| 1995 | |
| 1996 next_state_ = target_state_; | |
| 1997 target_state_ = STATE_NONE; | |
| 1998 if (!entry_) | |
| 1999 return OK; | |
| 2000 if (net_log_.IsLogging()) { | |
| 2001 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO, | |
| 2002 result); | |
| 2003 } | |
| 2004 | |
| 2005 // Balance the AddRef from WriteResponseInfoToEntry. | |
| 2006 if (result != io_buf_len_) { | |
| 2007 DLOG(ERROR) << "failed to write response info to cache"; | |
| 2008 DoneWritingToEntry(false); | |
| 2009 } | |
| 2010 return OK; | |
| 2011 } | |
| 2012 | |
| 2013 int HttpCache::Transaction::DoCacheReadMetadata() { | |
| 2014 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 2015 tracked_objects::ScopedTracker tracking_profile( | |
| 2016 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 2017 "422516 HttpCache::Transaction::DoCacheReadMetadata")); | |
| 2018 | |
| 2019 DCHECK(entry_); | |
| 2020 DCHECK(!response_.metadata.get()); | |
| 2021 next_state_ = STATE_CACHE_READ_METADATA_COMPLETE; | |
| 2022 | |
| 2023 response_.metadata = | |
| 2024 new IOBufferWithSize(entry_->disk_entry->GetDataSize(kMetadataIndex)); | |
| 2025 | |
| 2026 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO); | |
| 2027 return entry_->disk_entry->ReadData(kMetadataIndex, 0, | |
| 2028 response_.metadata.get(), | |
| 2029 response_.metadata->size(), | |
| 2030 io_callback_); | |
| 2031 } | |
| 2032 | |
| 2033 int HttpCache::Transaction::DoCacheReadMetadataComplete(int result) { | |
| 2034 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 2035 tracked_objects::ScopedTracker tracking_profile( | |
| 2036 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 2037 "422516 HttpCache::Transaction::DoCacheReadMetadataComplete")); | |
| 2038 | |
| 2039 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_INFO, result); | |
| 2040 if (result != response_.metadata->size()) | |
| 2041 return OnCacheReadError(result, false); | |
| 2042 return OK; | |
| 2043 } | |
| 2044 | |
| 2045 int HttpCache::Transaction::DoCacheQueryData() { | |
| 2046 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 2047 tracked_objects::ScopedTracker tracking_profile( | |
| 2048 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 2049 "422516 HttpCache::Transaction::DoCacheQueryData")); | |
| 2050 | |
| 2051 next_state_ = STATE_CACHE_QUERY_DATA_COMPLETE; | |
| 2052 return entry_->disk_entry->ReadyForSparseIO(io_callback_); | |
| 2053 } | |
| 2054 | |
| 2055 int HttpCache::Transaction::DoCacheQueryDataComplete(int result) { | |
| 2056 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 2057 tracked_objects::ScopedTracker tracking_profile( | |
| 2058 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 2059 "422516 HttpCache::Transaction::DoCacheQueryDataComplete")); | |
| 2060 | |
| 2061 DCHECK_EQ(OK, result); | |
| 2062 if (!cache_.get()) | |
| 2063 return ERR_UNEXPECTED; | |
| 2064 | |
| 2065 return ValidateEntryHeadersAndContinue(); | |
| 2066 } | |
| 2067 | |
| 2068 int HttpCache::Transaction::DoCacheReadData() { | |
| 2069 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 2070 tracked_objects::ScopedTracker tracking_profile( | |
| 2071 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 2072 "422516 HttpCache::Transaction::DoCacheReadData")); | |
| 2073 | |
| 2074 DCHECK(entry_); | |
| 2075 next_state_ = STATE_CACHE_READ_DATA_COMPLETE; | |
| 2076 | |
| 2077 if (net_log_.IsLogging()) | |
| 2078 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_DATA); | |
| 2079 if (partial_.get()) { | |
| 2080 return partial_->CacheRead(entry_->disk_entry, read_buf_.get(), io_buf_len_, | |
| 2081 io_callback_); | |
| 2082 } | |
| 2083 | |
| 2084 return entry_->disk_entry->ReadData(kResponseContentIndex, read_offset_, | |
| 2085 read_buf_.get(), io_buf_len_, | |
| 2086 io_callback_); | |
| 2087 } | |
| 2088 | |
| 2089 int HttpCache::Transaction::DoCacheReadDataComplete(int result) { | |
| 2090 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 2091 tracked_objects::ScopedTracker tracking_profile( | |
| 2092 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 2093 "422516 HttpCache::Transaction::DoCacheReadDataComplete")); | |
| 2094 | |
| 2095 if (net_log_.IsLogging()) { | |
| 2096 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_DATA, | |
| 2097 result); | |
| 2098 } | |
| 2099 | |
| 2100 if (!cache_.get()) | |
| 2101 return ERR_UNEXPECTED; | |
| 2102 | |
| 2103 if (partial_.get()) { | |
| 2104 // Partial requests are confusing to report in histograms because they may | |
| 2105 // have multiple underlying requests. | |
| 2106 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 2107 return DoPartialCacheReadCompleted(result); | |
| 2108 } | |
| 2109 | |
| 2110 if (result > 0) { | |
| 2111 read_offset_ += result; | |
| 2112 } else if (result == 0) { // End of file. | |
| 2113 RecordHistograms(); | |
| 2114 cache_->DoneReadingFromEntry(entry_, this); | |
| 2115 entry_ = NULL; | |
| 2116 } else { | |
| 2117 return OnCacheReadError(result, false); | |
| 2118 } | |
| 2119 return result; | |
| 2120 } | |
| 2121 | |
| 2122 int HttpCache::Transaction::DoCacheWriteData(int num_bytes) { | |
| 2123 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 2124 tracked_objects::ScopedTracker tracking_profile( | |
| 2125 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 2126 "422516 HttpCache::Transaction::DoCacheWriteData")); | |
| 2127 | |
| 2128 next_state_ = STATE_CACHE_WRITE_DATA_COMPLETE; | |
| 2129 write_len_ = num_bytes; | |
| 2130 if (entry_) { | |
| 2131 if (net_log_.IsLogging()) | |
| 2132 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA); | |
| 2133 } | |
| 2134 | |
| 2135 return AppendResponseDataToEntry(read_buf_.get(), num_bytes, io_callback_); | |
| 2136 } | |
| 2137 | |
| 2138 int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { | |
| 2139 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 2140 tracked_objects::ScopedTracker tracking_profile( | |
| 2141 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 2142 "422516 HttpCache::Transaction::DoCacheWriteDataComplete")); | |
| 2143 | |
| 2144 if (entry_) { | |
| 2145 if (net_log_.IsLogging()) { | |
| 2146 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA, | |
| 2147 result); | |
| 2148 } | |
| 2149 } | |
| 2150 // Balance the AddRef from DoCacheWriteData. | |
| 2151 if (!cache_.get()) | |
| 2152 return ERR_UNEXPECTED; | |
| 2153 | |
| 2154 if (result != write_len_) { | |
| 2155 DLOG(ERROR) << "failed to write response data to cache"; | |
| 2156 DoneWritingToEntry(false); | |
| 2157 | |
| 2158 // We want to ignore errors writing to disk and just keep reading from | |
| 2159 // the network. | |
| 2160 result = write_len_; | |
| 2161 } else if (!done_reading_ && entry_) { | |
| 2162 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); | |
| 2163 int64 body_size = response_.headers->GetContentLength(); | |
| 2164 if (body_size >= 0 && body_size <= current_size) | |
| 2165 done_reading_ = true; | |
| 2166 } | |
| 2167 | |
| 2168 if (partial_.get()) { | |
| 2169 // This may be the last request. | |
| 2170 if (!(result == 0 && !truncated_ && | |
| 2171 (partial_->IsLastRange() || mode_ == WRITE))) | |
| 2172 return DoPartialNetworkReadCompleted(result); | |
| 2173 } | |
| 2174 | |
| 2175 if (result == 0) { | |
| 2176 // End of file. This may be the result of a connection problem so see if we | |
| 2177 // have to keep the entry around to be flagged as truncated later on. | |
| 2178 if (done_reading_ || !entry_ || partial_.get() || | |
| 2179 response_.headers->GetContentLength() <= 0) | |
| 2180 DoneWritingToEntry(true); | |
| 2181 } | |
| 2182 | |
| 2183 return result; | |
| 2184 } | |
| 2185 | |
| 2186 //----------------------------------------------------------------------------- | |
| 2187 | |
| 2188 void HttpCache::Transaction::ReadCertChain() { | |
| 2189 std::string key = | |
| 2190 GetCacheKeyForCert(response_.ssl_info.cert->os_cert_handle()); | |
| 2191 const X509Certificate::OSCertHandles& intermediates = | |
| 2192 response_.ssl_info.cert->GetIntermediateCertificates(); | |
| 2193 int dist_from_root = intermediates.size(); | |
| 2194 | |
| 2195 scoped_refptr<SharedChainData> shared_chain_data( | |
| 2196 new SharedChainData(intermediates.size() + 1, TimeTicks::Now())); | |
| 2197 cache_->cert_cache()->GetCertificate(key, | |
| 2198 base::Bind(&OnCertReadIOComplete, | |
| 2199 dist_from_root, | |
| 2200 true /* is leaf */, | |
| 2201 shared_chain_data)); | |
| 2202 | |
| 2203 for (X509Certificate::OSCertHandles::const_iterator it = | |
| 2204 intermediates.begin(); | |
| 2205 it != intermediates.end(); | |
| 2206 ++it) { | |
| 2207 --dist_from_root; | |
| 2208 key = GetCacheKeyForCert(*it); | |
| 2209 cache_->cert_cache()->GetCertificate(key, | |
| 2210 base::Bind(&OnCertReadIOComplete, | |
| 2211 dist_from_root, | |
| 2212 false /* is not leaf */, | |
| 2213 shared_chain_data)); | |
| 2214 } | |
| 2215 DCHECK_EQ(0, dist_from_root); | |
| 2216 } | |
| 2217 | |
| 2218 void HttpCache::Transaction::WriteCertChain() { | |
| 2219 const X509Certificate::OSCertHandles& intermediates = | |
| 2220 response_.ssl_info.cert->GetIntermediateCertificates(); | |
| 2221 int dist_from_root = intermediates.size(); | |
| 2222 | |
| 2223 scoped_refptr<SharedChainData> shared_chain_data( | |
| 2224 new SharedChainData(intermediates.size() + 1, TimeTicks::Now())); | |
| 2225 cache_->cert_cache()->SetCertificate( | |
| 2226 response_.ssl_info.cert->os_cert_handle(), | |
| 2227 base::Bind(&OnCertWriteIOComplete, | |
| 2228 dist_from_root, | |
| 2229 true /* is leaf */, | |
| 2230 shared_chain_data)); | |
| 2231 for (X509Certificate::OSCertHandles::const_iterator it = | |
| 2232 intermediates.begin(); | |
| 2233 it != intermediates.end(); | |
| 2234 ++it) { | |
| 2235 --dist_from_root; | |
| 2236 cache_->cert_cache()->SetCertificate(*it, | |
| 2237 base::Bind(&OnCertWriteIOComplete, | |
| 2238 dist_from_root, | |
| 2239 false /* is not leaf */, | |
| 2240 shared_chain_data)); | |
| 2241 } | |
| 2242 DCHECK_EQ(0, dist_from_root); | |
| 2243 } | |
| 2244 | |
| 2245 void HttpCache::Transaction::SetRequest(const BoundNetLog& net_log, | |
| 2246 const HttpRequestInfo* request) { | |
| 2247 net_log_ = net_log; | |
| 2248 request_ = request; | |
| 2249 effective_load_flags_ = request_->load_flags; | |
| 2250 | |
| 2251 switch (cache_->mode()) { | |
| 2252 case NORMAL: | |
| 2253 break; | |
| 2254 case RECORD: | |
| 2255 // When in record mode, we want to NEVER load from the cache. | |
| 2256 // The reason for this is because we save the Set-Cookie headers | |
| 2257 // (intentionally). If we read from the cache, we replay them | |
| 2258 // prematurely. | |
| 2259 effective_load_flags_ |= LOAD_BYPASS_CACHE; | |
| 2260 break; | |
| 2261 case PLAYBACK: | |
| 2262 // When in playback mode, we want to load exclusively from the cache. | |
| 2263 effective_load_flags_ |= LOAD_ONLY_FROM_CACHE; | |
| 2264 break; | |
| 2265 case DISABLE: | |
| 2266 effective_load_flags_ |= LOAD_DISABLE_CACHE; | |
| 2267 break; | |
| 2268 } | |
| 2269 | |
| 2270 // Some headers imply load flags. The order here is significant. | |
| 2271 // | |
| 2272 // LOAD_DISABLE_CACHE : no cache read or write | |
| 2273 // LOAD_BYPASS_CACHE : no cache read | |
| 2274 // LOAD_VALIDATE_CACHE : no cache read unless validation | |
| 2275 // | |
| 2276 // The former modes trump latter modes, so if we find a matching header we | |
| 2277 // can stop iterating kSpecialHeaders. | |
| 2278 // | |
| 2279 static const struct { | |
| 2280 const HeaderNameAndValue* search; | |
| 2281 int load_flag; | |
| 2282 } kSpecialHeaders[] = { | |
| 2283 { kPassThroughHeaders, LOAD_DISABLE_CACHE }, | |
| 2284 { kForceFetchHeaders, LOAD_BYPASS_CACHE }, | |
| 2285 { kForceValidateHeaders, LOAD_VALIDATE_CACHE }, | |
| 2286 }; | |
| 2287 | |
| 2288 bool range_found = false; | |
| 2289 bool external_validation_error = false; | |
| 2290 bool special_headers = false; | |
| 2291 | |
| 2292 if (request_->extra_headers.HasHeader(HttpRequestHeaders::kRange)) | |
| 2293 range_found = true; | |
| 2294 | |
| 2295 for (size_t i = 0; i < arraysize(kSpecialHeaders); ++i) { | |
| 2296 if (HeaderMatches(request_->extra_headers, kSpecialHeaders[i].search)) { | |
| 2297 effective_load_flags_ |= kSpecialHeaders[i].load_flag; | |
| 2298 special_headers = true; | |
| 2299 break; | |
| 2300 } | |
| 2301 } | |
| 2302 | |
| 2303 // Check for conditionalization headers which may correspond with a | |
| 2304 // cache validation request. | |
| 2305 for (size_t i = 0; i < arraysize(kValidationHeaders); ++i) { | |
| 2306 const ValidationHeaderInfo& info = kValidationHeaders[i]; | |
| 2307 std::string validation_value; | |
| 2308 if (request_->extra_headers.GetHeader( | |
| 2309 info.request_header_name, &validation_value)) { | |
| 2310 if (!external_validation_.values[i].empty() || | |
| 2311 validation_value.empty()) { | |
| 2312 external_validation_error = true; | |
| 2313 } | |
| 2314 external_validation_.values[i] = validation_value; | |
| 2315 external_validation_.initialized = true; | |
| 2316 } | |
| 2317 } | |
| 2318 | |
| 2319 if (range_found || special_headers || external_validation_.initialized) { | |
| 2320 // Log the headers before request_ is modified. | |
| 2321 std::string empty; | |
| 2322 net_log_.AddEvent( | |
| 2323 NetLog::TYPE_HTTP_CACHE_CALLER_REQUEST_HEADERS, | |
| 2324 base::Bind(&HttpRequestHeaders::NetLogCallback, | |
| 2325 base::Unretained(&request_->extra_headers), &empty)); | |
| 2326 } | |
| 2327 | |
| 2328 // We don't support ranges and validation headers. | |
| 2329 if (range_found && external_validation_.initialized) { | |
| 2330 LOG(WARNING) << "Byte ranges AND validation headers found."; | |
| 2331 effective_load_flags_ |= LOAD_DISABLE_CACHE; | |
| 2332 } | |
| 2333 | |
| 2334 // If there is more than one validation header, we can't treat this request as | |
| 2335 // a cache validation, since we don't know for sure which header the server | |
| 2336 // will give us a response for (and they could be contradictory). | |
| 2337 if (external_validation_error) { | |
| 2338 LOG(WARNING) << "Multiple or malformed validation headers found."; | |
| 2339 effective_load_flags_ |= LOAD_DISABLE_CACHE; | |
| 2340 } | |
| 2341 | |
| 2342 if (range_found && !(effective_load_flags_ & LOAD_DISABLE_CACHE)) { | |
| 2343 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 2344 partial_.reset(new PartialData); | |
| 2345 if (request_->method == "GET" && partial_->Init(request_->extra_headers)) { | |
| 2346 // We will be modifying the actual range requested to the server, so | |
| 2347 // let's remove the header here. | |
| 2348 custom_request_.reset(new HttpRequestInfo(*request_)); | |
| 2349 custom_request_->extra_headers.RemoveHeader(HttpRequestHeaders::kRange); | |
| 2350 request_ = custom_request_.get(); | |
| 2351 partial_->SetHeaders(custom_request_->extra_headers); | |
| 2352 } else { | |
| 2353 // The range is invalid or we cannot handle it properly. | |
| 2354 VLOG(1) << "Invalid byte range found."; | |
| 2355 effective_load_flags_ |= LOAD_DISABLE_CACHE; | |
| 2356 partial_.reset(NULL); | |
| 2357 } | |
| 2358 } | |
| 2359 } | |
| 2360 | |
| 2361 bool HttpCache::Transaction::ShouldPassThrough() { | |
| 2362 // We may have a null disk_cache if there is an error we cannot recover from, | |
| 2363 // like not enough disk space, or sharing violations. | |
| 2364 if (!cache_->disk_cache_.get()) | |
| 2365 return true; | |
| 2366 | |
| 2367 // When using the record/playback modes, we always use the cache | |
| 2368 // and we never pass through. | |
| 2369 if (cache_->mode() == RECORD || cache_->mode() == PLAYBACK) | |
| 2370 return false; | |
| 2371 | |
| 2372 if (effective_load_flags_ & LOAD_DISABLE_CACHE) | |
| 2373 return true; | |
| 2374 | |
| 2375 if (request_->method == "GET" || request_->method == "HEAD") | |
| 2376 return false; | |
| 2377 | |
| 2378 if (request_->method == "POST" && request_->upload_data_stream && | |
| 2379 request_->upload_data_stream->identifier()) { | |
| 2380 return false; | |
| 2381 } | |
| 2382 | |
| 2383 if (request_->method == "PUT" && request_->upload_data_stream) | |
| 2384 return false; | |
| 2385 | |
| 2386 if (request_->method == "DELETE") | |
| 2387 return false; | |
| 2388 | |
| 2389 return true; | |
| 2390 } | |
| 2391 | |
| 2392 int HttpCache::Transaction::BeginCacheRead() { | |
| 2393 // We don't support any combination of LOAD_ONLY_FROM_CACHE and byte ranges. | |
| 2394 if (response_.headers->response_code() == 206 || partial_.get()) { | |
| 2395 NOTREACHED(); | |
| 2396 return ERR_CACHE_MISS; | |
| 2397 } | |
| 2398 | |
| 2399 if (request_->method == "HEAD") | |
| 2400 FixHeadersForHead(); | |
| 2401 | |
| 2402 // We don't have the whole resource. | |
| 2403 if (truncated_) | |
| 2404 return ERR_CACHE_MISS; | |
| 2405 | |
| 2406 if (entry_->disk_entry->GetDataSize(kMetadataIndex)) | |
| 2407 next_state_ = STATE_CACHE_READ_METADATA; | |
| 2408 | |
| 2409 return OK; | |
| 2410 } | |
| 2411 | |
| 2412 int HttpCache::Transaction::BeginCacheValidation() { | |
| 2413 DCHECK(mode_ == READ_WRITE); | |
| 2414 | |
| 2415 ValidationType required_validation = RequiresValidation(); | |
| 2416 | |
| 2417 bool skip_validation = (required_validation == VALIDATION_NONE); | |
| 2418 | |
| 2419 if (required_validation == VALIDATION_ASYNCHRONOUS && | |
| 2420 !(request_->method == "GET" && (truncated_ || partial_)) && cache_ && | |
| 2421 cache_->use_stale_while_revalidate()) { | |
| 2422 TriggerAsyncValidation(); | |
| 2423 skip_validation = true; | |
| 2424 } | |
| 2425 | |
| 2426 if (request_->method == "HEAD" && | |
| 2427 (truncated_ || response_.headers->response_code() == 206)) { | |
| 2428 DCHECK(!partial_); | |
| 2429 if (skip_validation) | |
| 2430 return SetupEntryForRead(); | |
| 2431 | |
| 2432 // Bail out! | |
| 2433 next_state_ = STATE_SEND_REQUEST; | |
| 2434 mode_ = NONE; | |
| 2435 return OK; | |
| 2436 } | |
| 2437 | |
| 2438 if (truncated_) { | |
| 2439 // Truncated entries can cause partial gets, so we shouldn't record this | |
| 2440 // load in histograms. | |
| 2441 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 2442 skip_validation = !partial_->initial_validation(); | |
| 2443 } | |
| 2444 | |
| 2445 if (partial_.get() && (is_sparse_ || truncated_) && | |
| 2446 (!partial_->IsCurrentRangeCached() || invalid_range_)) { | |
| 2447 // Force revalidation for sparse or truncated entries. Note that we don't | |
| 2448 // want to ignore the regular validation logic just because a byte range was | |
| 2449 // part of the request. | |
| 2450 skip_validation = false; | |
| 2451 } | |
| 2452 | |
| 2453 if (skip_validation) { | |
| 2454 // TODO(ricea): Is this pattern okay for asynchronous revalidations? | |
| 2455 UpdateTransactionPattern(PATTERN_ENTRY_USED); | |
| 2456 RecordOfflineStatus(effective_load_flags_, OFFLINE_STATUS_FRESH_CACHE); | |
| 2457 return SetupEntryForRead(); | |
| 2458 } else { | |
| 2459 // Make the network request conditional, to see if we may reuse our cached | |
| 2460 // response. If we cannot do so, then we just resort to a normal fetch. | |
| 2461 // Our mode remains READ_WRITE for a conditional request. Even if the | |
| 2462 // conditionalization fails, we don't switch to WRITE mode until we | |
| 2463 // know we won't be falling back to using the cache entry in the | |
| 2464 // LOAD_FROM_CACHE_IF_OFFLINE case. | |
| 2465 if (!ConditionalizeRequest()) { | |
| 2466 couldnt_conditionalize_request_ = true; | |
| 2467 UpdateTransactionPattern(PATTERN_ENTRY_CANT_CONDITIONALIZE); | |
| 2468 if (partial_.get()) | |
| 2469 return DoRestartPartialRequest(); | |
| 2470 | |
| 2471 DCHECK_NE(206, response_.headers->response_code()); | |
| 2472 } | |
| 2473 next_state_ = STATE_SEND_REQUEST; | |
| 2474 } | |
| 2475 return OK; | |
| 2476 } | |
| 2477 | |
| 2478 int HttpCache::Transaction::BeginPartialCacheValidation() { | |
| 2479 DCHECK(mode_ == READ_WRITE); | |
| 2480 | |
| 2481 if (response_.headers->response_code() != 206 && !partial_.get() && | |
| 2482 !truncated_) { | |
| 2483 return BeginCacheValidation(); | |
| 2484 } | |
| 2485 | |
| 2486 // Partial requests should not be recorded in histograms. | |
| 2487 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 2488 if (range_requested_) { | |
| 2489 next_state_ = STATE_CACHE_QUERY_DATA; | |
| 2490 return OK; | |
| 2491 } | |
| 2492 | |
| 2493 // The request is not for a range, but we have stored just ranges. | |
| 2494 | |
| 2495 if (request_->method == "HEAD") | |
| 2496 return BeginCacheValidation(); | |
| 2497 | |
| 2498 partial_.reset(new PartialData()); | |
| 2499 partial_->SetHeaders(request_->extra_headers); | |
| 2500 if (!custom_request_.get()) { | |
| 2501 custom_request_.reset(new HttpRequestInfo(*request_)); | |
| 2502 request_ = custom_request_.get(); | |
| 2503 } | |
| 2504 | |
| 2505 return ValidateEntryHeadersAndContinue(); | |
| 2506 } | |
| 2507 | |
| 2508 // This should only be called once per request. | |
| 2509 int HttpCache::Transaction::ValidateEntryHeadersAndContinue() { | |
| 2510 DCHECK(mode_ == READ_WRITE); | |
| 2511 | |
| 2512 if (!partial_->UpdateFromStoredHeaders( | |
| 2513 response_.headers.get(), entry_->disk_entry, truncated_)) { | |
| 2514 return DoRestartPartialRequest(); | |
| 2515 } | |
| 2516 | |
| 2517 if (response_.headers->response_code() == 206) | |
| 2518 is_sparse_ = true; | |
| 2519 | |
| 2520 if (!partial_->IsRequestedRangeOK()) { | |
| 2521 // The stored data is fine, but the request may be invalid. | |
| 2522 invalid_range_ = true; | |
| 2523 } | |
| 2524 | |
| 2525 next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION; | |
| 2526 return OK; | |
| 2527 } | |
| 2528 | |
| 2529 int HttpCache::Transaction::BeginExternallyConditionalizedRequest() { | |
| 2530 DCHECK_EQ(UPDATE, mode_); | |
| 2531 DCHECK(external_validation_.initialized); | |
| 2532 | |
| 2533 for (size_t i = 0; i < arraysize(kValidationHeaders); i++) { | |
| 2534 if (external_validation_.values[i].empty()) | |
| 2535 continue; | |
| 2536 // Retrieve either the cached response's "etag" or "last-modified" header. | |
| 2537 std::string validator; | |
| 2538 response_.headers->EnumerateHeader( | |
| 2539 NULL, | |
| 2540 kValidationHeaders[i].related_response_header_name, | |
| 2541 &validator); | |
| 2542 | |
| 2543 if (response_.headers->response_code() != 200 || truncated_ || | |
| 2544 validator.empty() || validator != external_validation_.values[i]) { | |
| 2545 // The externally conditionalized request is not a validation request | |
| 2546 // for our existing cache entry. Proceed with caching disabled. | |
| 2547 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 2548 DoneWritingToEntry(true); | |
| 2549 } | |
| 2550 } | |
| 2551 | |
| 2552 // TODO(ricea): This calculation is expensive to perform just to collect | |
| 2553 // statistics. Either remove it or use the result, depending on the result of | |
| 2554 // the experiment. | |
| 2555 ExternallyConditionalizedType type = | |
| 2556 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE; | |
| 2557 if (mode_ == NONE) | |
| 2558 type = EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS; | |
| 2559 else if (RequiresValidation()) | |
| 2560 type = EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION; | |
| 2561 | |
| 2562 // TODO(ricea): Add CACHE_USABLE_STALE once stale-while-revalidate CL landed. | |
| 2563 // TODO(ricea): Either remove this histogram or make it permanent by M40. | |
| 2564 UMA_HISTOGRAM_ENUMERATION("HttpCache.ExternallyConditionalized", | |
| 2565 type, | |
| 2566 EXTERNALLY_CONDITIONALIZED_MAX); | |
| 2567 | |
| 2568 next_state_ = STATE_SEND_REQUEST; | |
| 2569 return OK; | |
| 2570 } | |
| 2571 | |
| 2572 int HttpCache::Transaction::RestartNetworkRequest() { | |
| 2573 DCHECK(mode_ & WRITE || mode_ == NONE); | |
| 2574 DCHECK(network_trans_.get()); | |
| 2575 DCHECK_EQ(STATE_NONE, next_state_); | |
| 2576 | |
| 2577 next_state_ = STATE_SEND_REQUEST_COMPLETE; | |
| 2578 int rv = network_trans_->RestartIgnoringLastError(io_callback_); | |
| 2579 if (rv != ERR_IO_PENDING) | |
| 2580 return DoLoop(rv); | |
| 2581 return rv; | |
| 2582 } | |
| 2583 | |
| 2584 int HttpCache::Transaction::RestartNetworkRequestWithCertificate( | |
| 2585 X509Certificate* client_cert) { | |
| 2586 DCHECK(mode_ & WRITE || mode_ == NONE); | |
| 2587 DCHECK(network_trans_.get()); | |
| 2588 DCHECK_EQ(STATE_NONE, next_state_); | |
| 2589 | |
| 2590 next_state_ = STATE_SEND_REQUEST_COMPLETE; | |
| 2591 int rv = network_trans_->RestartWithCertificate(client_cert, io_callback_); | |
| 2592 if (rv != ERR_IO_PENDING) | |
| 2593 return DoLoop(rv); | |
| 2594 return rv; | |
| 2595 } | |
| 2596 | |
| 2597 int HttpCache::Transaction::RestartNetworkRequestWithAuth( | |
| 2598 const AuthCredentials& credentials) { | |
| 2599 DCHECK(mode_ & WRITE || mode_ == NONE); | |
| 2600 DCHECK(network_trans_.get()); | |
| 2601 DCHECK_EQ(STATE_NONE, next_state_); | |
| 2602 | |
| 2603 next_state_ = STATE_SEND_REQUEST_COMPLETE; | |
| 2604 int rv = network_trans_->RestartWithAuth(credentials, io_callback_); | |
| 2605 if (rv != ERR_IO_PENDING) | |
| 2606 return DoLoop(rv); | |
| 2607 return rv; | |
| 2608 } | |
| 2609 | |
| 2610 ValidationType HttpCache::Transaction::RequiresValidation() { | |
| 2611 // TODO(darin): need to do more work here: | |
| 2612 // - make sure we have a matching request method | |
| 2613 // - watch out for cached responses that depend on authentication | |
| 2614 | |
| 2615 // In playback mode, nothing requires validation. | |
| 2616 if (cache_->mode() == net::HttpCache::PLAYBACK) | |
| 2617 return VALIDATION_NONE; | |
| 2618 | |
| 2619 if (response_.vary_data.is_valid() && | |
| 2620 !response_.vary_data.MatchesRequest(*request_, | |
| 2621 *response_.headers.get())) { | |
| 2622 vary_mismatch_ = true; | |
| 2623 return VALIDATION_SYNCHRONOUS; | |
| 2624 } | |
| 2625 | |
| 2626 if (effective_load_flags_ & LOAD_PREFERRING_CACHE) | |
| 2627 return VALIDATION_NONE; | |
| 2628 | |
| 2629 if (response_.unused_since_prefetch && | |
| 2630 !(effective_load_flags_ & LOAD_PREFETCH) && | |
| 2631 response_.headers->GetCurrentAge( | |
| 2632 response_.request_time, response_.response_time, | |
| 2633 cache_->clock_->Now()) < TimeDelta::FromMinutes(kPrefetchReuseMins)) { | |
| 2634 // The first use of a resource after prefetch within a short window skips | |
| 2635 // validation. | |
| 2636 return VALIDATION_NONE; | |
| 2637 } | |
| 2638 | |
| 2639 if (effective_load_flags_ & (LOAD_VALIDATE_CACHE | LOAD_ASYNC_REVALIDATION)) | |
| 2640 return VALIDATION_SYNCHRONOUS; | |
| 2641 | |
| 2642 if (request_->method == "PUT" || request_->method == "DELETE") | |
| 2643 return VALIDATION_SYNCHRONOUS; | |
| 2644 | |
| 2645 ValidationType validation_required_by_headers = | |
| 2646 response_.headers->RequiresValidation(response_.request_time, | |
| 2647 response_.response_time, | |
| 2648 cache_->clock_->Now()); | |
| 2649 | |
| 2650 if (validation_required_by_headers == VALIDATION_ASYNCHRONOUS) { | |
| 2651 // Asynchronous revalidation is only supported for GET and HEAD methods. | |
| 2652 if (request_->method != "GET" && request_->method != "HEAD") | |
| 2653 return VALIDATION_SYNCHRONOUS; | |
| 2654 } | |
| 2655 | |
| 2656 return validation_required_by_headers; | |
| 2657 } | |
| 2658 | |
| 2659 bool HttpCache::Transaction::ConditionalizeRequest() { | |
| 2660 DCHECK(response_.headers.get()); | |
| 2661 | |
| 2662 if (request_->method == "PUT" || request_->method == "DELETE") | |
| 2663 return false; | |
| 2664 | |
| 2665 // This only makes sense for cached 200 or 206 responses. | |
| 2666 if (response_.headers->response_code() != 200 && | |
| 2667 response_.headers->response_code() != 206) { | |
| 2668 return false; | |
| 2669 } | |
| 2670 | |
| 2671 if (fail_conditionalization_for_test_) | |
| 2672 return false; | |
| 2673 | |
| 2674 DCHECK(response_.headers->response_code() != 206 || | |
| 2675 response_.headers->HasStrongValidators()); | |
| 2676 | |
| 2677 // Just use the first available ETag and/or Last-Modified header value. | |
| 2678 // TODO(darin): Or should we use the last? | |
| 2679 | |
| 2680 std::string etag_value; | |
| 2681 if (response_.headers->GetHttpVersion() >= HttpVersion(1, 1)) | |
| 2682 response_.headers->EnumerateHeader(NULL, "etag", &etag_value); | |
| 2683 | |
| 2684 std::string last_modified_value; | |
| 2685 if (!vary_mismatch_) { | |
| 2686 response_.headers->EnumerateHeader(NULL, "last-modified", | |
| 2687 &last_modified_value); | |
| 2688 } | |
| 2689 | |
| 2690 if (etag_value.empty() && last_modified_value.empty()) | |
| 2691 return false; | |
| 2692 | |
| 2693 if (!partial_.get()) { | |
| 2694 // Need to customize the request, so this forces us to allocate :( | |
| 2695 custom_request_.reset(new HttpRequestInfo(*request_)); | |
| 2696 request_ = custom_request_.get(); | |
| 2697 } | |
| 2698 DCHECK(custom_request_.get()); | |
| 2699 | |
| 2700 bool use_if_range = partial_.get() && !partial_->IsCurrentRangeCached() && | |
| 2701 !invalid_range_; | |
| 2702 | |
| 2703 if (!use_if_range) { | |
| 2704 // stale-while-revalidate is not useful when we only have a partial response | |
| 2705 // cached, so don't set the header in that case. | |
| 2706 HttpResponseHeaders::FreshnessLifetimes lifetimes = | |
| 2707 response_.headers->GetFreshnessLifetimes(response_.response_time); | |
| 2708 if (lifetimes.staleness > TimeDelta()) { | |
| 2709 TimeDelta current_age = response_.headers->GetCurrentAge( | |
| 2710 response_.request_time, response_.response_time, | |
| 2711 cache_->clock_->Now()); | |
| 2712 | |
| 2713 custom_request_->extra_headers.SetHeader( | |
| 2714 kFreshnessHeader, | |
| 2715 base::StringPrintf("max-age=%" PRId64 | |
| 2716 ",stale-while-revalidate=%" PRId64 ",age=%" PRId64, | |
| 2717 lifetimes.freshness.InSeconds(), | |
| 2718 lifetimes.staleness.InSeconds(), | |
| 2719 current_age.InSeconds())); | |
| 2720 } | |
| 2721 } | |
| 2722 | |
| 2723 if (!etag_value.empty()) { | |
| 2724 if (use_if_range) { | |
| 2725 // We don't want to switch to WRITE mode if we don't have this block of a | |
| 2726 // byte-range request because we may have other parts cached. | |
| 2727 custom_request_->extra_headers.SetHeader( | |
| 2728 HttpRequestHeaders::kIfRange, etag_value); | |
| 2729 } else { | |
| 2730 custom_request_->extra_headers.SetHeader( | |
| 2731 HttpRequestHeaders::kIfNoneMatch, etag_value); | |
| 2732 } | |
| 2733 // For byte-range requests, make sure that we use only one way to validate | |
| 2734 // the request. | |
| 2735 if (partial_.get() && !partial_->IsCurrentRangeCached()) | |
| 2736 return true; | |
| 2737 } | |
| 2738 | |
| 2739 if (!last_modified_value.empty()) { | |
| 2740 if (use_if_range) { | |
| 2741 custom_request_->extra_headers.SetHeader( | |
| 2742 HttpRequestHeaders::kIfRange, last_modified_value); | |
| 2743 } else { | |
| 2744 custom_request_->extra_headers.SetHeader( | |
| 2745 HttpRequestHeaders::kIfModifiedSince, last_modified_value); | |
| 2746 } | |
| 2747 } | |
| 2748 | |
| 2749 return true; | |
| 2750 } | |
| 2751 | |
| 2752 // We just received some headers from the server. We may have asked for a range, | |
| 2753 // in which case partial_ has an object. This could be the first network request | |
| 2754 // we make to fulfill the original request, or we may be already reading (from | |
| 2755 // the net and / or the cache). If we are not expecting a certain response, we | |
| 2756 // just bypass the cache for this request (but again, maybe we are reading), and | |
| 2757 // delete partial_ (so we are not able to "fix" the headers that we return to | |
| 2758 // the user). This results in either a weird response for the caller (we don't | |
| 2759 // expect it after all), or maybe a range that was not exactly what it was asked | |
| 2760 // for. | |
| 2761 // | |
| 2762 // If the server is simply telling us that the resource has changed, we delete | |
| 2763 // the cached entry and restart the request as the caller intended (by returning | |
| 2764 // false from this method). However, we may not be able to do that at any point, | |
| 2765 // for instance if we already returned the headers to the user. | |
| 2766 // | |
| 2767 // WARNING: Whenever this code returns false, it has to make sure that the next | |
| 2768 // time it is called it will return true so that we don't keep retrying the | |
| 2769 // request. | |
| 2770 bool HttpCache::Transaction::ValidatePartialResponse() { | |
| 2771 const HttpResponseHeaders* headers = new_response_->headers.get(); | |
| 2772 int response_code = headers->response_code(); | |
| 2773 bool partial_response = (response_code == 206); | |
| 2774 handling_206_ = false; | |
| 2775 | |
| 2776 if (!entry_ || request_->method != "GET") | |
| 2777 return true; | |
| 2778 | |
| 2779 if (invalid_range_) { | |
| 2780 // We gave up trying to match this request with the stored data. If the | |
| 2781 // server is ok with the request, delete the entry, otherwise just ignore | |
| 2782 // this request | |
| 2783 DCHECK(!reading_); | |
| 2784 if (partial_response || response_code == 200) { | |
| 2785 DoomPartialEntry(true); | |
| 2786 mode_ = NONE; | |
| 2787 } else { | |
| 2788 if (response_code == 304) | |
| 2789 FailRangeRequest(); | |
| 2790 IgnoreRangeRequest(); | |
| 2791 } | |
| 2792 return true; | |
| 2793 } | |
| 2794 | |
| 2795 if (!partial_.get()) { | |
| 2796 // We are not expecting 206 but we may have one. | |
| 2797 if (partial_response) | |
| 2798 IgnoreRangeRequest(); | |
| 2799 | |
| 2800 return true; | |
| 2801 } | |
| 2802 | |
| 2803 // TODO(rvargas): Do we need to consider other results here?. | |
| 2804 bool failure = response_code == 200 || response_code == 416; | |
| 2805 | |
| 2806 if (partial_->IsCurrentRangeCached()) { | |
| 2807 // We asked for "If-None-Match: " so a 206 means a new object. | |
| 2808 if (partial_response) | |
| 2809 failure = true; | |
| 2810 | |
| 2811 if (response_code == 304 && partial_->ResponseHeadersOK(headers)) | |
| 2812 return true; | |
| 2813 } else { | |
| 2814 // We asked for "If-Range: " so a 206 means just another range. | |
| 2815 if (partial_response) { | |
| 2816 if (partial_->ResponseHeadersOK(headers)) { | |
| 2817 handling_206_ = true; | |
| 2818 return true; | |
| 2819 } else { | |
| 2820 failure = true; | |
| 2821 } | |
| 2822 } | |
| 2823 | |
| 2824 if (!reading_ && !is_sparse_ && !partial_response) { | |
| 2825 // See if we can ignore the fact that we issued a byte range request. | |
| 2826 // If the server sends 200, just store it. If it sends an error, redirect | |
| 2827 // or something else, we may store the response as long as we didn't have | |
| 2828 // anything already stored. | |
| 2829 if (response_code == 200 || | |
| 2830 (!truncated_ && response_code != 304 && response_code != 416)) { | |
| 2831 // The server is sending something else, and we can save it. | |
| 2832 DCHECK((truncated_ && !partial_->IsLastRange()) || range_requested_); | |
| 2833 partial_.reset(); | |
| 2834 truncated_ = false; | |
| 2835 return true; | |
| 2836 } | |
| 2837 } | |
| 2838 | |
| 2839 // 304 is not expected here, but we'll spare the entry (unless it was | |
| 2840 // truncated). | |
| 2841 if (truncated_) | |
| 2842 failure = true; | |
| 2843 } | |
| 2844 | |
| 2845 if (failure) { | |
| 2846 // We cannot truncate this entry, it has to be deleted. | |
| 2847 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 2848 mode_ = NONE; | |
| 2849 if (is_sparse_ || truncated_) { | |
| 2850 // There was something cached to start with, either sparsed data (206), or | |
| 2851 // a truncated 200, which means that we probably modified the request, | |
| 2852 // adding a byte range or modifying the range requested by the caller. | |
| 2853 if (!reading_ && !partial_->IsLastRange()) { | |
| 2854 // We have not returned anything to the caller yet so it should be safe | |
| 2855 // to issue another network request, this time without us messing up the | |
| 2856 // headers. | |
| 2857 ResetPartialState(true); | |
| 2858 return false; | |
| 2859 } | |
| 2860 LOG(WARNING) << "Failed to revalidate partial entry"; | |
| 2861 } | |
| 2862 DoomPartialEntry(true); | |
| 2863 return true; | |
| 2864 } | |
| 2865 | |
| 2866 IgnoreRangeRequest(); | |
| 2867 return true; | |
| 2868 } | |
| 2869 | |
| 2870 void HttpCache::Transaction::IgnoreRangeRequest() { | |
| 2871 // We have a problem. We may or may not be reading already (in which case we | |
| 2872 // returned the headers), but we'll just pretend that this request is not | |
| 2873 // using the cache and see what happens. Most likely this is the first | |
| 2874 // response from the server (it's not changing its mind midway, right?). | |
| 2875 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
| 2876 if (mode_ & WRITE) | |
| 2877 DoneWritingToEntry(mode_ != WRITE); | |
| 2878 else if (mode_ & READ && entry_) | |
| 2879 cache_->DoneReadingFromEntry(entry_, this); | |
| 2880 | |
| 2881 partial_.reset(NULL); | |
| 2882 entry_ = NULL; | |
| 2883 mode_ = NONE; | |
| 2884 } | |
| 2885 | |
| 2886 void HttpCache::Transaction::FixHeadersForHead() { | |
| 2887 if (response_.headers->response_code() == 206) { | |
| 2888 response_.headers->RemoveHeader("Content-Range"); | |
| 2889 response_.headers->ReplaceStatusLine("HTTP/1.1 200 OK"); | |
| 2890 } | |
| 2891 } | |
| 2892 | |
| 2893 void HttpCache::Transaction::TriggerAsyncValidation() { | |
| 2894 DCHECK(!request_->upload_data_stream); | |
| 2895 BoundNetLog async_revalidation_net_log( | |
| 2896 BoundNetLog::Make(net_log_.net_log(), NetLog::SOURCE_ASYNC_REVALIDATION)); | |
| 2897 net_log_.AddEvent( | |
| 2898 NetLog::TYPE_HTTP_CACHE_VALIDATE_RESOURCE_ASYNC, | |
| 2899 async_revalidation_net_log.source().ToEventParametersCallback()); | |
| 2900 async_revalidation_net_log.BeginEvent( | |
| 2901 NetLog::TYPE_ASYNC_REVALIDATION, | |
| 2902 base::Bind( | |
| 2903 &NetLogAsyncRevalidationInfoCallback, net_log_.source(), request_)); | |
| 2904 base::MessageLoop::current()->PostTask( | |
| 2905 FROM_HERE, | |
| 2906 base::Bind(&HttpCache::PerformAsyncValidation, | |
| 2907 cache_, // cache_ is a weak pointer. | |
| 2908 *request_, | |
| 2909 async_revalidation_net_log)); | |
| 2910 } | |
| 2911 | |
| 2912 void HttpCache::Transaction::FailRangeRequest() { | |
| 2913 response_ = *new_response_; | |
| 2914 partial_->FixResponseHeaders(response_.headers.get(), false); | |
| 2915 } | |
| 2916 | |
| 2917 int HttpCache::Transaction::SetupEntryForRead() { | |
| 2918 if (network_trans_) | |
| 2919 ResetNetworkTransaction(); | |
| 2920 if (partial_.get()) { | |
| 2921 if (truncated_ || is_sparse_ || !invalid_range_) { | |
| 2922 // We are going to return the saved response headers to the caller, so | |
| 2923 // we may need to adjust them first. | |
| 2924 next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; | |
| 2925 return OK; | |
| 2926 } else { | |
| 2927 partial_.reset(); | |
| 2928 } | |
| 2929 } | |
| 2930 cache_->ConvertWriterToReader(entry_); | |
| 2931 mode_ = READ; | |
| 2932 | |
| 2933 if (request_->method == "HEAD") | |
| 2934 FixHeadersForHead(); | |
| 2935 | |
| 2936 if (entry_->disk_entry->GetDataSize(kMetadataIndex)) | |
| 2937 next_state_ = STATE_CACHE_READ_METADATA; | |
| 2938 return OK; | |
| 2939 } | |
| 2940 | |
| 2941 | |
| 2942 int HttpCache::Transaction::ReadFromNetwork(IOBuffer* data, int data_len) { | |
| 2943 read_buf_ = data; | |
| 2944 io_buf_len_ = data_len; | |
| 2945 next_state_ = STATE_NETWORK_READ; | |
| 2946 return DoLoop(OK); | |
| 2947 } | |
| 2948 | |
| 2949 int HttpCache::Transaction::ReadFromEntry(IOBuffer* data, int data_len) { | |
| 2950 if (request_->method == "HEAD") | |
| 2951 return 0; | |
| 2952 | |
| 2953 read_buf_ = data; | |
| 2954 io_buf_len_ = data_len; | |
| 2955 next_state_ = STATE_CACHE_READ_DATA; | |
| 2956 return DoLoop(OK); | |
| 2957 } | |
| 2958 | |
| 2959 int HttpCache::Transaction::WriteToEntry(int index, int offset, | |
| 2960 IOBuffer* data, int data_len, | |
| 2961 const CompletionCallback& callback) { | |
| 2962 if (!entry_) | |
| 2963 return data_len; | |
| 2964 | |
| 2965 int rv = 0; | |
| 2966 if (!partial_.get() || !data_len) { | |
| 2967 rv = entry_->disk_entry->WriteData(index, offset, data, data_len, callback, | |
| 2968 true); | |
| 2969 } else { | |
| 2970 rv = partial_->CacheWrite(entry_->disk_entry, data, data_len, callback); | |
| 2971 } | |
| 2972 return rv; | |
| 2973 } | |
| 2974 | |
| 2975 int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { | |
| 2976 next_state_ = STATE_CACHE_WRITE_RESPONSE_COMPLETE; | |
| 2977 if (!entry_) | |
| 2978 return OK; | |
| 2979 | |
| 2980 // Do not cache no-store content (unless we are record mode). Do not cache | |
| 2981 // content with cert errors either. This is to prevent not reporting net | |
| 2982 // errors when loading a resource from the cache. When we load a page over | |
| 2983 // HTTPS with a cert error we show an SSL blocking page. If the user clicks | |
| 2984 // proceed we reload the resource ignoring the errors. The loaded resource | |
| 2985 // is then cached. If that resource is subsequently loaded from the cache, | |
| 2986 // no net error is reported (even though the cert status contains the actual | |
| 2987 // errors) and no SSL blocking page is shown. An alternative would be to | |
| 2988 // reverse-map the cert status to a net error and replay the net error. | |
| 2989 if ((cache_->mode() != RECORD && | |
| 2990 response_.headers->HasHeaderValue("cache-control", "no-store")) || | |
| 2991 net::IsCertStatusError(response_.ssl_info.cert_status)) { | |
| 2992 DoneWritingToEntry(false); | |
| 2993 if (net_log_.IsLogging()) | |
| 2994 net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); | |
| 2995 return OK; | |
| 2996 } | |
| 2997 | |
| 2998 // cert_cache() will be null if the CertCacheTrial field trial is disabled. | |
| 2999 if (cache_->cert_cache() && response_.ssl_info.is_valid()) | |
| 3000 WriteCertChain(); | |
| 3001 | |
| 3002 // When writing headers, we normally only write the non-transient | |
| 3003 // headers; when in record mode, record everything. | |
| 3004 bool skip_transient_headers = (cache_->mode() != RECORD); | |
| 3005 | |
| 3006 if (truncated) | |
| 3007 DCHECK_EQ(200, response_.headers->response_code()); | |
| 3008 | |
| 3009 scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer()); | |
| 3010 response_.Persist(data->pickle(), skip_transient_headers, truncated); | |
| 3011 data->Done(); | |
| 3012 | |
| 3013 io_buf_len_ = data->pickle()->size(); | |
| 3014 return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(), | |
| 3015 io_buf_len_, io_callback_, true); | |
| 3016 } | |
| 3017 | |
| 3018 int HttpCache::Transaction::AppendResponseDataToEntry( | |
| 3019 IOBuffer* data, int data_len, const CompletionCallback& callback) { | |
| 3020 if (!entry_ || !data_len) | |
| 3021 return data_len; | |
| 3022 | |
| 3023 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); | |
| 3024 return WriteToEntry(kResponseContentIndex, current_size, data, data_len, | |
| 3025 callback); | |
| 3026 } | |
| 3027 | |
| 3028 void HttpCache::Transaction::DoneWritingToEntry(bool success) { | |
| 3029 if (!entry_) | |
| 3030 return; | |
| 3031 | |
| 3032 RecordHistograms(); | |
| 3033 | |
| 3034 cache_->DoneWritingToEntry(entry_, success); | |
| 3035 entry_ = NULL; | |
| 3036 mode_ = NONE; // switch to 'pass through' mode | |
| 3037 } | |
| 3038 | |
| 3039 int HttpCache::Transaction::OnCacheReadError(int result, bool restart) { | |
| 3040 DLOG(ERROR) << "ReadData failed: " << result; | |
| 3041 const int result_for_histogram = std::max(0, -result); | |
| 3042 if (restart) { | |
| 3043 UMA_HISTOGRAM_SPARSE_SLOWLY("HttpCache.ReadErrorRestartable", | |
| 3044 result_for_histogram); | |
| 3045 } else { | |
| 3046 UMA_HISTOGRAM_SPARSE_SLOWLY("HttpCache.ReadErrorNonRestartable", | |
| 3047 result_for_histogram); | |
| 3048 } | |
| 3049 | |
| 3050 // Avoid using this entry in the future. | |
| 3051 if (cache_.get()) | |
| 3052 cache_->DoomActiveEntry(cache_key_); | |
| 3053 | |
| 3054 if (restart) { | |
| 3055 DCHECK(!reading_); | |
| 3056 DCHECK(!network_trans_.get()); | |
| 3057 cache_->DoneWithEntry(entry_, this, false); | |
| 3058 entry_ = NULL; | |
| 3059 is_sparse_ = false; | |
| 3060 partial_.reset(); | |
| 3061 next_state_ = STATE_GET_BACKEND; | |
| 3062 return OK; | |
| 3063 } | |
| 3064 | |
| 3065 return ERR_CACHE_READ_FAILURE; | |
| 3066 } | |
| 3067 | |
| 3068 void HttpCache::Transaction::OnAddToEntryTimeout(base::TimeTicks start_time) { | |
| 3069 if (entry_lock_waiting_since_ != start_time) | |
| 3070 return; | |
| 3071 | |
| 3072 DCHECK_EQ(next_state_, STATE_ADD_TO_ENTRY_COMPLETE); | |
| 3073 | |
| 3074 if (!cache_) | |
| 3075 return; | |
| 3076 | |
| 3077 cache_->RemovePendingTransaction(this); | |
| 3078 OnIOComplete(ERR_CACHE_LOCK_TIMEOUT); | |
| 3079 } | |
| 3080 | |
| 3081 void HttpCache::Transaction::DoomPartialEntry(bool delete_object) { | |
| 3082 DVLOG(2) << "DoomPartialEntry"; | |
| 3083 int rv = cache_->DoomEntry(cache_key_, NULL); | |
| 3084 DCHECK_EQ(OK, rv); | |
| 3085 cache_->DoneWithEntry(entry_, this, false); | |
| 3086 entry_ = NULL; | |
| 3087 is_sparse_ = false; | |
| 3088 truncated_ = false; | |
| 3089 if (delete_object) | |
| 3090 partial_.reset(NULL); | |
| 3091 } | |
| 3092 | |
| 3093 int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result) { | |
| 3094 partial_->OnNetworkReadCompleted(result); | |
| 3095 | |
| 3096 if (result == 0) { | |
| 3097 // We need to move on to the next range. | |
| 3098 ResetNetworkTransaction(); | |
| 3099 next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION; | |
| 3100 } | |
| 3101 return result; | |
| 3102 } | |
| 3103 | |
| 3104 int HttpCache::Transaction::DoPartialCacheReadCompleted(int result) { | |
| 3105 partial_->OnCacheReadCompleted(result); | |
| 3106 | |
| 3107 if (result == 0 && mode_ == READ_WRITE) { | |
| 3108 // We need to move on to the next range. | |
| 3109 next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION; | |
| 3110 } else if (result < 0) { | |
| 3111 return OnCacheReadError(result, false); | |
| 3112 } | |
| 3113 return result; | |
| 3114 } | |
| 3115 | |
| 3116 int HttpCache::Transaction::DoRestartPartialRequest() { | |
| 3117 // The stored data cannot be used. Get rid of it and restart this request. | |
| 3118 net_log_.AddEvent(NetLog::TYPE_HTTP_CACHE_RESTART_PARTIAL_REQUEST); | |
| 3119 | |
| 3120 // WRITE + Doom + STATE_INIT_ENTRY == STATE_CREATE_ENTRY (without an attempt | |
| 3121 // to Doom the entry again). | |
| 3122 mode_ = WRITE; | |
| 3123 ResetPartialState(!range_requested_); | |
| 3124 next_state_ = STATE_CREATE_ENTRY; | |
| 3125 return OK; | |
| 3126 } | |
| 3127 | |
| 3128 void HttpCache::Transaction::ResetPartialState(bool delete_object) { | |
| 3129 partial_->RestoreHeaders(&custom_request_->extra_headers); | |
| 3130 DoomPartialEntry(delete_object); | |
| 3131 | |
| 3132 if (!delete_object) { | |
| 3133 // The simplest way to re-initialize partial_ is to create a new object. | |
| 3134 partial_.reset(new PartialData()); | |
| 3135 if (partial_->Init(request_->extra_headers)) | |
| 3136 partial_->SetHeaders(custom_request_->extra_headers); | |
| 3137 else | |
| 3138 partial_.reset(); | |
| 3139 } | |
| 3140 } | |
| 3141 | |
| 3142 void HttpCache::Transaction::ResetNetworkTransaction() { | |
| 3143 DCHECK(!old_network_trans_load_timing_); | |
| 3144 DCHECK(network_trans_); | |
| 3145 LoadTimingInfo load_timing; | |
| 3146 if (network_trans_->GetLoadTimingInfo(&load_timing)) | |
| 3147 old_network_trans_load_timing_.reset(new LoadTimingInfo(load_timing)); | |
| 3148 total_received_bytes_ += network_trans_->GetTotalReceivedBytes(); | |
| 3149 network_trans_.reset(); | |
| 3150 } | |
| 3151 | |
| 3152 // Histogram data from the end of 2010 show the following distribution of | |
| 3153 // response headers: | |
| 3154 // | |
| 3155 // Content-Length............... 87% | |
| 3156 // Date......................... 98% | |
| 3157 // Last-Modified................ 49% | |
| 3158 // Etag......................... 19% | |
| 3159 // Accept-Ranges: bytes......... 25% | |
| 3160 // Accept-Ranges: none.......... 0.4% | |
| 3161 // Strong Validator............. 50% | |
| 3162 // Strong Validator + ranges.... 24% | |
| 3163 // Strong Validator + CL........ 49% | |
| 3164 // | |
| 3165 bool HttpCache::Transaction::CanResume(bool has_data) { | |
| 3166 // Double check that there is something worth keeping. | |
| 3167 if (has_data && !entry_->disk_entry->GetDataSize(kResponseContentIndex)) | |
| 3168 return false; | |
| 3169 | |
| 3170 if (request_->method != "GET") | |
| 3171 return false; | |
| 3172 | |
| 3173 // Note that if this is a 206, content-length was already fixed after calling | |
| 3174 // PartialData::ResponseHeadersOK(). | |
| 3175 if (response_.headers->GetContentLength() <= 0 || | |
| 3176 response_.headers->HasHeaderValue("Accept-Ranges", "none") || | |
| 3177 !response_.headers->HasStrongValidators()) { | |
| 3178 return false; | |
| 3179 } | |
| 3180 | |
| 3181 return true; | |
| 3182 } | |
| 3183 | |
| 3184 void HttpCache::Transaction::UpdateTransactionPattern( | |
| 3185 TransactionPattern new_transaction_pattern) { | |
| 3186 if (transaction_pattern_ == PATTERN_NOT_COVERED) | |
| 3187 return; | |
| 3188 DCHECK(transaction_pattern_ == PATTERN_UNDEFINED || | |
| 3189 new_transaction_pattern == PATTERN_NOT_COVERED); | |
| 3190 transaction_pattern_ = new_transaction_pattern; | |
| 3191 } | |
| 3192 | |
| 3193 void HttpCache::Transaction::RecordHistograms() { | |
| 3194 DCHECK_NE(PATTERN_UNDEFINED, transaction_pattern_); | |
| 3195 if (!cache_.get() || !cache_->GetCurrentBackend() || | |
| 3196 cache_->GetCurrentBackend()->GetCacheType() != DISK_CACHE || | |
| 3197 cache_->mode() != NORMAL || request_->method != "GET") { | |
| 3198 return; | |
| 3199 } | |
| 3200 UMA_HISTOGRAM_ENUMERATION( | |
| 3201 "HttpCache.Pattern", transaction_pattern_, PATTERN_MAX); | |
| 3202 if (transaction_pattern_ == PATTERN_NOT_COVERED) | |
| 3203 return; | |
| 3204 DCHECK(!range_requested_); | |
| 3205 DCHECK(!first_cache_access_since_.is_null()); | |
| 3206 | |
| 3207 TimeDelta total_time = base::TimeTicks::Now() - first_cache_access_since_; | |
| 3208 | |
| 3209 UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone", total_time); | |
| 3210 | |
| 3211 bool did_send_request = !send_request_since_.is_null(); | |
| 3212 DCHECK( | |
| 3213 (did_send_request && | |
| 3214 (transaction_pattern_ == PATTERN_ENTRY_NOT_CACHED || | |
| 3215 transaction_pattern_ == PATTERN_ENTRY_VALIDATED || | |
| 3216 transaction_pattern_ == PATTERN_ENTRY_UPDATED || | |
| 3217 transaction_pattern_ == PATTERN_ENTRY_CANT_CONDITIONALIZE)) || | |
| 3218 (!did_send_request && transaction_pattern_ == PATTERN_ENTRY_USED)); | |
| 3219 | |
| 3220 if (!did_send_request) { | |
| 3221 DCHECK(transaction_pattern_ == PATTERN_ENTRY_USED); | |
| 3222 UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.Used", total_time); | |
| 3223 return; | |
| 3224 } | |
| 3225 | |
| 3226 TimeDelta before_send_time = send_request_since_ - first_cache_access_since_; | |
| 3227 int64 before_send_percent = (total_time.ToInternalValue() == 0) ? | |
| 3228 0 : before_send_time * 100 / total_time; | |
| 3229 DCHECK_GE(before_send_percent, 0); | |
| 3230 DCHECK_LE(before_send_percent, 100); | |
| 3231 base::HistogramBase::Sample before_send_sample = | |
| 3232 static_cast<base::HistogramBase::Sample>(before_send_percent); | |
| 3233 | |
| 3234 UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.SentRequest", total_time); | |
| 3235 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend", before_send_time); | |
| 3236 UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend", before_send_sample); | |
| 3237 | |
| 3238 // TODO(gavinp): Remove or minimize these histograms, particularly the ones | |
| 3239 // below this comment after we have received initial data. | |
| 3240 switch (transaction_pattern_) { | |
| 3241 case PATTERN_ENTRY_CANT_CONDITIONALIZE: { | |
| 3242 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.CantConditionalize", | |
| 3243 before_send_time); | |
| 3244 UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.CantConditionalize", | |
| 3245 before_send_sample); | |
| 3246 break; | |
| 3247 } | |
| 3248 case PATTERN_ENTRY_NOT_CACHED: { | |
| 3249 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.NotCached", before_send_time); | |
| 3250 UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.NotCached", | |
| 3251 before_send_sample); | |
| 3252 break; | |
| 3253 } | |
| 3254 case PATTERN_ENTRY_VALIDATED: { | |
| 3255 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Validated", before_send_time); | |
| 3256 UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.Validated", | |
| 3257 before_send_sample); | |
| 3258 break; | |
| 3259 } | |
| 3260 case PATTERN_ENTRY_UPDATED: { | |
| 3261 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Updated", before_send_time); | |
| 3262 UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.Updated", | |
| 3263 before_send_sample); | |
| 3264 break; | |
| 3265 } | |
| 3266 default: | |
| 3267 NOTREACHED(); | |
| 3268 } | |
| 3269 } | |
| 3270 | |
| 3271 void HttpCache::Transaction::OnIOComplete(int result) { | |
| 3272 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 3273 tracked_objects::ScopedTracker tracking_profile( | |
| 3274 FROM_HERE_WITH_EXPLICIT_FUNCTION("422516 Transaction::OnIOComplete")); | |
| 3275 | |
| 3276 DoLoop(result); | |
| 3277 } | |
| 3278 | |
| 3279 } // namespace net | |
| OLD | NEW |