| 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.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/compiler_specific.h" | |
| 10 | |
| 11 #if defined(OS_POSIX) | |
| 12 #include <unistd.h> | |
| 13 #endif | |
| 14 | |
| 15 #include "base/bind.h" | |
| 16 #include "base/bind_helpers.h" | |
| 17 #include "base/callback.h" | |
| 18 #include "base/files/file_util.h" | |
| 19 #include "base/format_macros.h" | |
| 20 #include "base/location.h" | |
| 21 #include "base/memory/ref_counted.h" | |
| 22 #include "base/message_loop/message_loop.h" | |
| 23 #include "base/metrics/field_trial.h" | |
| 24 #include "base/metrics/histogram.h" | |
| 25 #include "base/pickle.h" | |
| 26 #include "base/profiler/scoped_tracker.h" | |
| 27 #include "base/stl_util.h" | |
| 28 #include "base/strings/string_number_conversions.h" | |
| 29 #include "base/strings/string_util.h" | |
| 30 #include "base/strings/stringprintf.h" | |
| 31 #include "base/threading/worker_pool.h" | |
| 32 #include "base/time/default_clock.h" | |
| 33 #include "base/time/time.h" | |
| 34 #include "net/base/cache_type.h" | |
| 35 #include "net/base/io_buffer.h" | |
| 36 #include "net/base/load_flags.h" | |
| 37 #include "net/base/net_errors.h" | |
| 38 #include "net/base/network_delegate.h" | |
| 39 #include "net/base/upload_data_stream.h" | |
| 40 #include "net/disk_cache/disk_cache.h" | |
| 41 #include "net/http/disk_based_cert_cache.h" | |
| 42 #include "net/http/disk_cache_based_quic_server_info.h" | |
| 43 #include "net/http/http_cache_transaction.h" | |
| 44 #include "net/http/http_network_layer.h" | |
| 45 #include "net/http/http_network_session.h" | |
| 46 #include "net/http/http_request_info.h" | |
| 47 #include "net/http/http_response_headers.h" | |
| 48 #include "net/http/http_response_info.h" | |
| 49 #include "net/http/http_util.h" | |
| 50 #include "net/quic/crypto/quic_server_info.h" | |
| 51 | |
| 52 namespace { | |
| 53 | |
| 54 bool UseCertCache() { | |
| 55 return base::FieldTrialList::FindFullName("CertCacheTrial") == | |
| 56 "ExperimentGroup"; | |
| 57 } | |
| 58 | |
| 59 // Adaptor to delete a file on a worker thread. | |
| 60 void DeletePath(base::FilePath path) { | |
| 61 base::DeleteFile(path, false); | |
| 62 } | |
| 63 | |
| 64 } // namespace | |
| 65 | |
| 66 namespace net { | |
| 67 | |
| 68 HttpCache::DefaultBackend::DefaultBackend( | |
| 69 CacheType type, | |
| 70 BackendType backend_type, | |
| 71 const base::FilePath& path, | |
| 72 int max_bytes, | |
| 73 const scoped_refptr<base::SingleThreadTaskRunner>& thread) | |
| 74 : type_(type), | |
| 75 backend_type_(backend_type), | |
| 76 path_(path), | |
| 77 max_bytes_(max_bytes), | |
| 78 thread_(thread) { | |
| 79 } | |
| 80 | |
| 81 HttpCache::DefaultBackend::~DefaultBackend() {} | |
| 82 | |
| 83 // static | |
| 84 HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) { | |
| 85 return new DefaultBackend(MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT, | |
| 86 base::FilePath(), max_bytes, NULL); | |
| 87 } | |
| 88 | |
| 89 int HttpCache::DefaultBackend::CreateBackend( | |
| 90 NetLog* net_log, scoped_ptr<disk_cache::Backend>* backend, | |
| 91 const CompletionCallback& callback) { | |
| 92 DCHECK_GE(max_bytes_, 0); | |
| 93 return disk_cache::CreateCacheBackend(type_, | |
| 94 backend_type_, | |
| 95 path_, | |
| 96 max_bytes_, | |
| 97 true, | |
| 98 thread_, | |
| 99 net_log, | |
| 100 backend, | |
| 101 callback); | |
| 102 } | |
| 103 | |
| 104 //----------------------------------------------------------------------------- | |
| 105 | |
| 106 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry) | |
| 107 : disk_entry(entry), | |
| 108 writer(NULL), | |
| 109 will_process_pending_queue(false), | |
| 110 doomed(false) { | |
| 111 } | |
| 112 | |
| 113 HttpCache::ActiveEntry::~ActiveEntry() { | |
| 114 if (disk_entry) { | |
| 115 disk_entry->Close(); | |
| 116 disk_entry = NULL; | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 //----------------------------------------------------------------------------- | |
| 121 | |
| 122 // This structure keeps track of work items that are attempting to create or | |
| 123 // open cache entries or the backend itself. | |
| 124 struct HttpCache::PendingOp { | |
| 125 PendingOp() : disk_entry(NULL), writer(NULL) {} | |
| 126 ~PendingOp() {} | |
| 127 | |
| 128 disk_cache::Entry* disk_entry; | |
| 129 scoped_ptr<disk_cache::Backend> backend; | |
| 130 WorkItem* writer; | |
| 131 CompletionCallback callback; // BackendCallback. | |
| 132 WorkItemList pending_queue; | |
| 133 }; | |
| 134 | |
| 135 //----------------------------------------------------------------------------- | |
| 136 | |
| 137 // The type of operation represented by a work item. | |
| 138 enum WorkItemOperation { | |
| 139 WI_CREATE_BACKEND, | |
| 140 WI_OPEN_ENTRY, | |
| 141 WI_CREATE_ENTRY, | |
| 142 WI_DOOM_ENTRY | |
| 143 }; | |
| 144 | |
| 145 // A work item encapsulates a single request to the backend with all the | |
| 146 // information needed to complete that request. | |
| 147 class HttpCache::WorkItem { | |
| 148 public: | |
| 149 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry) | |
| 150 : operation_(operation), | |
| 151 trans_(trans), | |
| 152 entry_(entry), | |
| 153 backend_(NULL) {} | |
| 154 WorkItem(WorkItemOperation operation, Transaction* trans, | |
| 155 const net::CompletionCallback& cb, disk_cache::Backend** backend) | |
| 156 : operation_(operation), | |
| 157 trans_(trans), | |
| 158 entry_(NULL), | |
| 159 callback_(cb), | |
| 160 backend_(backend) {} | |
| 161 ~WorkItem() {} | |
| 162 | |
| 163 // Calls back the transaction with the result of the operation. | |
| 164 void NotifyTransaction(int result, ActiveEntry* entry) { | |
| 165 DCHECK(!entry || entry->disk_entry); | |
| 166 if (entry_) | |
| 167 *entry_ = entry; | |
| 168 if (trans_) | |
| 169 trans_->io_callback().Run(result); | |
| 170 } | |
| 171 | |
| 172 // Notifies the caller about the operation completion. Returns true if the | |
| 173 // callback was invoked. | |
| 174 bool DoCallback(int result, disk_cache::Backend* backend) { | |
| 175 if (backend_) | |
| 176 *backend_ = backend; | |
| 177 if (!callback_.is_null()) { | |
| 178 callback_.Run(result); | |
| 179 return true; | |
| 180 } | |
| 181 return false; | |
| 182 } | |
| 183 | |
| 184 WorkItemOperation operation() { return operation_; } | |
| 185 void ClearTransaction() { trans_ = NULL; } | |
| 186 void ClearEntry() { entry_ = NULL; } | |
| 187 void ClearCallback() { callback_.Reset(); } | |
| 188 bool Matches(Transaction* trans) const { return trans == trans_; } | |
| 189 bool IsValid() const { return trans_ || entry_ || !callback_.is_null(); } | |
| 190 | |
| 191 private: | |
| 192 WorkItemOperation operation_; | |
| 193 Transaction* trans_; | |
| 194 ActiveEntry** entry_; | |
| 195 net::CompletionCallback callback_; // User callback. | |
| 196 disk_cache::Backend** backend_; | |
| 197 }; | |
| 198 | |
| 199 //----------------------------------------------------------------------------- | |
| 200 | |
| 201 // This class encapsulates a transaction whose only purpose is to write metadata | |
| 202 // to a given entry. | |
| 203 class HttpCache::MetadataWriter { | |
| 204 public: | |
| 205 explicit MetadataWriter(HttpCache::Transaction* trans) | |
| 206 : transaction_(trans), | |
| 207 verified_(false), | |
| 208 buf_len_(0) { | |
| 209 } | |
| 210 | |
| 211 ~MetadataWriter() {} | |
| 212 | |
| 213 // Implements the bulk of HttpCache::WriteMetadata. | |
| 214 void Write(const GURL& url, | |
| 215 double expected_response_time, | |
| 216 IOBuffer* buf, | |
| 217 int buf_len); | |
| 218 | |
| 219 private: | |
| 220 void VerifyResponse(int result); | |
| 221 void SelfDestroy(); | |
| 222 void OnIOComplete(int result); | |
| 223 | |
| 224 scoped_ptr<HttpCache::Transaction> transaction_; | |
| 225 bool verified_; | |
| 226 scoped_refptr<IOBuffer> buf_; | |
| 227 int buf_len_; | |
| 228 double expected_response_time_; | |
| 229 HttpRequestInfo request_info_; | |
| 230 DISALLOW_COPY_AND_ASSIGN(MetadataWriter); | |
| 231 }; | |
| 232 | |
| 233 void HttpCache::MetadataWriter::Write(const GURL& url, | |
| 234 double expected_response_time, | |
| 235 IOBuffer* buf, | |
| 236 int buf_len) { | |
| 237 DCHECK_GT(buf_len, 0); | |
| 238 DCHECK(buf); | |
| 239 DCHECK(buf->data()); | |
| 240 request_info_.url = url; | |
| 241 request_info_.method = "GET"; | |
| 242 request_info_.load_flags = LOAD_ONLY_FROM_CACHE; | |
| 243 | |
| 244 expected_response_time_ = expected_response_time; | |
| 245 buf_ = buf; | |
| 246 buf_len_ = buf_len; | |
| 247 verified_ = false; | |
| 248 | |
| 249 int rv = transaction_->Start( | |
| 250 &request_info_, | |
| 251 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)), | |
| 252 BoundNetLog()); | |
| 253 if (rv != ERR_IO_PENDING) | |
| 254 VerifyResponse(rv); | |
| 255 } | |
| 256 | |
| 257 void HttpCache::MetadataWriter::VerifyResponse(int result) { | |
| 258 verified_ = true; | |
| 259 if (result != OK) | |
| 260 return SelfDestroy(); | |
| 261 | |
| 262 const HttpResponseInfo* response_info = transaction_->GetResponseInfo(); | |
| 263 DCHECK(response_info->was_cached); | |
| 264 if (response_info->response_time.ToDoubleT() != expected_response_time_) | |
| 265 return SelfDestroy(); | |
| 266 | |
| 267 result = transaction_->WriteMetadata( | |
| 268 buf_.get(), | |
| 269 buf_len_, | |
| 270 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this))); | |
| 271 if (result != ERR_IO_PENDING) | |
| 272 SelfDestroy(); | |
| 273 } | |
| 274 | |
| 275 void HttpCache::MetadataWriter::SelfDestroy() { | |
| 276 delete this; | |
| 277 } | |
| 278 | |
| 279 void HttpCache::MetadataWriter::OnIOComplete(int result) { | |
| 280 if (!verified_) | |
| 281 return VerifyResponse(result); | |
| 282 SelfDestroy(); | |
| 283 } | |
| 284 | |
| 285 //----------------------------------------------------------------------------- | |
| 286 | |
| 287 class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory { | |
| 288 public: | |
| 289 explicit QuicServerInfoFactoryAdaptor(HttpCache* http_cache) | |
| 290 : http_cache_(http_cache) { | |
| 291 } | |
| 292 | |
| 293 QuicServerInfo* GetForServer(const QuicServerId& server_id) override { | |
| 294 return new DiskCacheBasedQuicServerInfo(server_id, http_cache_); | |
| 295 } | |
| 296 | |
| 297 private: | |
| 298 HttpCache* const http_cache_; | |
| 299 }; | |
| 300 | |
| 301 //----------------------------------------------------------------------------- | |
| 302 | |
| 303 class HttpCache::AsyncValidation { | |
| 304 public: | |
| 305 AsyncValidation(const HttpRequestInfo& original_request, HttpCache* cache) | |
| 306 : request_(original_request), cache_(cache) {} | |
| 307 ~AsyncValidation() {} | |
| 308 | |
| 309 void Start(const BoundNetLog& net_log, | |
| 310 scoped_ptr<Transaction> transaction, | |
| 311 NetworkDelegate* network_delegate); | |
| 312 | |
| 313 private: | |
| 314 void OnStarted(int result); | |
| 315 void DoRead(); | |
| 316 void OnRead(int result); | |
| 317 | |
| 318 // Terminate this request with net error code |result|. Logs the transaction | |
| 319 // result and asks HttpCache to delete this object. | |
| 320 // If there was a client or server certificate error, it cannot be recovered | |
| 321 // asynchronously, so we need to prevent future attempts to asynchronously | |
| 322 // fetch the resource. In this case, the cache entry is doomed. | |
| 323 void Terminate(int result); | |
| 324 | |
| 325 HttpRequestInfo request_; | |
| 326 scoped_refptr<IOBuffer> buf_; | |
| 327 CompletionCallback read_callback_; | |
| 328 scoped_ptr<Transaction> transaction_; | |
| 329 base::Time start_time_; | |
| 330 | |
| 331 // The HttpCache object owns this object. This object is always deleted before | |
| 332 // the pointer to the cache becomes invalid. | |
| 333 HttpCache* cache_; | |
| 334 | |
| 335 DISALLOW_COPY_AND_ASSIGN(AsyncValidation); | |
| 336 }; | |
| 337 | |
| 338 void HttpCache::AsyncValidation::Start(const BoundNetLog& net_log, | |
| 339 scoped_ptr<Transaction> transaction, | |
| 340 NetworkDelegate* network_delegate) { | |
| 341 transaction_ = transaction.Pass(); | |
| 342 if (network_delegate) { | |
| 343 // This code is necessary to enable async transactions to pass over the | |
| 344 // data-reduction proxy. This is a violation of the "once-and-only-once" | |
| 345 // principle, since it copies code from URLRequestHttpJob. We cannot use the | |
| 346 // original callback passed to HttpCache::Transaction by URLRequestHttpJob | |
| 347 // as it will only be valid as long as the URLRequestHttpJob object is | |
| 348 // alive, and that object will be deleted as soon as the synchronous request | |
| 349 // completes. | |
| 350 // | |
| 351 // This code is also an encapsulation violation. We are exploiting the fact | |
| 352 // that the |request| parameter to NotifyBeforeSendProxyHeaders() is never | |
| 353 // actually used for anything, and so can be NULL. | |
| 354 // | |
| 355 // TODO(ricea): Do this better. | |
| 356 transaction_->SetBeforeProxyHeadersSentCallback( | |
| 357 base::Bind(&NetworkDelegate::NotifyBeforeSendProxyHeaders, | |
| 358 base::Unretained(network_delegate), | |
| 359 static_cast<URLRequest*>(NULL))); | |
| 360 // The above use of base::Unretained is safe because the NetworkDelegate has | |
| 361 // to live at least as long as the HttpNetworkSession which has to live as | |
| 362 // least as long as the HttpNetworkLayer which has to live at least as long | |
| 363 // this HttpCache object. | |
| 364 } | |
| 365 | |
| 366 DCHECK_EQ(0, request_.load_flags & LOAD_ASYNC_REVALIDATION); | |
| 367 request_.load_flags |= LOAD_ASYNC_REVALIDATION; | |
| 368 start_time_ = cache_->clock()->Now(); | |
| 369 // This use of base::Unretained is safe because |transaction_| is owned by | |
| 370 // this object. | |
| 371 read_callback_ = base::Bind(&AsyncValidation::OnRead, base::Unretained(this)); | |
| 372 // This use of base::Unretained is safe as above. | |
| 373 int rv = transaction_->Start( | |
| 374 &request_, | |
| 375 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)), | |
| 376 net_log); | |
| 377 | |
| 378 if (rv == ERR_IO_PENDING) | |
| 379 return; | |
| 380 | |
| 381 OnStarted(rv); | |
| 382 } | |
| 383 | |
| 384 void HttpCache::AsyncValidation::OnStarted(int result) { | |
| 385 if (result != OK) { | |
| 386 DVLOG(1) << "Asynchronous transaction start failed for " << request_.url; | |
| 387 Terminate(result); | |
| 388 return; | |
| 389 } | |
| 390 | |
| 391 while (transaction_->IsReadyToRestartForAuth()) { | |
| 392 // This code is based on URLRequestHttpJob::RestartTransactionWithAuth, | |
| 393 // however when we do this here cookies on the response will not be | |
| 394 // stored. Fortunately only a tiny number of sites set cookies on 401 | |
| 395 // responses, and none of them use stale-while-revalidate. | |
| 396 result = transaction_->RestartWithAuth( | |
| 397 AuthCredentials(), | |
| 398 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this))); | |
| 399 if (result == ERR_IO_PENDING) | |
| 400 return; | |
| 401 if (result != OK) { | |
| 402 DVLOG(1) << "Synchronous transaction restart with auth failed for " | |
| 403 << request_.url; | |
| 404 Terminate(result); | |
| 405 return; | |
| 406 } | |
| 407 } | |
| 408 | |
| 409 DoRead(); | |
| 410 } | |
| 411 | |
| 412 void HttpCache::AsyncValidation::DoRead() { | |
| 413 const size_t kBufSize = 4096; | |
| 414 if (!buf_.get()) | |
| 415 buf_ = new IOBuffer(kBufSize); | |
| 416 | |
| 417 int rv = 0; | |
| 418 do { | |
| 419 rv = transaction_->Read(buf_.get(), kBufSize, read_callback_); | |
| 420 } while (rv > 0); | |
| 421 | |
| 422 if (rv == ERR_IO_PENDING) | |
| 423 return; | |
| 424 | |
| 425 OnRead(rv); | |
| 426 } | |
| 427 | |
| 428 void HttpCache::AsyncValidation::OnRead(int result) { | |
| 429 if (result > 0) { | |
| 430 DoRead(); | |
| 431 return; | |
| 432 } | |
| 433 Terminate(result); | |
| 434 } | |
| 435 | |
| 436 void HttpCache::AsyncValidation::Terminate(int result) { | |
| 437 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED || IsCertificateError(result)) { | |
| 438 // We should not attempt to access this resource asynchronously again until | |
| 439 // the certificate problem has been resolved. | |
| 440 // TODO(ricea): For ERR_SSL_CLIENT_AUTH_CERT_NEEDED, mark the entry as | |
| 441 // requiring synchronous revalidation rather than just deleting it. Other | |
| 442 // certificate errors cause the resource to be considered uncacheable | |
| 443 // anyway. | |
| 444 cache_->DoomEntry(transaction_->key(), transaction_.get()); | |
| 445 } | |
| 446 base::TimeDelta duration = cache_->clock()->Now() - start_time_; | |
| 447 UMA_HISTOGRAM_TIMES("HttpCache.AsyncValidationDuration", duration); | |
| 448 transaction_->net_log().EndEventWithNetErrorCode( | |
| 449 NetLog::TYPE_ASYNC_REVALIDATION, result); | |
| 450 cache_->DeleteAsyncValidation(cache_->GenerateCacheKey(&request_)); | |
| 451 // |this| is deleted. | |
| 452 } | |
| 453 | |
| 454 //----------------------------------------------------------------------------- | |
| 455 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params, | |
| 456 BackendFactory* backend_factory) | |
| 457 : net_log_(params.net_log), | |
| 458 backend_factory_(backend_factory), | |
| 459 building_backend_(false), | |
| 460 bypass_lock_for_test_(false), | |
| 461 fail_conditionalization_for_test_(false), | |
| 462 use_stale_while_revalidate_(params.use_stale_while_revalidate), | |
| 463 mode_(NORMAL), | |
| 464 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))), | |
| 465 clock_(new base::DefaultClock()), | |
| 466 weak_factory_(this) { | |
| 467 SetupQuicServerInfoFactory(network_layer_->GetSession()); | |
| 468 } | |
| 469 | |
| 470 | |
| 471 // This call doesn't change the shared |session|'s QuicServerInfoFactory because | |
| 472 // |session| is shared. | |
| 473 HttpCache::HttpCache(HttpNetworkSession* session, | |
| 474 BackendFactory* backend_factory) | |
| 475 : net_log_(session->net_log()), | |
| 476 backend_factory_(backend_factory), | |
| 477 building_backend_(false), | |
| 478 bypass_lock_for_test_(false), | |
| 479 fail_conditionalization_for_test_(false), | |
| 480 use_stale_while_revalidate_(session->params().use_stale_while_revalidate), | |
| 481 mode_(NORMAL), | |
| 482 network_layer_(new HttpNetworkLayer(session)), | |
| 483 clock_(new base::DefaultClock()), | |
| 484 weak_factory_(this) { | |
| 485 } | |
| 486 | |
| 487 HttpCache::HttpCache(HttpTransactionFactory* network_layer, | |
| 488 NetLog* net_log, | |
| 489 BackendFactory* backend_factory) | |
| 490 : net_log_(net_log), | |
| 491 backend_factory_(backend_factory), | |
| 492 building_backend_(false), | |
| 493 bypass_lock_for_test_(false), | |
| 494 fail_conditionalization_for_test_(false), | |
| 495 use_stale_while_revalidate_(false), | |
| 496 mode_(NORMAL), | |
| 497 network_layer_(network_layer), | |
| 498 clock_(new base::DefaultClock()), | |
| 499 weak_factory_(this) { | |
| 500 SetupQuicServerInfoFactory(network_layer_->GetSession()); | |
| 501 HttpNetworkSession* session = network_layer_->GetSession(); | |
| 502 if (session) | |
| 503 use_stale_while_revalidate_ = session->params().use_stale_while_revalidate; | |
| 504 } | |
| 505 | |
| 506 HttpCache::~HttpCache() { | |
| 507 // Transactions should see an invalid cache after this point; otherwise they | |
| 508 // could see an inconsistent object (half destroyed). | |
| 509 weak_factory_.InvalidateWeakPtrs(); | |
| 510 | |
| 511 // If we have any active entries remaining, then we need to deactivate them. | |
| 512 // We may have some pending calls to OnProcessPendingQueue, but since those | |
| 513 // won't run (due to our destruction), we can simply ignore the corresponding | |
| 514 // will_process_pending_queue flag. | |
| 515 while (!active_entries_.empty()) { | |
| 516 ActiveEntry* entry = active_entries_.begin()->second; | |
| 517 entry->will_process_pending_queue = false; | |
| 518 entry->pending_queue.clear(); | |
| 519 entry->readers.clear(); | |
| 520 entry->writer = NULL; | |
| 521 DeactivateEntry(entry); | |
| 522 } | |
| 523 | |
| 524 STLDeleteElements(&doomed_entries_); | |
| 525 STLDeleteValues(&async_validations_); | |
| 526 | |
| 527 // Before deleting pending_ops_, we have to make sure that the disk cache is | |
| 528 // done with said operations, or it will attempt to use deleted data. | |
| 529 cert_cache_.reset(); | |
| 530 disk_cache_.reset(); | |
| 531 | |
| 532 PendingOpsMap::iterator pending_it = pending_ops_.begin(); | |
| 533 for (; pending_it != pending_ops_.end(); ++pending_it) { | |
| 534 // We are not notifying the transactions about the cache going away, even | |
| 535 // though they are waiting for a callback that will never fire. | |
| 536 PendingOp* pending_op = pending_it->second; | |
| 537 delete pending_op->writer; | |
| 538 bool delete_pending_op = true; | |
| 539 if (building_backend_) { | |
| 540 // If we don't have a backend, when its construction finishes it will | |
| 541 // deliver the callbacks. | |
| 542 if (!pending_op->callback.is_null()) { | |
| 543 // If not null, the callback will delete the pending operation later. | |
| 544 delete_pending_op = false; | |
| 545 } | |
| 546 } else { | |
| 547 pending_op->callback.Reset(); | |
| 548 } | |
| 549 | |
| 550 STLDeleteElements(&pending_op->pending_queue); | |
| 551 if (delete_pending_op) | |
| 552 delete pending_op; | |
| 553 } | |
| 554 } | |
| 555 | |
| 556 int HttpCache::GetBackend(disk_cache::Backend** backend, | |
| 557 const CompletionCallback& callback) { | |
| 558 DCHECK(!callback.is_null()); | |
| 559 | |
| 560 if (disk_cache_.get()) { | |
| 561 *backend = disk_cache_.get(); | |
| 562 return OK; | |
| 563 } | |
| 564 | |
| 565 return CreateBackend(backend, callback); | |
| 566 } | |
| 567 | |
| 568 disk_cache::Backend* HttpCache::GetCurrentBackend() const { | |
| 569 return disk_cache_.get(); | |
| 570 } | |
| 571 | |
| 572 // static | |
| 573 bool HttpCache::ParseResponseInfo(const char* data, int len, | |
| 574 HttpResponseInfo* response_info, | |
| 575 bool* response_truncated) { | |
| 576 Pickle pickle(data, len); | |
| 577 return response_info->InitFromPickle(pickle, response_truncated); | |
| 578 } | |
| 579 | |
| 580 void HttpCache::WriteMetadata(const GURL& url, | |
| 581 RequestPriority priority, | |
| 582 double expected_response_time, | |
| 583 IOBuffer* buf, | |
| 584 int buf_len) { | |
| 585 if (!buf_len) | |
| 586 return; | |
| 587 | |
| 588 // Do lazy initialization of disk cache if needed. | |
| 589 if (!disk_cache_.get()) { | |
| 590 // We don't care about the result. | |
| 591 CreateBackend(NULL, net::CompletionCallback()); | |
| 592 } | |
| 593 | |
| 594 HttpCache::Transaction* trans = | |
| 595 new HttpCache::Transaction(priority, this); | |
| 596 MetadataWriter* writer = new MetadataWriter(trans); | |
| 597 | |
| 598 // The writer will self destruct when done. | |
| 599 writer->Write(url, expected_response_time, buf, buf_len); | |
| 600 } | |
| 601 | |
| 602 void HttpCache::CloseAllConnections() { | |
| 603 HttpNetworkSession* session = GetSession(); | |
| 604 if (session) | |
| 605 session->CloseAllConnections(); | |
| 606 } | |
| 607 | |
| 608 void HttpCache::CloseIdleConnections() { | |
| 609 HttpNetworkSession* session = GetSession(); | |
| 610 if (session) | |
| 611 session->CloseIdleConnections(); | |
| 612 } | |
| 613 | |
| 614 void HttpCache::OnExternalCacheHit(const GURL& url, | |
| 615 const std::string& http_method) { | |
| 616 if (!disk_cache_.get() || mode_ == DISABLE) | |
| 617 return; | |
| 618 | |
| 619 HttpRequestInfo request_info; | |
| 620 request_info.url = url; | |
| 621 request_info.method = http_method; | |
| 622 std::string key = GenerateCacheKey(&request_info); | |
| 623 disk_cache_->OnExternalCacheHit(key); | |
| 624 } | |
| 625 | |
| 626 void HttpCache::InitializeInfiniteCache(const base::FilePath& path) { | |
| 627 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes") | |
| 628 return; | |
| 629 base::WorkerPool::PostTask(FROM_HERE, base::Bind(&DeletePath, path), true); | |
| 630 } | |
| 631 | |
| 632 int HttpCache::CreateTransaction(RequestPriority priority, | |
| 633 scoped_ptr<HttpTransaction>* trans) { | |
| 634 // Do lazy initialization of disk cache if needed. | |
| 635 if (!disk_cache_.get()) { | |
| 636 // We don't care about the result. | |
| 637 CreateBackend(NULL, net::CompletionCallback()); | |
| 638 } | |
| 639 | |
| 640 HttpCache::Transaction* transaction = | |
| 641 new HttpCache::Transaction(priority, this); | |
| 642 if (bypass_lock_for_test_) | |
| 643 transaction->BypassLockForTest(); | |
| 644 if (fail_conditionalization_for_test_) | |
| 645 transaction->FailConditionalizationForTest(); | |
| 646 | |
| 647 trans->reset(transaction); | |
| 648 return OK; | |
| 649 } | |
| 650 | |
| 651 HttpCache* HttpCache::GetCache() { | |
| 652 return this; | |
| 653 } | |
| 654 | |
| 655 HttpNetworkSession* HttpCache::GetSession() { | |
| 656 return network_layer_->GetSession(); | |
| 657 } | |
| 658 | |
| 659 scoped_ptr<HttpTransactionFactory> | |
| 660 HttpCache::SetHttpNetworkTransactionFactoryForTesting( | |
| 661 scoped_ptr<HttpTransactionFactory> new_network_layer) { | |
| 662 scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass()); | |
| 663 network_layer_ = new_network_layer.Pass(); | |
| 664 return old_network_layer.Pass(); | |
| 665 } | |
| 666 | |
| 667 //----------------------------------------------------------------------------- | |
| 668 | |
| 669 int HttpCache::CreateBackend(disk_cache::Backend** backend, | |
| 670 const net::CompletionCallback& callback) { | |
| 671 if (!backend_factory_.get()) | |
| 672 return ERR_FAILED; | |
| 673 | |
| 674 building_backend_ = true; | |
| 675 | |
| 676 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback, | |
| 677 backend)); | |
| 678 | |
| 679 // This is the only operation that we can do that is not related to any given | |
| 680 // entry, so we use an empty key for it. | |
| 681 PendingOp* pending_op = GetPendingOp(std::string()); | |
| 682 if (pending_op->writer) { | |
| 683 if (!callback.is_null()) | |
| 684 pending_op->pending_queue.push_back(item.release()); | |
| 685 return ERR_IO_PENDING; | |
| 686 } | |
| 687 | |
| 688 DCHECK(pending_op->pending_queue.empty()); | |
| 689 | |
| 690 pending_op->writer = item.release(); | |
| 691 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, | |
| 692 GetWeakPtr(), pending_op); | |
| 693 | |
| 694 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend, | |
| 695 pending_op->callback); | |
| 696 if (rv != ERR_IO_PENDING) { | |
| 697 pending_op->writer->ClearCallback(); | |
| 698 pending_op->callback.Run(rv); | |
| 699 } | |
| 700 | |
| 701 return rv; | |
| 702 } | |
| 703 | |
| 704 int HttpCache::GetBackendForTransaction(Transaction* trans) { | |
| 705 if (disk_cache_.get()) | |
| 706 return OK; | |
| 707 | |
| 708 if (!building_backend_) | |
| 709 return ERR_FAILED; | |
| 710 | |
| 711 WorkItem* item = new WorkItem( | |
| 712 WI_CREATE_BACKEND, trans, net::CompletionCallback(), NULL); | |
| 713 PendingOp* pending_op = GetPendingOp(std::string()); | |
| 714 DCHECK(pending_op->writer); | |
| 715 pending_op->pending_queue.push_back(item); | |
| 716 return ERR_IO_PENDING; | |
| 717 } | |
| 718 | |
| 719 // Generate a key that can be used inside the cache. | |
| 720 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) { | |
| 721 // Strip out the reference, username, and password sections of the URL. | |
| 722 std::string url = HttpUtil::SpecForRequest(request->url); | |
| 723 | |
| 724 DCHECK(mode_ != DISABLE); | |
| 725 if (mode_ == NORMAL) { | |
| 726 // No valid URL can begin with numerals, so we should not have to worry | |
| 727 // about collisions with normal URLs. | |
| 728 if (request->upload_data_stream && | |
| 729 request->upload_data_stream->identifier()) { | |
| 730 url.insert(0, base::StringPrintf( | |
| 731 "%" PRId64 "/", request->upload_data_stream->identifier())); | |
| 732 } | |
| 733 return url; | |
| 734 } | |
| 735 | |
| 736 // In playback and record mode, we cache everything. | |
| 737 | |
| 738 // Lazily initialize. | |
| 739 if (playback_cache_map_ == NULL) | |
| 740 playback_cache_map_.reset(new PlaybackCacheMap()); | |
| 741 | |
| 742 // Each time we request an item from the cache, we tag it with a | |
| 743 // generation number. During playback, multiple fetches for the same | |
| 744 // item will use the same generation number and pull the proper | |
| 745 // instance of an URL from the cache. | |
| 746 int generation = 0; | |
| 747 DCHECK(playback_cache_map_ != NULL); | |
| 748 if (playback_cache_map_->find(url) != playback_cache_map_->end()) | |
| 749 generation = (*playback_cache_map_)[url]; | |
| 750 (*playback_cache_map_)[url] = generation + 1; | |
| 751 | |
| 752 // The key into the cache is GENERATION # + METHOD + URL. | |
| 753 std::string result = base::IntToString(generation); | |
| 754 result.append(request->method); | |
| 755 result.append(url); | |
| 756 return result; | |
| 757 } | |
| 758 | |
| 759 void HttpCache::DoomActiveEntry(const std::string& key) { | |
| 760 ActiveEntriesMap::iterator it = active_entries_.find(key); | |
| 761 if (it == active_entries_.end()) | |
| 762 return; | |
| 763 | |
| 764 // This is not a performance critical operation, this is handling an error | |
| 765 // condition so it is OK to look up the entry again. | |
| 766 int rv = DoomEntry(key, NULL); | |
| 767 DCHECK_EQ(OK, rv); | |
| 768 } | |
| 769 | |
| 770 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) { | |
| 771 // Need to abandon the ActiveEntry, but any transaction attached to the entry | |
| 772 // should not be impacted. Dooming an entry only means that it will no | |
| 773 // longer be returned by FindActiveEntry (and it will also be destroyed once | |
| 774 // all consumers are finished with the entry). | |
| 775 ActiveEntriesMap::iterator it = active_entries_.find(key); | |
| 776 if (it == active_entries_.end()) { | |
| 777 DCHECK(trans); | |
| 778 return AsyncDoomEntry(key, trans); | |
| 779 } | |
| 780 | |
| 781 ActiveEntry* entry = it->second; | |
| 782 active_entries_.erase(it); | |
| 783 | |
| 784 // We keep track of doomed entries so that we can ensure that they are | |
| 785 // cleaned up properly when the cache is destroyed. | |
| 786 doomed_entries_.insert(entry); | |
| 787 | |
| 788 entry->disk_entry->Doom(); | |
| 789 entry->doomed = true; | |
| 790 | |
| 791 DCHECK(entry->writer || !entry->readers.empty() || | |
| 792 entry->will_process_pending_queue); | |
| 793 return OK; | |
| 794 } | |
| 795 | |
| 796 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) { | |
| 797 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL); | |
| 798 PendingOp* pending_op = GetPendingOp(key); | |
| 799 if (pending_op->writer) { | |
| 800 pending_op->pending_queue.push_back(item); | |
| 801 return ERR_IO_PENDING; | |
| 802 } | |
| 803 | |
| 804 DCHECK(pending_op->pending_queue.empty()); | |
| 805 | |
| 806 pending_op->writer = item; | |
| 807 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, | |
| 808 GetWeakPtr(), pending_op); | |
| 809 | |
| 810 int rv = disk_cache_->DoomEntry(key, pending_op->callback); | |
| 811 if (rv != ERR_IO_PENDING) { | |
| 812 item->ClearTransaction(); | |
| 813 pending_op->callback.Run(rv); | |
| 814 } | |
| 815 | |
| 816 return rv; | |
| 817 } | |
| 818 | |
| 819 void HttpCache::DoomMainEntryForUrl(const GURL& url) { | |
| 820 if (!disk_cache_) | |
| 821 return; | |
| 822 | |
| 823 HttpRequestInfo temp_info; | |
| 824 temp_info.url = url; | |
| 825 temp_info.method = "GET"; | |
| 826 std::string key = GenerateCacheKey(&temp_info); | |
| 827 | |
| 828 // Defer to DoomEntry if there is an active entry, otherwise call | |
| 829 // AsyncDoomEntry without triggering a callback. | |
| 830 if (active_entries_.count(key)) | |
| 831 DoomEntry(key, NULL); | |
| 832 else | |
| 833 AsyncDoomEntry(key, NULL); | |
| 834 } | |
| 835 | |
| 836 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) { | |
| 837 DCHECK(entry->doomed); | |
| 838 DCHECK(!entry->writer); | |
| 839 DCHECK(entry->readers.empty()); | |
| 840 DCHECK(entry->pending_queue.empty()); | |
| 841 | |
| 842 ActiveEntriesSet::iterator it = doomed_entries_.find(entry); | |
| 843 DCHECK(it != doomed_entries_.end()); | |
| 844 doomed_entries_.erase(it); | |
| 845 | |
| 846 delete entry; | |
| 847 } | |
| 848 | |
| 849 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) { | |
| 850 ActiveEntriesMap::const_iterator it = active_entries_.find(key); | |
| 851 return it != active_entries_.end() ? it->second : NULL; | |
| 852 } | |
| 853 | |
| 854 HttpCache::ActiveEntry* HttpCache::ActivateEntry( | |
| 855 disk_cache::Entry* disk_entry) { | |
| 856 DCHECK(!FindActiveEntry(disk_entry->GetKey())); | |
| 857 ActiveEntry* entry = new ActiveEntry(disk_entry); | |
| 858 active_entries_[disk_entry->GetKey()] = entry; | |
| 859 return entry; | |
| 860 } | |
| 861 | |
| 862 void HttpCache::DeactivateEntry(ActiveEntry* entry) { | |
| 863 DCHECK(!entry->will_process_pending_queue); | |
| 864 DCHECK(!entry->doomed); | |
| 865 DCHECK(!entry->writer); | |
| 866 DCHECK(entry->disk_entry); | |
| 867 DCHECK(entry->readers.empty()); | |
| 868 DCHECK(entry->pending_queue.empty()); | |
| 869 | |
| 870 std::string key = entry->disk_entry->GetKey(); | |
| 871 if (key.empty()) | |
| 872 return SlowDeactivateEntry(entry); | |
| 873 | |
| 874 ActiveEntriesMap::iterator it = active_entries_.find(key); | |
| 875 DCHECK(it != active_entries_.end()); | |
| 876 DCHECK(it->second == entry); | |
| 877 | |
| 878 active_entries_.erase(it); | |
| 879 delete entry; | |
| 880 } | |
| 881 | |
| 882 // We don't know this entry's key so we have to find it without it. | |
| 883 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) { | |
| 884 for (ActiveEntriesMap::iterator it = active_entries_.begin(); | |
| 885 it != active_entries_.end(); ++it) { | |
| 886 if (it->second == entry) { | |
| 887 active_entries_.erase(it); | |
| 888 delete entry; | |
| 889 break; | |
| 890 } | |
| 891 } | |
| 892 } | |
| 893 | |
| 894 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) { | |
| 895 DCHECK(!FindActiveEntry(key)); | |
| 896 | |
| 897 PendingOpsMap::const_iterator it = pending_ops_.find(key); | |
| 898 if (it != pending_ops_.end()) | |
| 899 return it->second; | |
| 900 | |
| 901 PendingOp* operation = new PendingOp(); | |
| 902 pending_ops_[key] = operation; | |
| 903 return operation; | |
| 904 } | |
| 905 | |
| 906 void HttpCache::DeletePendingOp(PendingOp* pending_op) { | |
| 907 std::string key; | |
| 908 if (pending_op->disk_entry) | |
| 909 key = pending_op->disk_entry->GetKey(); | |
| 910 | |
| 911 if (!key.empty()) { | |
| 912 PendingOpsMap::iterator it = pending_ops_.find(key); | |
| 913 DCHECK(it != pending_ops_.end()); | |
| 914 pending_ops_.erase(it); | |
| 915 } else { | |
| 916 for (PendingOpsMap::iterator it = pending_ops_.begin(); | |
| 917 it != pending_ops_.end(); ++it) { | |
| 918 if (it->second == pending_op) { | |
| 919 pending_ops_.erase(it); | |
| 920 break; | |
| 921 } | |
| 922 } | |
| 923 } | |
| 924 DCHECK(pending_op->pending_queue.empty()); | |
| 925 | |
| 926 delete pending_op; | |
| 927 } | |
| 928 | |
| 929 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry, | |
| 930 Transaction* trans) { | |
| 931 ActiveEntry* active_entry = FindActiveEntry(key); | |
| 932 if (active_entry) { | |
| 933 *entry = active_entry; | |
| 934 return OK; | |
| 935 } | |
| 936 | |
| 937 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry); | |
| 938 PendingOp* pending_op = GetPendingOp(key); | |
| 939 if (pending_op->writer) { | |
| 940 pending_op->pending_queue.push_back(item); | |
| 941 return ERR_IO_PENDING; | |
| 942 } | |
| 943 | |
| 944 DCHECK(pending_op->pending_queue.empty()); | |
| 945 | |
| 946 pending_op->writer = item; | |
| 947 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, | |
| 948 GetWeakPtr(), pending_op); | |
| 949 | |
| 950 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry), | |
| 951 pending_op->callback); | |
| 952 if (rv != ERR_IO_PENDING) { | |
| 953 item->ClearTransaction(); | |
| 954 pending_op->callback.Run(rv); | |
| 955 } | |
| 956 | |
| 957 return rv; | |
| 958 } | |
| 959 | |
| 960 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry, | |
| 961 Transaction* trans) { | |
| 962 if (FindActiveEntry(key)) { | |
| 963 return ERR_CACHE_RACE; | |
| 964 } | |
| 965 | |
| 966 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry); | |
| 967 PendingOp* pending_op = GetPendingOp(key); | |
| 968 if (pending_op->writer) { | |
| 969 pending_op->pending_queue.push_back(item); | |
| 970 return ERR_IO_PENDING; | |
| 971 } | |
| 972 | |
| 973 DCHECK(pending_op->pending_queue.empty()); | |
| 974 | |
| 975 pending_op->writer = item; | |
| 976 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, | |
| 977 GetWeakPtr(), pending_op); | |
| 978 | |
| 979 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry), | |
| 980 pending_op->callback); | |
| 981 if (rv != ERR_IO_PENDING) { | |
| 982 item->ClearTransaction(); | |
| 983 pending_op->callback.Run(rv); | |
| 984 } | |
| 985 | |
| 986 return rv; | |
| 987 } | |
| 988 | |
| 989 void HttpCache::DestroyEntry(ActiveEntry* entry) { | |
| 990 if (entry->doomed) { | |
| 991 FinalizeDoomedEntry(entry); | |
| 992 } else { | |
| 993 DeactivateEntry(entry); | |
| 994 } | |
| 995 } | |
| 996 | |
| 997 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) { | |
| 998 DCHECK(entry); | |
| 999 DCHECK(entry->disk_entry); | |
| 1000 | |
| 1001 // We implement a basic reader/writer lock for the disk cache entry. If | |
| 1002 // there is already a writer, then everyone has to wait for the writer to | |
| 1003 // finish before they can access the cache entry. There can be multiple | |
| 1004 // readers. | |
| 1005 // | |
| 1006 // NOTE: If the transaction can only write, then the entry should not be in | |
| 1007 // use (since any existing entry should have already been doomed). | |
| 1008 | |
| 1009 if (entry->writer || entry->will_process_pending_queue) { | |
| 1010 entry->pending_queue.push_back(trans); | |
| 1011 return ERR_IO_PENDING; | |
| 1012 } | |
| 1013 | |
| 1014 if (trans->mode() & Transaction::WRITE) { | |
| 1015 // transaction needs exclusive access to the entry | |
| 1016 if (entry->readers.empty()) { | |
| 1017 entry->writer = trans; | |
| 1018 } else { | |
| 1019 entry->pending_queue.push_back(trans); | |
| 1020 return ERR_IO_PENDING; | |
| 1021 } | |
| 1022 } else { | |
| 1023 // transaction needs read access to the entry | |
| 1024 entry->readers.push_back(trans); | |
| 1025 } | |
| 1026 | |
| 1027 // We do this before calling EntryAvailable to force any further calls to | |
| 1028 // AddTransactionToEntry to add their transaction to the pending queue, which | |
| 1029 // ensures FIFO ordering. | |
| 1030 if (!entry->writer && !entry->pending_queue.empty()) | |
| 1031 ProcessPendingQueue(entry); | |
| 1032 | |
| 1033 return OK; | |
| 1034 } | |
| 1035 | |
| 1036 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans, | |
| 1037 bool cancel) { | |
| 1038 // If we already posted a task to move on to the next transaction and this was | |
| 1039 // the writer, there is nothing to cancel. | |
| 1040 if (entry->will_process_pending_queue && entry->readers.empty()) | |
| 1041 return; | |
| 1042 | |
| 1043 if (entry->writer) { | |
| 1044 DCHECK(trans == entry->writer); | |
| 1045 | |
| 1046 // Assume there was a failure. | |
| 1047 bool success = false; | |
| 1048 if (cancel) { | |
| 1049 DCHECK(entry->disk_entry); | |
| 1050 // This is a successful operation in the sense that we want to keep the | |
| 1051 // entry. | |
| 1052 success = trans->AddTruncatedFlag(); | |
| 1053 // The previous operation may have deleted the entry. | |
| 1054 if (!trans->entry()) | |
| 1055 return; | |
| 1056 } | |
| 1057 DoneWritingToEntry(entry, success); | |
| 1058 } else { | |
| 1059 DoneReadingFromEntry(entry, trans); | |
| 1060 } | |
| 1061 } | |
| 1062 | |
| 1063 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) { | |
| 1064 DCHECK(entry->readers.empty()); | |
| 1065 | |
| 1066 entry->writer = NULL; | |
| 1067 | |
| 1068 if (success) { | |
| 1069 ProcessPendingQueue(entry); | |
| 1070 } else { | |
| 1071 DCHECK(!entry->will_process_pending_queue); | |
| 1072 | |
| 1073 // We failed to create this entry. | |
| 1074 TransactionList pending_queue; | |
| 1075 pending_queue.swap(entry->pending_queue); | |
| 1076 | |
| 1077 entry->disk_entry->Doom(); | |
| 1078 DestroyEntry(entry); | |
| 1079 | |
| 1080 // We need to do something about these pending entries, which now need to | |
| 1081 // be added to a new entry. | |
| 1082 while (!pending_queue.empty()) { | |
| 1083 // ERR_CACHE_RACE causes the transaction to restart the whole process. | |
| 1084 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE); | |
| 1085 pending_queue.pop_front(); | |
| 1086 } | |
| 1087 } | |
| 1088 } | |
| 1089 | |
| 1090 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) { | |
| 1091 DCHECK(!entry->writer); | |
| 1092 | |
| 1093 TransactionList::iterator it = | |
| 1094 std::find(entry->readers.begin(), entry->readers.end(), trans); | |
| 1095 DCHECK(it != entry->readers.end()); | |
| 1096 | |
| 1097 entry->readers.erase(it); | |
| 1098 | |
| 1099 ProcessPendingQueue(entry); | |
| 1100 } | |
| 1101 | |
| 1102 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) { | |
| 1103 DCHECK(entry->writer); | |
| 1104 DCHECK(entry->writer->mode() == Transaction::READ_WRITE); | |
| 1105 DCHECK(entry->readers.empty()); | |
| 1106 | |
| 1107 Transaction* trans = entry->writer; | |
| 1108 | |
| 1109 entry->writer = NULL; | |
| 1110 entry->readers.push_back(trans); | |
| 1111 | |
| 1112 ProcessPendingQueue(entry); | |
| 1113 } | |
| 1114 | |
| 1115 LoadState HttpCache::GetLoadStateForPendingTransaction( | |
| 1116 const Transaction* trans) { | |
| 1117 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key()); | |
| 1118 if (i == active_entries_.end()) { | |
| 1119 // If this is really a pending transaction, and it is not part of | |
| 1120 // active_entries_, we should be creating the backend or the entry. | |
| 1121 return LOAD_STATE_WAITING_FOR_CACHE; | |
| 1122 } | |
| 1123 | |
| 1124 Transaction* writer = i->second->writer; | |
| 1125 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE; | |
| 1126 } | |
| 1127 | |
| 1128 void HttpCache::RemovePendingTransaction(Transaction* trans) { | |
| 1129 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key()); | |
| 1130 bool found = false; | |
| 1131 if (i != active_entries_.end()) | |
| 1132 found = RemovePendingTransactionFromEntry(i->second, trans); | |
| 1133 | |
| 1134 if (found) | |
| 1135 return; | |
| 1136 | |
| 1137 if (building_backend_) { | |
| 1138 PendingOpsMap::const_iterator j = pending_ops_.find(std::string()); | |
| 1139 if (j != pending_ops_.end()) | |
| 1140 found = RemovePendingTransactionFromPendingOp(j->second, trans); | |
| 1141 | |
| 1142 if (found) | |
| 1143 return; | |
| 1144 } | |
| 1145 | |
| 1146 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key()); | |
| 1147 if (j != pending_ops_.end()) | |
| 1148 found = RemovePendingTransactionFromPendingOp(j->second, trans); | |
| 1149 | |
| 1150 if (found) | |
| 1151 return; | |
| 1152 | |
| 1153 ActiveEntriesSet::iterator k = doomed_entries_.begin(); | |
| 1154 for (; k != doomed_entries_.end() && !found; ++k) | |
| 1155 found = RemovePendingTransactionFromEntry(*k, trans); | |
| 1156 | |
| 1157 DCHECK(found) << "Pending transaction not found"; | |
| 1158 } | |
| 1159 | |
| 1160 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry, | |
| 1161 Transaction* trans) { | |
| 1162 TransactionList& pending_queue = entry->pending_queue; | |
| 1163 | |
| 1164 TransactionList::iterator j = | |
| 1165 find(pending_queue.begin(), pending_queue.end(), trans); | |
| 1166 if (j == pending_queue.end()) | |
| 1167 return false; | |
| 1168 | |
| 1169 pending_queue.erase(j); | |
| 1170 return true; | |
| 1171 } | |
| 1172 | |
| 1173 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op, | |
| 1174 Transaction* trans) { | |
| 1175 if (pending_op->writer->Matches(trans)) { | |
| 1176 pending_op->writer->ClearTransaction(); | |
| 1177 pending_op->writer->ClearEntry(); | |
| 1178 return true; | |
| 1179 } | |
| 1180 WorkItemList& pending_queue = pending_op->pending_queue; | |
| 1181 | |
| 1182 WorkItemList::iterator it = pending_queue.begin(); | |
| 1183 for (; it != pending_queue.end(); ++it) { | |
| 1184 if ((*it)->Matches(trans)) { | |
| 1185 delete *it; | |
| 1186 pending_queue.erase(it); | |
| 1187 return true; | |
| 1188 } | |
| 1189 } | |
| 1190 return false; | |
| 1191 } | |
| 1192 | |
| 1193 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) { | |
| 1194 if (session && | |
| 1195 !session->quic_stream_factory()->has_quic_server_info_factory()) { | |
| 1196 DCHECK(!quic_server_info_factory_); | |
| 1197 quic_server_info_factory_.reset(new QuicServerInfoFactoryAdaptor(this)); | |
| 1198 session->quic_stream_factory()->set_quic_server_info_factory( | |
| 1199 quic_server_info_factory_.get()); | |
| 1200 } | |
| 1201 } | |
| 1202 | |
| 1203 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) { | |
| 1204 // Multiple readers may finish with an entry at once, so we want to batch up | |
| 1205 // calls to OnProcessPendingQueue. This flag also tells us that we should | |
| 1206 // not delete the entry before OnProcessPendingQueue runs. | |
| 1207 if (entry->will_process_pending_queue) | |
| 1208 return; | |
| 1209 entry->will_process_pending_queue = true; | |
| 1210 | |
| 1211 base::MessageLoop::current()->PostTask( | |
| 1212 FROM_HERE, | |
| 1213 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry)); | |
| 1214 } | |
| 1215 | |
| 1216 void HttpCache::PerformAsyncValidation(const HttpRequestInfo& original_request, | |
| 1217 const BoundNetLog& net_log) { | |
| 1218 DCHECK(use_stale_while_revalidate_); | |
| 1219 std::string key = GenerateCacheKey(&original_request); | |
| 1220 AsyncValidation* async_validation = | |
| 1221 new AsyncValidation(original_request, this); | |
| 1222 typedef AsyncValidationMap::value_type AsyncValidationKeyValue; | |
| 1223 bool insert_ok = | |
| 1224 async_validations_.insert(AsyncValidationKeyValue(key, async_validation)) | |
| 1225 .second; | |
| 1226 if (!insert_ok) { | |
| 1227 DVLOG(1) << "Harmless race condition detected on URL " | |
| 1228 << original_request.url << "; discarding redundant revalidation."; | |
| 1229 delete async_validation; | |
| 1230 return; | |
| 1231 } | |
| 1232 HttpNetworkSession* network_session = GetSession(); | |
| 1233 NetworkDelegate* network_delegate = NULL; | |
| 1234 if (network_session) | |
| 1235 network_delegate = network_session->network_delegate(); | |
| 1236 scoped_ptr<HttpTransaction> transaction; | |
| 1237 CreateTransaction(IDLE, &transaction); | |
| 1238 scoped_ptr<Transaction> downcast_transaction( | |
| 1239 static_cast<Transaction*>(transaction.release())); | |
| 1240 async_validation->Start( | |
| 1241 net_log, downcast_transaction.Pass(), network_delegate); | |
| 1242 // |async_validation| may have been deleted here. | |
| 1243 } | |
| 1244 | |
| 1245 void HttpCache::DeleteAsyncValidation(const std::string& url) { | |
| 1246 AsyncValidationMap::iterator it = async_validations_.find(url); | |
| 1247 CHECK(it != async_validations_.end()); // security-critical invariant | |
| 1248 AsyncValidation* async_validation = it->second; | |
| 1249 async_validations_.erase(it); | |
| 1250 delete async_validation; | |
| 1251 } | |
| 1252 | |
| 1253 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) { | |
| 1254 entry->will_process_pending_queue = false; | |
| 1255 DCHECK(!entry->writer); | |
| 1256 | |
| 1257 // If no one is interested in this entry, then we can deactivate it. | |
| 1258 if (entry->pending_queue.empty()) { | |
| 1259 if (entry->readers.empty()) | |
| 1260 DestroyEntry(entry); | |
| 1261 return; | |
| 1262 } | |
| 1263 | |
| 1264 // Promote next transaction from the pending queue. | |
| 1265 Transaction* next = entry->pending_queue.front(); | |
| 1266 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty()) | |
| 1267 return; // Have to wait. | |
| 1268 | |
| 1269 entry->pending_queue.erase(entry->pending_queue.begin()); | |
| 1270 | |
| 1271 int rv = AddTransactionToEntry(entry, next); | |
| 1272 if (rv != ERR_IO_PENDING) { | |
| 1273 next->io_callback().Run(rv); | |
| 1274 } | |
| 1275 } | |
| 1276 | |
| 1277 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) { | |
| 1278 WorkItemOperation op = pending_op->writer->operation(); | |
| 1279 | |
| 1280 // Completing the creation of the backend is simpler than the other cases. | |
| 1281 if (op == WI_CREATE_BACKEND) | |
| 1282 return OnBackendCreated(result, pending_op); | |
| 1283 | |
| 1284 scoped_ptr<WorkItem> item(pending_op->writer); | |
| 1285 bool fail_requests = false; | |
| 1286 | |
| 1287 ActiveEntry* entry = NULL; | |
| 1288 std::string key; | |
| 1289 if (result == OK) { | |
| 1290 if (op == WI_DOOM_ENTRY) { | |
| 1291 // Anything after a Doom has to be restarted. | |
| 1292 fail_requests = true; | |
| 1293 } else if (item->IsValid()) { | |
| 1294 key = pending_op->disk_entry->GetKey(); | |
| 1295 entry = ActivateEntry(pending_op->disk_entry); | |
| 1296 } else { | |
| 1297 // The writer transaction is gone. | |
| 1298 if (op == WI_CREATE_ENTRY) | |
| 1299 pending_op->disk_entry->Doom(); | |
| 1300 pending_op->disk_entry->Close(); | |
| 1301 pending_op->disk_entry = NULL; | |
| 1302 fail_requests = true; | |
| 1303 } | |
| 1304 } | |
| 1305 | |
| 1306 // We are about to notify a bunch of transactions, and they may decide to | |
| 1307 // re-issue a request (or send a different one). If we don't delete | |
| 1308 // pending_op, the new request will be appended to the end of the list, and | |
| 1309 // we'll see it again from this point before it has a chance to complete (and | |
| 1310 // we'll be messing out the request order). The down side is that if for some | |
| 1311 // reason notifying request A ends up cancelling request B (for the same key), | |
| 1312 // we won't find request B anywhere (because it would be in a local variable | |
| 1313 // here) and that's bad. If there is a chance for that to happen, we'll have | |
| 1314 // to move the callback used to be a CancelableCallback. By the way, for this | |
| 1315 // to happen the action (to cancel B) has to be synchronous to the | |
| 1316 // notification for request A. | |
| 1317 WorkItemList pending_items; | |
| 1318 pending_items.swap(pending_op->pending_queue); | |
| 1319 DeletePendingOp(pending_op); | |
| 1320 | |
| 1321 item->NotifyTransaction(result, entry); | |
| 1322 | |
| 1323 while (!pending_items.empty()) { | |
| 1324 item.reset(pending_items.front()); | |
| 1325 pending_items.pop_front(); | |
| 1326 | |
| 1327 if (item->operation() == WI_DOOM_ENTRY) { | |
| 1328 // A queued doom request is always a race. | |
| 1329 fail_requests = true; | |
| 1330 } else if (result == OK) { | |
| 1331 entry = FindActiveEntry(key); | |
| 1332 if (!entry) | |
| 1333 fail_requests = true; | |
| 1334 } | |
| 1335 | |
| 1336 if (fail_requests) { | |
| 1337 item->NotifyTransaction(ERR_CACHE_RACE, NULL); | |
| 1338 continue; | |
| 1339 } | |
| 1340 | |
| 1341 if (item->operation() == WI_CREATE_ENTRY) { | |
| 1342 if (result == OK) { | |
| 1343 // A second Create request, but the first request succeeded. | |
| 1344 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL); | |
| 1345 } else { | |
| 1346 if (op != WI_CREATE_ENTRY) { | |
| 1347 // Failed Open followed by a Create. | |
| 1348 item->NotifyTransaction(ERR_CACHE_RACE, NULL); | |
| 1349 fail_requests = true; | |
| 1350 } else { | |
| 1351 item->NotifyTransaction(result, entry); | |
| 1352 } | |
| 1353 } | |
| 1354 } else { | |
| 1355 if (op == WI_CREATE_ENTRY && result != OK) { | |
| 1356 // Failed Create followed by an Open. | |
| 1357 item->NotifyTransaction(ERR_CACHE_RACE, NULL); | |
| 1358 fail_requests = true; | |
| 1359 } else { | |
| 1360 item->NotifyTransaction(result, entry); | |
| 1361 } | |
| 1362 } | |
| 1363 } | |
| 1364 } | |
| 1365 | |
| 1366 // static | |
| 1367 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache, | |
| 1368 PendingOp* pending_op, | |
| 1369 int rv) { | |
| 1370 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 1371 tracked_objects::ScopedTracker tracking_profile( | |
| 1372 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 1373 "422516 HttpCache::OnPendingOpComplete")); | |
| 1374 | |
| 1375 if (cache.get()) { | |
| 1376 cache->OnIOComplete(rv, pending_op); | |
| 1377 } else { | |
| 1378 // The callback was cancelled so we should delete the pending_op that | |
| 1379 // was used with this callback. | |
| 1380 delete pending_op; | |
| 1381 } | |
| 1382 } | |
| 1383 | |
| 1384 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) { | |
| 1385 scoped_ptr<WorkItem> item(pending_op->writer); | |
| 1386 WorkItemOperation op = item->operation(); | |
| 1387 DCHECK_EQ(WI_CREATE_BACKEND, op); | |
| 1388 | |
| 1389 // We don't need the callback anymore. | |
| 1390 pending_op->callback.Reset(); | |
| 1391 | |
| 1392 if (backend_factory_.get()) { | |
| 1393 // We may end up calling OnBackendCreated multiple times if we have pending | |
| 1394 // work items. The first call saves the backend and releases the factory, | |
| 1395 // and the last call clears building_backend_. | |
| 1396 backend_factory_.reset(); // Reclaim memory. | |
| 1397 if (result == OK) { | |
| 1398 disk_cache_ = pending_op->backend.Pass(); | |
| 1399 if (UseCertCache()) | |
| 1400 cert_cache_.reset(new DiskBasedCertCache(disk_cache_.get())); | |
| 1401 } | |
| 1402 } | |
| 1403 | |
| 1404 if (!pending_op->pending_queue.empty()) { | |
| 1405 WorkItem* pending_item = pending_op->pending_queue.front(); | |
| 1406 pending_op->pending_queue.pop_front(); | |
| 1407 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation()); | |
| 1408 | |
| 1409 // We want to process a single callback at a time, because the cache may | |
| 1410 // go away from the callback. | |
| 1411 pending_op->writer = pending_item; | |
| 1412 | |
| 1413 base::MessageLoop::current()->PostTask( | |
| 1414 FROM_HERE, | |
| 1415 base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(), | |
| 1416 result, pending_op)); | |
| 1417 } else { | |
| 1418 building_backend_ = false; | |
| 1419 DeletePendingOp(pending_op); | |
| 1420 } | |
| 1421 | |
| 1422 // The cache may be gone when we return from the callback. | |
| 1423 if (!item->DoCallback(result, disk_cache_.get())) | |
| 1424 item->NotifyTransaction(result, NULL); | |
| 1425 } | |
| 1426 | |
| 1427 } // namespace net | |
| OLD | NEW |