http_cache.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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/callback.h" 16#include "base/format_macros.h" 17#include "base/message_loop.h" 18#include "base/pickle.h" 19#include "base/ref_counted.h" 20#include "base/stl_util-inl.h" 21#include "base/string_number_conversions.h" 22#include "base/string_util.h" 23#include "base/stringprintf.h" 24#include "net/base/io_buffer.h" 25#include "net/base/load_flags.h" 26#include "net/base/net_errors.h" 27#include "net/disk_cache/disk_cache.h" 28#include "net/http/disk_cache_based_ssl_host_info.h" 29#include "net/http/http_cache_transaction.h" 30#include "net/http/http_network_layer.h" 31#include "net/http/http_network_session.h" 32#include "net/http/http_request_info.h" 33#include "net/http/http_response_headers.h" 34#include "net/http/http_response_info.h" 35#include "net/http/http_util.h" 36#include "net/socket/ssl_host_info.h" 37#include "net/spdy/spdy_session_pool.h" 38 39namespace net { 40 41HttpCache::DefaultBackend::DefaultBackend(CacheType type, 42 const FilePath& path, 43 int max_bytes, 44 base::MessageLoopProxy* thread) 45 : type_(type), 46 path_(path), 47 max_bytes_(max_bytes), 48 thread_(thread) { 49} 50 51HttpCache::DefaultBackend::~DefaultBackend() {} 52 53// static 54HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) { 55 return new DefaultBackend(MEMORY_CACHE, FilePath(), max_bytes, NULL); 56} 57 58int HttpCache::DefaultBackend::CreateBackend(disk_cache::Backend** backend, 59 CompletionCallback* callback) { 60 DCHECK_GE(max_bytes_, 0); 61 return disk_cache::CreateCacheBackend(type_, path_, max_bytes_, true, 62 thread_, backend, callback); 63} 64 65//----------------------------------------------------------------------------- 66 67HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* e) 68 : disk_entry(e), 69 writer(NULL), 70 will_process_pending_queue(false), 71 doomed(false) { 72} 73 74HttpCache::ActiveEntry::~ActiveEntry() { 75 if (disk_entry) { 76 disk_entry->Close(); 77 disk_entry = NULL; 78 } 79} 80 81//----------------------------------------------------------------------------- 82 83// This structure keeps track of work items that are attempting to create or 84// open cache entries or the backend itself. 85struct HttpCache::PendingOp { 86 PendingOp() : disk_entry(NULL), backend(NULL), writer(NULL), callback(NULL) {} 87 ~PendingOp() {} 88 89 disk_cache::Entry* disk_entry; 90 disk_cache::Backend* backend; 91 WorkItem* writer; 92 CompletionCallback* callback; // BackendCallback. 93 WorkItemList pending_queue; 94}; 95 96//----------------------------------------------------------------------------- 97 98// The type of operation represented by a work item. 99enum WorkItemOperation { 100 WI_CREATE_BACKEND, 101 WI_OPEN_ENTRY, 102 WI_CREATE_ENTRY, 103 WI_DOOM_ENTRY 104}; 105 106// A work item encapsulates a single request to the backend with all the 107// information needed to complete that request. 108class HttpCache::WorkItem { 109 public: 110 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry) 111 : operation_(operation), trans_(trans), entry_(entry), callback_(NULL), 112 backend_(NULL) {} 113 WorkItem(WorkItemOperation operation, Transaction* trans, 114 CompletionCallback* cb, disk_cache::Backend** backend) 115 : operation_(operation), trans_(trans), entry_(NULL), callback_(cb), 116 backend_(backend) {} 117 ~WorkItem() {} 118 119 // Calls back the transaction with the result of the operation. 120 void NotifyTransaction(int result, ActiveEntry* entry) { 121 DCHECK(!entry || entry->disk_entry); 122 if (entry_) 123 *entry_ = entry; 124 if (trans_) 125 trans_->io_callback()->Run(result); 126 } 127 128 // Notifies the caller about the operation completion. Returns true if the 129 // callback was invoked. 130 bool DoCallback(int result, disk_cache::Backend* backend) { 131 if (backend_) 132 *backend_ = backend; 133 if (callback_) { 134 callback_->Run(result); 135 return true; 136 } 137 return false; 138 } 139 140 WorkItemOperation operation() { return operation_; } 141 void ClearTransaction() { trans_ = NULL; } 142 void ClearEntry() { entry_ = NULL; } 143 void ClearCallback() { callback_ = NULL; } 144 bool Matches(Transaction* trans) const { return trans == trans_; } 145 bool IsValid() const { return trans_ || entry_ || callback_; } 146 147 private: 148 WorkItemOperation operation_; 149 Transaction* trans_; 150 ActiveEntry** entry_; 151 CompletionCallback* callback_; // User callback. 152 disk_cache::Backend** backend_; 153}; 154 155//----------------------------------------------------------------------------- 156 157// This class is a specialized type of CompletionCallback that allows us to 158// pass multiple arguments to the completion routine. 159class HttpCache::BackendCallback : public CallbackRunner<Tuple1<int> > { 160 public: 161 BackendCallback(HttpCache* cache, PendingOp* pending_op) 162 : cache_(cache), pending_op_(pending_op) {} 163 ~BackendCallback() {} 164 165 virtual void RunWithParams(const Tuple1<int>& params) { 166 if (cache_) { 167 cache_->OnIOComplete(params.a, pending_op_); 168 } else { 169 // The callback was cancelled so we should delete the pending_op that 170 // was used with this callback. 171 delete pending_op_; 172 } 173 delete this; 174 } 175 176 void Cancel() { 177 cache_ = NULL; 178 } 179 180 private: 181 HttpCache* cache_; 182 PendingOp* pending_op_; 183 DISALLOW_COPY_AND_ASSIGN(BackendCallback); 184}; 185 186//----------------------------------------------------------------------------- 187 188// This class encapsulates a transaction whose only purpose is to write metadata 189// to a given entry. 190class HttpCache::MetadataWriter { 191 public: 192 explicit MetadataWriter(HttpCache::Transaction* trans) 193 : transaction_(trans), 194 ALLOW_THIS_IN_INITIALIZER_LIST( 195 callback_(this, &MetadataWriter::OnIOComplete)) {} 196 ~MetadataWriter() {} 197 198 // Implementes the bulk of HttpCache::WriteMetadata. 199 void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf, 200 int buf_len); 201 202 private: 203 void VerifyResponse(int result); 204 void SelfDestroy(); 205 void OnIOComplete(int result); 206 207 scoped_ptr<HttpCache::Transaction> transaction_; 208 bool verified_; 209 scoped_refptr<IOBuffer> buf_; 210 int buf_len_; 211 base::Time expected_response_time_; 212 CompletionCallbackImpl<MetadataWriter> callback_; 213 HttpRequestInfo request_info_; 214 DISALLOW_COPY_AND_ASSIGN(MetadataWriter); 215}; 216 217void HttpCache::MetadataWriter::Write(const GURL& url, 218 base::Time expected_response_time, 219 IOBuffer* buf, int buf_len) { 220 DCHECK_GT(buf_len, 0); 221 DCHECK(buf); 222 DCHECK(buf->data()); 223 request_info_.url = url; 224 request_info_.method = "GET"; 225 request_info_.load_flags = LOAD_ONLY_FROM_CACHE; 226 227 expected_response_time_ = expected_response_time; 228 buf_ = buf; 229 buf_len_ = buf_len; 230 verified_ = false; 231 232 int rv = transaction_->Start(&request_info_, &callback_, BoundNetLog()); 233 if (rv != ERR_IO_PENDING) 234 VerifyResponse(rv); 235} 236 237void HttpCache::MetadataWriter::VerifyResponse(int result) { 238 verified_ = true; 239 if (result != OK) 240 return SelfDestroy(); 241 242 const HttpResponseInfo* response_info = transaction_->GetResponseInfo(); 243 DCHECK(response_info->was_cached); 244 if (response_info->response_time != expected_response_time_) 245 return SelfDestroy(); 246 247 result = transaction_->WriteMetadata(buf_, buf_len_, &callback_); 248 if (result != ERR_IO_PENDING) 249 SelfDestroy(); 250} 251 252void HttpCache::MetadataWriter::SelfDestroy() { 253 delete this; 254} 255 256void HttpCache::MetadataWriter::OnIOComplete(int result) { 257 if (!verified_) 258 return VerifyResponse(result); 259 SelfDestroy(); 260} 261 262//----------------------------------------------------------------------------- 263 264class HttpCache::SSLHostInfoFactoryAdaptor : public SSLHostInfoFactory { 265 public: 266 SSLHostInfoFactoryAdaptor(HttpCache* http_cache) 267 : http_cache_(http_cache) { 268 } 269 270 SSLHostInfo* GetForHost(const std::string& hostname, 271 const SSLConfig& ssl_config) { 272 return new DiskCacheBasedSSLHostInfo(hostname, ssl_config, http_cache_); 273 } 274 275 private: 276 HttpCache* const http_cache_; 277}; 278 279//----------------------------------------------------------------------------- 280 281HttpCache::HttpCache(HostResolver* host_resolver, 282 DnsRRResolver* dnsrr_resolver, 283 ProxyService* proxy_service, 284 SSLConfigService* ssl_config_service, 285 HttpAuthHandlerFactory* http_auth_handler_factory, 286 HttpNetworkDelegate* network_delegate, 287 NetLog* net_log, 288 BackendFactory* backend_factory) 289 : backend_factory_(backend_factory), 290 building_backend_(false), 291 mode_(NORMAL), 292 ssl_host_info_factory_(new SSLHostInfoFactoryAdaptor( 293 ALLOW_THIS_IN_INITIALIZER_LIST(this))), 294 network_layer_(HttpNetworkLayer::CreateFactory(host_resolver, 295 dnsrr_resolver, ssl_host_info_factory_.get(), 296 proxy_service, ssl_config_service, 297 http_auth_handler_factory, network_delegate, net_log)), 298 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), 299 enable_range_support_(true) { 300} 301 302HttpCache::HttpCache(HttpNetworkSession* session, 303 BackendFactory* backend_factory) 304 : backend_factory_(backend_factory), 305 building_backend_(false), 306 mode_(NORMAL), 307 network_layer_(HttpNetworkLayer::CreateFactory(session)), 308 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), 309 enable_range_support_(true) { 310} 311 312HttpCache::HttpCache(HttpTransactionFactory* network_layer, 313 BackendFactory* backend_factory) 314 : backend_factory_(backend_factory), 315 building_backend_(false), 316 mode_(NORMAL), 317 network_layer_(network_layer), 318 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), 319 enable_range_support_(true) { 320} 321 322HttpCache::~HttpCache() { 323 // If we have any active entries remaining, then we need to deactivate them. 324 // We may have some pending calls to OnProcessPendingQueue, but since those 325 // won't run (due to our destruction), we can simply ignore the corresponding 326 // will_process_pending_queue flag. 327 while (!active_entries_.empty()) { 328 ActiveEntry* entry = active_entries_.begin()->second; 329 entry->will_process_pending_queue = false; 330 entry->pending_queue.clear(); 331 entry->readers.clear(); 332 entry->writer = NULL; 333 DeactivateEntry(entry); 334 } 335 336 STLDeleteElements(&doomed_entries_); 337 338 PendingOpsMap::iterator pending_it = pending_ops_.begin(); 339 for (; pending_it != pending_ops_.end(); ++pending_it) { 340 // We are not notifying the transactions about the cache going away, even 341 // though they are waiting for a callback that will never fire. 342 PendingOp* pending_op = pending_it->second; 343 delete pending_op->writer; 344 bool delete_pending_op = true; 345 if (building_backend_) { 346 // If we don't have a backend, when its construction finishes it will 347 // deliver the callbacks. 348 BackendCallback* callback = 349 static_cast<BackendCallback*>(pending_op->callback); 350 if (callback) { 351 // The callback will delete the pending operation. 352 callback->Cancel(); 353 delete_pending_op = false; 354 } 355 } else { 356 delete pending_op->callback; 357 } 358 359 STLDeleteElements(&pending_op->pending_queue); 360 if (delete_pending_op) 361 delete pending_op; 362 } 363} 364 365int HttpCache::GetBackend(disk_cache::Backend** backend, 366 CompletionCallback* callback) { 367 DCHECK(callback != NULL); 368 369 if (disk_cache_.get()) { 370 *backend = disk_cache_.get(); 371 return OK; 372 } 373 374 return CreateBackend(backend, callback); 375} 376 377disk_cache::Backend* HttpCache::GetCurrentBackend() { 378 return disk_cache_.get(); 379} 380 381int HttpCache::CreateTransaction(scoped_ptr<HttpTransaction>* trans) { 382 // Do lazy initialization of disk cache if needed. 383 if (!disk_cache_.get()) 384 CreateBackend(NULL, NULL); // We don't care about the result. 385 386 trans->reset(new HttpCache::Transaction(this, enable_range_support_)); 387 return OK; 388} 389 390HttpCache* HttpCache::GetCache() { 391 return this; 392} 393 394HttpNetworkSession* HttpCache::GetSession() { 395 net::HttpNetworkLayer* network = 396 static_cast<net::HttpNetworkLayer*>(network_layer_.get()); 397 return network->GetSession(); 398} 399 400void HttpCache::Suspend(bool suspend) { 401 network_layer_->Suspend(suspend); 402} 403 404// static 405bool HttpCache::ParseResponseInfo(const char* data, int len, 406 HttpResponseInfo* response_info, 407 bool* response_truncated) { 408 Pickle pickle(data, len); 409 return response_info->InitFromPickle(pickle, response_truncated); 410} 411 412void HttpCache::WriteMetadata(const GURL& url, 413 base::Time expected_response_time, IOBuffer* buf, 414 int buf_len) { 415 if (!buf_len) 416 return; 417 418 // Do lazy initialization of disk cache if needed. 419 if (!disk_cache_.get()) 420 CreateBackend(NULL, NULL); // We don't care about the result. 421 422 HttpCache::Transaction* trans = 423 new HttpCache::Transaction(this, enable_range_support_); 424 MetadataWriter* writer = new MetadataWriter(trans); 425 426 // The writer will self destruct when done. 427 writer->Write(url, expected_response_time, buf, buf_len); 428} 429 430void HttpCache::CloseCurrentConnections() { 431 net::HttpNetworkLayer* network = 432 static_cast<net::HttpNetworkLayer*>(network_layer_.get()); 433 HttpNetworkSession* session = network->GetSession(); 434 if (session) { 435 session->FlushSocketPools(); 436 if (session->spdy_session_pool()) 437 session->spdy_session_pool()->CloseCurrentSessions(); 438 } 439} 440 441//----------------------------------------------------------------------------- 442 443int HttpCache::CreateBackend(disk_cache::Backend** backend, 444 CompletionCallback* callback) { 445 if (!backend_factory_.get()) 446 return ERR_FAILED; 447 448 building_backend_ = true; 449 450 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback, 451 backend)); 452 453 // This is the only operation that we can do that is not related to any given 454 // entry, so we use an empty key for it. 455 PendingOp* pending_op = GetPendingOp(""); 456 if (pending_op->writer) { 457 if (callback) 458 pending_op->pending_queue.push_back(item.release()); 459 return ERR_IO_PENDING; 460 } 461 462 DCHECK(pending_op->pending_queue.empty()); 463 464 pending_op->writer = item.release(); 465 BackendCallback* my_callback = new BackendCallback(this, pending_op); 466 pending_op->callback = my_callback; 467 468 int rv = backend_factory_->CreateBackend(&pending_op->backend, my_callback); 469 if (rv != ERR_IO_PENDING) { 470 pending_op->writer->ClearCallback(); 471 my_callback->Run(rv); 472 } 473 474 return rv; 475} 476 477int HttpCache::GetBackendForTransaction(Transaction* trans) { 478 if (disk_cache_.get()) 479 return OK; 480 481 if (!building_backend_) 482 return ERR_FAILED; 483 484 WorkItem* item = new WorkItem(WI_CREATE_BACKEND, trans, NULL, NULL); 485 PendingOp* pending_op = GetPendingOp(""); 486 DCHECK(pending_op->writer); 487 pending_op->pending_queue.push_back(item); 488 return ERR_IO_PENDING; 489} 490 491// Generate a key that can be used inside the cache. 492std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) { 493 // Strip out the reference, username, and password sections of the URL. 494 std::string url = HttpUtil::SpecForRequest(request->url); 495 496 DCHECK(mode_ != DISABLE); 497 if (mode_ == NORMAL) { 498 // No valid URL can begin with numerals, so we should not have to worry 499 // about collisions with normal URLs. 500 if (request->upload_data && request->upload_data->identifier()) { 501 url.insert(0, base::StringPrintf("%" PRId64 "/", 502 request->upload_data->identifier())); 503 } 504 return url; 505 } 506 507 // In playback and record mode, we cache everything. 508 509 // Lazily initialize. 510 if (playback_cache_map_ == NULL) 511 playback_cache_map_.reset(new PlaybackCacheMap()); 512 513 // Each time we request an item from the cache, we tag it with a 514 // generation number. During playback, multiple fetches for the same 515 // item will use the same generation number and pull the proper 516 // instance of an URL from the cache. 517 int generation = 0; 518 DCHECK(playback_cache_map_ != NULL); 519 if (playback_cache_map_->find(url) != playback_cache_map_->end()) 520 generation = (*playback_cache_map_)[url]; 521 (*playback_cache_map_)[url] = generation + 1; 522 523 // The key into the cache is GENERATION # + METHOD + URL. 524 std::string result = base::IntToString(generation); 525 result.append(request->method); 526 result.append(url); 527 return result; 528} 529 530int HttpCache::DoomEntry(const std::string& key, Transaction* trans) { 531 // Need to abandon the ActiveEntry, but any transaction attached to the entry 532 // should not be impacted. Dooming an entry only means that it will no 533 // longer be returned by FindActiveEntry (and it will also be destroyed once 534 // all consumers are finished with the entry). 535 ActiveEntriesMap::iterator it = active_entries_.find(key); 536 if (it == active_entries_.end()) { 537 return AsyncDoomEntry(key, trans); 538 } 539 540 ActiveEntry* entry = it->second; 541 active_entries_.erase(it); 542 543 // We keep track of doomed entries so that we can ensure that they are 544 // cleaned up properly when the cache is destroyed. 545 doomed_entries_.insert(entry); 546 547 entry->disk_entry->Doom(); 548 entry->doomed = true; 549 550 DCHECK(entry->writer || !entry->readers.empty()); 551 return OK; 552} 553 554int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) { 555 DCHECK(trans); 556 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL); 557 PendingOp* pending_op = GetPendingOp(key); 558 if (pending_op->writer) { 559 pending_op->pending_queue.push_back(item); 560 return ERR_IO_PENDING; 561 } 562 563 DCHECK(pending_op->pending_queue.empty()); 564 565 pending_op->writer = item; 566 BackendCallback* my_callback = new BackendCallback(this, pending_op); 567 pending_op->callback = my_callback; 568 569 int rv = disk_cache_->DoomEntry(key, my_callback); 570 if (rv != ERR_IO_PENDING) { 571 item->ClearTransaction(); 572 my_callback->Run(rv); 573 } 574 575 return rv; 576} 577 578void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) { 579 DCHECK(entry->doomed); 580 DCHECK(!entry->writer); 581 DCHECK(entry->readers.empty()); 582 DCHECK(entry->pending_queue.empty()); 583 584 ActiveEntriesSet::iterator it = doomed_entries_.find(entry); 585 DCHECK(it != doomed_entries_.end()); 586 doomed_entries_.erase(it); 587 588 delete entry; 589} 590 591HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) { 592 ActiveEntriesMap::const_iterator it = active_entries_.find(key); 593 return it != active_entries_.end() ? it->second : NULL; 594} 595 596HttpCache::ActiveEntry* HttpCache::ActivateEntry( 597 disk_cache::Entry* disk_entry) { 598 DCHECK(!FindActiveEntry(disk_entry->GetKey())); 599 ActiveEntry* entry = new ActiveEntry(disk_entry); 600 active_entries_[disk_entry->GetKey()] = entry; 601 return entry; 602} 603 604void HttpCache::DeactivateEntry(ActiveEntry* entry) { 605 DCHECK(!entry->will_process_pending_queue); 606 DCHECK(!entry->doomed); 607 DCHECK(!entry->writer); 608 DCHECK(entry->disk_entry); 609 DCHECK(entry->readers.empty()); 610 DCHECK(entry->pending_queue.empty()); 611 612 std::string key = entry->disk_entry->GetKey(); 613 if (key.empty()) 614 return SlowDeactivateEntry(entry); 615 616 ActiveEntriesMap::iterator it = active_entries_.find(key); 617 DCHECK(it != active_entries_.end()); 618 DCHECK(it->second == entry); 619 620 active_entries_.erase(it); 621 delete entry; 622} 623 624// We don't know this entry's key so we have to find it without it. 625void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) { 626 for (ActiveEntriesMap::iterator it = active_entries_.begin(); 627 it != active_entries_.end(); ++it) { 628 if (it->second == entry) { 629 active_entries_.erase(it); 630 delete entry; 631 break; 632 } 633 } 634} 635 636HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) { 637 DCHECK(!FindActiveEntry(key)); 638 639 PendingOpsMap::const_iterator it = pending_ops_.find(key); 640 if (it != pending_ops_.end()) 641 return it->second; 642 643 PendingOp* operation = new PendingOp(); 644 pending_ops_[key] = operation; 645 return operation; 646} 647 648void HttpCache::DeletePendingOp(PendingOp* pending_op) { 649 std::string key; 650 if (pending_op->disk_entry) 651 key = pending_op->disk_entry->GetKey(); 652 653 if (!key.empty()) { 654 PendingOpsMap::iterator it = pending_ops_.find(key); 655 DCHECK(it != pending_ops_.end()); 656 pending_ops_.erase(it); 657 } else { 658 for (PendingOpsMap::iterator it = pending_ops_.begin(); 659 it != pending_ops_.end(); ++it) { 660 if (it->second == pending_op) { 661 pending_ops_.erase(it); 662 break; 663 } 664 } 665 } 666 DCHECK(pending_op->pending_queue.empty()); 667 668 delete pending_op; 669} 670 671int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry, 672 Transaction* trans) { 673 ActiveEntry* active_entry = FindActiveEntry(key); 674 if (active_entry) { 675 *entry = active_entry; 676 return OK; 677 } 678 679 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry); 680 PendingOp* pending_op = GetPendingOp(key); 681 if (pending_op->writer) { 682 pending_op->pending_queue.push_back(item); 683 return ERR_IO_PENDING; 684 } 685 686 DCHECK(pending_op->pending_queue.empty()); 687 688 pending_op->writer = item; 689 BackendCallback* my_callback = new BackendCallback(this, pending_op); 690 pending_op->callback = my_callback; 691 692 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry), my_callback); 693 if (rv != ERR_IO_PENDING) { 694 item->ClearTransaction(); 695 my_callback->Run(rv); 696 } 697 698 return rv; 699} 700 701int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry, 702 Transaction* trans) { 703 DCHECK(!FindActiveEntry(key)); 704 705 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry); 706 PendingOp* pending_op = GetPendingOp(key); 707 if (pending_op->writer) { 708 pending_op->pending_queue.push_back(item); 709 return ERR_IO_PENDING; 710 } 711 712 DCHECK(pending_op->pending_queue.empty()); 713 714 pending_op->writer = item; 715 BackendCallback* my_callback = new BackendCallback(this, pending_op); 716 pending_op->callback = my_callback; 717 718 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry), 719 my_callback); 720 if (rv != ERR_IO_PENDING) { 721 item->ClearTransaction(); 722 my_callback->Run(rv); 723 } 724 725 return rv; 726} 727 728void HttpCache::DestroyEntry(ActiveEntry* entry) { 729 if (entry->doomed) { 730 FinalizeDoomedEntry(entry); 731 } else { 732 DeactivateEntry(entry); 733 } 734} 735 736int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) { 737 DCHECK(entry); 738 DCHECK(entry->disk_entry); 739 740 // We implement a basic reader/writer lock for the disk cache entry. If 741 // there is already a writer, then everyone has to wait for the writer to 742 // finish before they can access the cache entry. There can be multiple 743 // readers. 744 // 745 // NOTE: If the transaction can only write, then the entry should not be in 746 // use (since any existing entry should have already been doomed). 747 748 if (entry->writer || entry->will_process_pending_queue) { 749 entry->pending_queue.push_back(trans); 750 return ERR_IO_PENDING; 751 } 752 753 if (trans->mode() & Transaction::WRITE) { 754 // transaction needs exclusive access to the entry 755 if (entry->readers.empty()) { 756 entry->writer = trans; 757 } else { 758 entry->pending_queue.push_back(trans); 759 return ERR_IO_PENDING; 760 } 761 } else { 762 // transaction needs read access to the entry 763 entry->readers.push_back(trans); 764 } 765 766 // We do this before calling EntryAvailable to force any further calls to 767 // AddTransactionToEntry to add their transaction to the pending queue, which 768 // ensures FIFO ordering. 769 if (!entry->writer && !entry->pending_queue.empty()) 770 ProcessPendingQueue(entry); 771 772 return OK; 773} 774 775void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans, 776 bool cancel) { 777 // If we already posted a task to move on to the next transaction and this was 778 // the writer, there is nothing to cancel. 779 if (entry->will_process_pending_queue && entry->readers.empty()) 780 return; 781 782 if (entry->writer) { 783 DCHECK(trans == entry->writer); 784 785 // Assume there was a failure. 786 bool success = false; 787 if (cancel) { 788 DCHECK(entry->disk_entry); 789 // This is a successful operation in the sense that we want to keep the 790 // entry. 791 success = trans->AddTruncatedFlag(); 792 } 793 DoneWritingToEntry(entry, success); 794 } else { 795 DoneReadingFromEntry(entry, trans); 796 } 797} 798 799void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) { 800 DCHECK(entry->readers.empty()); 801 802 entry->writer = NULL; 803 804 if (success) { 805 ProcessPendingQueue(entry); 806 } else { 807 DCHECK(!entry->will_process_pending_queue); 808 809 // We failed to create this entry. 810 TransactionList pending_queue; 811 pending_queue.swap(entry->pending_queue); 812 813 entry->disk_entry->Doom(); 814 DestroyEntry(entry); 815 816 // We need to do something about these pending entries, which now need to 817 // be added to a new entry. 818 while (!pending_queue.empty()) { 819 // ERR_CACHE_RACE causes the transaction to restart the whole process. 820 pending_queue.front()->io_callback()->Run(ERR_CACHE_RACE); 821 pending_queue.pop_front(); 822 } 823 } 824} 825 826void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) { 827 DCHECK(!entry->writer); 828 829 TransactionList::iterator it = 830 std::find(entry->readers.begin(), entry->readers.end(), trans); 831 DCHECK(it != entry->readers.end()); 832 833 entry->readers.erase(it); 834 835 ProcessPendingQueue(entry); 836} 837 838void HttpCache::ConvertWriterToReader(ActiveEntry* entry) { 839 DCHECK(entry->writer); 840 DCHECK(entry->writer->mode() == Transaction::READ_WRITE); 841 DCHECK(entry->readers.empty()); 842 843 Transaction* trans = entry->writer; 844 845 entry->writer = NULL; 846 entry->readers.push_back(trans); 847 848 ProcessPendingQueue(entry); 849} 850 851LoadState HttpCache::GetLoadStateForPendingTransaction( 852 const Transaction* trans) { 853 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key()); 854 if (i == active_entries_.end()) { 855 // If this is really a pending transaction, and it is not part of 856 // active_entries_, we should be creating the backend or the entry. 857 return LOAD_STATE_WAITING_FOR_CACHE; 858 } 859 860 Transaction* writer = i->second->writer; 861 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE; 862} 863 864void HttpCache::RemovePendingTransaction(Transaction* trans) { 865 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key()); 866 bool found = false; 867 if (i != active_entries_.end()) 868 found = RemovePendingTransactionFromEntry(i->second, trans); 869 870 if (found) 871 return; 872 873 if (building_backend_) { 874 PendingOpsMap::const_iterator j = pending_ops_.find(""); 875 if (j != pending_ops_.end()) 876 found = RemovePendingTransactionFromPendingOp(j->second, trans); 877 878 if (found) 879 return; 880 } 881 882 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key()); 883 if (j != pending_ops_.end()) 884 found = RemovePendingTransactionFromPendingOp(j->second, trans); 885 886 if (found) 887 return; 888 889 ActiveEntriesSet::iterator k = doomed_entries_.begin(); 890 for (; k != doomed_entries_.end() && !found; ++k) 891 found = RemovePendingTransactionFromEntry(*k, trans); 892 893 DCHECK(found) << "Pending transaction not found"; 894} 895 896bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry, 897 Transaction* trans) { 898 TransactionList& pending_queue = entry->pending_queue; 899 900 TransactionList::iterator j = 901 find(pending_queue.begin(), pending_queue.end(), trans); 902 if (j == pending_queue.end()) 903 return false; 904 905 pending_queue.erase(j); 906 return true; 907} 908 909bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op, 910 Transaction* trans) { 911 if (pending_op->writer->Matches(trans)) { 912 pending_op->writer->ClearTransaction(); 913 pending_op->writer->ClearEntry(); 914 return true; 915 } 916 WorkItemList& pending_queue = pending_op->pending_queue; 917 918 WorkItemList::iterator it = pending_queue.begin(); 919 for (; it != pending_queue.end(); ++it) { 920 if ((*it)->Matches(trans)) { 921 delete *it; 922 pending_queue.erase(it); 923 return true; 924 } 925 } 926 return false; 927} 928 929void HttpCache::ProcessPendingQueue(ActiveEntry* entry) { 930 // Multiple readers may finish with an entry at once, so we want to batch up 931 // calls to OnProcessPendingQueue. This flag also tells us that we should 932 // not delete the entry before OnProcessPendingQueue runs. 933 if (entry->will_process_pending_queue) 934 return; 935 entry->will_process_pending_queue = true; 936 937 MessageLoop::current()->PostTask( 938 FROM_HERE, 939 task_factory_.NewRunnableMethod(&HttpCache::OnProcessPendingQueue, 940 entry)); 941} 942 943void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) { 944 entry->will_process_pending_queue = false; 945 DCHECK(!entry->writer); 946 947 // If no one is interested in this entry, then we can de-activate it. 948 if (entry->pending_queue.empty()) { 949 if (entry->readers.empty()) 950 DestroyEntry(entry); 951 return; 952 } 953 954 // Promote next transaction from the pending queue. 955 Transaction* next = entry->pending_queue.front(); 956 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty()) 957 return; // Have to wait. 958 959 entry->pending_queue.erase(entry->pending_queue.begin()); 960 961 int rv = AddTransactionToEntry(entry, next); 962 if (rv != ERR_IO_PENDING) { 963 next->io_callback()->Run(rv); 964 } 965} 966 967void HttpCache::OnIOComplete(int result, PendingOp* pending_op) { 968 WorkItemOperation op = pending_op->writer->operation(); 969 970 // Completing the creation of the backend is simpler than the other cases. 971 if (op == WI_CREATE_BACKEND) 972 return OnBackendCreated(result, pending_op); 973 974 scoped_ptr<WorkItem> item(pending_op->writer); 975 bool fail_requests = false; 976 977 ActiveEntry* entry = NULL; 978 std::string key; 979 if (result == OK) { 980 if (op == WI_DOOM_ENTRY) { 981 // Anything after a Doom has to be restarted. 982 fail_requests = true; 983 } else if (item->IsValid()) { 984 key = pending_op->disk_entry->GetKey(); 985 entry = ActivateEntry(pending_op->disk_entry); 986 } else { 987 // The writer transaction is gone. 988 if (op == WI_CREATE_ENTRY) 989 pending_op->disk_entry->Doom(); 990 pending_op->disk_entry->Close(); 991 pending_op->disk_entry = NULL; 992 fail_requests = true; 993 } 994 } 995 996 // We are about to notify a bunch of transactions, and they may decide to 997 // re-issue a request (or send a different one). If we don't delete 998 // pending_op, the new request will be appended to the end of the list, and 999 // we'll see it again from this point before it has a chance to complete (and 1000 // we'll be messing out the request order). The down side is that if for some 1001 // reason notifying request A ends up cancelling request B (for the same key), 1002 // we won't find request B anywhere (because it would be in a local variable 1003 // here) and that's bad. If there is a chance for that to happen, we'll have 1004 // to move the callback used to be a CancelableCallback. By the way, for this 1005 // to happen the action (to cancel B) has to be synchronous to the 1006 // notification for request A. 1007 WorkItemList pending_items; 1008 pending_items.swap(pending_op->pending_queue); 1009 DeletePendingOp(pending_op); 1010 1011 item->NotifyTransaction(result, entry); 1012 1013 while (!pending_items.empty()) { 1014 item.reset(pending_items.front()); 1015 pending_items.pop_front(); 1016 1017 if (item->operation() == WI_DOOM_ENTRY) { 1018 // A queued doom request is always a race. 1019 fail_requests = true; 1020 } else if (result == OK) { 1021 entry = FindActiveEntry(key); 1022 if (!entry) 1023 fail_requests = true; 1024 } 1025 1026 if (fail_requests) { 1027 item->NotifyTransaction(ERR_CACHE_RACE, NULL); 1028 continue; 1029 } 1030 1031 if (item->operation() == WI_CREATE_ENTRY) { 1032 if (result == OK) { 1033 // A second Create request, but the first request succeded. 1034 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL); 1035 } else { 1036 if (op != WI_CREATE_ENTRY) { 1037 // Failed Open followed by a Create. 1038 item->NotifyTransaction(ERR_CACHE_RACE, NULL); 1039 fail_requests = true; 1040 } else { 1041 item->NotifyTransaction(result, entry); 1042 } 1043 } 1044 } else { 1045 if (op == WI_CREATE_ENTRY && result != OK) { 1046 // Failed Create followed by an Open. 1047 item->NotifyTransaction(ERR_CACHE_RACE, NULL); 1048 fail_requests = true; 1049 } else { 1050 item->NotifyTransaction(result, entry); 1051 } 1052 } 1053 } 1054} 1055 1056void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) { 1057 scoped_ptr<WorkItem> item(pending_op->writer); 1058 WorkItemOperation op = item->operation(); 1059 DCHECK_EQ(WI_CREATE_BACKEND, op); 1060 1061 // We don't need the callback anymore. 1062 pending_op->callback = NULL; 1063 disk_cache::Backend* backend = pending_op->backend; 1064 1065 if (backend_factory_.get()) { 1066 // We may end up calling OnBackendCreated multiple times if we have pending 1067 // work items. The first call saves the backend and releases the factory, 1068 // and the last call clears building_backend_. 1069 backend_factory_.reset(); // Reclaim memory. 1070 if (result == OK) 1071 disk_cache_.reset(backend); 1072 } 1073 1074 if (!pending_op->pending_queue.empty()) { 1075 WorkItem* pending_item = pending_op->pending_queue.front(); 1076 pending_op->pending_queue.pop_front(); 1077 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation()); 1078 1079 // We want to process a single callback at a time, because the cache may 1080 // go away from the callback. 1081 pending_op->writer = pending_item; 1082 1083 MessageLoop::current()->PostTask( 1084 FROM_HERE, 1085 task_factory_.NewRunnableMethod(&HttpCache::OnBackendCreated, 1086 result, pending_op)); 1087 } else { 1088 building_backend_ = false; 1089 DeletePendingOp(pending_op); 1090 } 1091 1092 // The cache may be gone when we return from the callback. 1093 if (!item->DoCallback(result, backend)) 1094 item->NotifyTransaction(result, NULL); 1095} 1096 1097} // namespace net 1098