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