1 // Copyright (c) 2006-2009 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/format_macros.h" 16 #include "base/message_loop.h" 17 #include "base/pickle.h" 18 #include "base/ref_counted.h" 19 #include "base/string_util.h" 20 #include "net/base/io_buffer.h" 21 #include "net/base/net_errors.h" 22 #include "net/disk_cache/disk_cache.h" 23 #include "net/flip/flip_session_pool.h" 24 #include "net/http/http_cache_transaction.h" 25 #include "net/http/http_network_layer.h" 26 #include "net/http/http_network_session.h" 27 #include "net/http/http_request_info.h" 28 #include "net/http/http_response_headers.h" 29 #include "net/http/http_response_info.h" 30 31 namespace net { 32 33 // disk cache entry data indices. 34 enum { 35 kResponseInfoIndex, 36 kResponseContentIndex 37 }; 38 39 //----------------------------------------------------------------------------- 40 41 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* e) 42 : disk_entry(e), 43 writer(NULL), 44 will_process_pending_queue(false), 45 doomed(false) { 46 } 47 48 HttpCache::ActiveEntry::~ActiveEntry() { 49 if (disk_entry) 50 disk_entry->Close(); 51 } 52 53 //----------------------------------------------------------------------------- 54 55 // This structure keeps track of work items that are attempting to create or 56 // open cache entries. 57 struct HttpCache::NewEntry { 58 NewEntry() : disk_entry(NULL), writer(NULL) {} 59 ~NewEntry() {} 60 61 disk_cache::Entry* disk_entry; 62 WorkItem* writer; 63 WorkItemList pending_queue; 64 }; 65 66 //----------------------------------------------------------------------------- 67 68 // The type of operation represented by a work item. 69 enum WorkItemOperation { 70 WI_OPEN_ENTRY, 71 WI_CREATE_ENTRY, 72 WI_DOOM_ENTRY 73 }; 74 75 // A work item encapsulates a single request for cache entry with all the 76 // information needed to complete that request. 77 class HttpCache::WorkItem { 78 public: 79 WorkItem(ActiveEntry** entry, CompletionCallback* callback, 80 WorkItemOperation operation) 81 : entry_(entry), callback_(callback), operation_(operation) {} 82 ~WorkItem() {} 83 WorkItemOperation operation() { return operation_; } 84 85 // Calls back the transaction with the result of the operation. 86 void NotifyTransaction(int result, ActiveEntry* entry) { 87 if (entry_) 88 *entry_ = entry; 89 if (callback_) 90 callback_->Run(result); 91 } 92 93 void ClearCallback() { callback_ = NULL; } 94 void ClearEntry() { entry_ = NULL; } 95 bool Matches(CompletionCallback* cb) const { return cb == callback_; } 96 bool IsValid() const { return callback_ || entry_; } 97 98 private: 99 ActiveEntry** entry_; 100 CompletionCallback* callback_; 101 WorkItemOperation operation_; 102 }; 103 104 //----------------------------------------------------------------------------- 105 106 // This class is a specialized type of CompletionCallback that allows us to 107 // pass multiple arguments to the completion routine. 108 class HttpCache::BackendCallback : public CallbackRunner<Tuple1<int> > { 109 public: 110 BackendCallback(HttpCache* cache, NewEntry* entry) 111 : cache_(cache), entry_(entry) {} 112 ~BackendCallback() {} 113 114 virtual void RunWithParams(const Tuple1<int>& params) { 115 cache_->OnIOComplete(params.a, entry_); 116 delete this; 117 } 118 119 private: 120 HttpCache* cache_; 121 NewEntry* entry_; 122 DISALLOW_COPY_AND_ASSIGN(BackendCallback); 123 }; 124 125 //----------------------------------------------------------------------------- 126 127 HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier, 128 HostResolver* host_resolver, 129 ProxyService* proxy_service, 130 SSLConfigService* ssl_config_service, 131 const FilePath& cache_dir, 132 int cache_size) 133 : disk_cache_dir_(cache_dir), 134 mode_(NORMAL), 135 type_(DISK_CACHE), 136 network_layer_(HttpNetworkLayer::CreateFactory( 137 network_change_notifier, host_resolver, proxy_service, 138 ssl_config_service)), 139 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), 140 enable_range_support_(true), 141 cache_size_(cache_size) { 142 } 143 144 HttpCache::HttpCache(HttpNetworkSession* session, 145 const FilePath& cache_dir, 146 int cache_size) 147 : disk_cache_dir_(cache_dir), 148 mode_(NORMAL), 149 type_(DISK_CACHE), 150 network_layer_(HttpNetworkLayer::CreateFactory(session)), 151 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), 152 enable_range_support_(true), 153 cache_size_(cache_size) { 154 } 155 156 HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier, 157 HostResolver* host_resolver, 158 ProxyService* proxy_service, 159 SSLConfigService* ssl_config_service, 160 int cache_size) 161 : mode_(NORMAL), 162 type_(MEMORY_CACHE), 163 network_layer_(HttpNetworkLayer::CreateFactory( 164 network_change_notifier, host_resolver, proxy_service, 165 ssl_config_service)), 166 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), 167 enable_range_support_(true), 168 cache_size_(cache_size) { 169 } 170 171 HttpCache::HttpCache(HttpTransactionFactory* network_layer, 172 disk_cache::Backend* disk_cache) 173 : mode_(NORMAL), 174 type_(DISK_CACHE), 175 network_layer_(network_layer), 176 disk_cache_(disk_cache), 177 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), 178 enable_range_support_(true), 179 cache_size_(0) { 180 } 181 182 HttpCache::~HttpCache() { 183 // If we have any active entries remaining, then we need to deactivate them. 184 // We may have some pending calls to OnProcessPendingQueue, but since those 185 // won't run (due to our destruction), we can simply ignore the corresponding 186 // will_process_pending_queue flag. 187 while (!active_entries_.empty()) { 188 ActiveEntry* entry = active_entries_.begin()->second; 189 entry->will_process_pending_queue = false; 190 entry->pending_queue.clear(); 191 entry->readers.clear(); 192 entry->writer = NULL; 193 DeactivateEntry(entry); 194 } 195 196 ActiveEntriesSet::iterator it = doomed_entries_.begin(); 197 for (; it != doomed_entries_.end(); ++it) 198 delete *it; 199 } 200 201 disk_cache::Backend* HttpCache::GetBackend() { 202 if (disk_cache_.get()) 203 return disk_cache_.get(); 204 205 DCHECK_GE(cache_size_, 0); 206 if (type_ == MEMORY_CACHE) { 207 // We may end up with no folder name and no cache if the initialization 208 // of the disk cache fails. We want to be sure that what we wanted to have 209 // was an in-memory cache. 210 disk_cache_.reset(disk_cache::CreateInMemoryCacheBackend(cache_size_)); 211 } else if (!disk_cache_dir_.empty()) { 212 disk_cache_.reset(disk_cache::CreateCacheBackend(disk_cache_dir_, true, 213 cache_size_, type_)); 214 disk_cache_dir_ = FilePath(); // Reclaim memory. 215 } 216 return disk_cache_.get(); 217 } 218 219 int HttpCache::CreateTransaction(scoped_ptr<HttpTransaction>* trans) { 220 // Do lazy initialization of disk cache if needed. 221 GetBackend(); 222 trans->reset(new HttpCache::Transaction(this, enable_range_support_)); 223 return OK; 224 } 225 226 HttpCache* HttpCache::GetCache() { 227 return this; 228 } 229 230 HttpNetworkSession* HttpCache::GetSession() { 231 net::HttpNetworkLayer* network = 232 static_cast<net::HttpNetworkLayer*>(network_layer_.get()); 233 return network->GetSession(); 234 } 235 236 void HttpCache::Suspend(bool suspend) { 237 network_layer_->Suspend(suspend); 238 } 239 240 // static 241 bool HttpCache::ParseResponseInfo(const char* data, int len, 242 HttpResponseInfo* response_info, 243 bool* response_truncated) { 244 Pickle pickle(data, len); 245 return response_info->InitFromPickle(pickle, response_truncated); 246 } 247 248 // static 249 bool HttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry, 250 HttpResponseInfo* response_info, 251 bool* response_truncated) { 252 int size = disk_entry->GetDataSize(kResponseInfoIndex); 253 254 scoped_refptr<IOBuffer> buffer = new IOBuffer(size); 255 int rv = disk_entry->ReadData(kResponseInfoIndex, 0, buffer, size, NULL); 256 if (rv != size) { 257 DLOG(ERROR) << "ReadData failed: " << rv; 258 return false; 259 } 260 261 return ParseResponseInfo(buffer->data(), size, response_info, 262 response_truncated); 263 } 264 265 // static 266 bool HttpCache::WriteResponseInfo(disk_cache::Entry* disk_entry, 267 const HttpResponseInfo* response_info, 268 bool skip_transient_headers, 269 bool response_truncated) { 270 Pickle pickle; 271 response_info->Persist( 272 &pickle, skip_transient_headers, response_truncated); 273 274 scoped_refptr<WrappedIOBuffer> data = new WrappedIOBuffer( 275 reinterpret_cast<const char*>(pickle.data())); 276 int len = static_cast<int>(pickle.size()); 277 278 return disk_entry->WriteData(kResponseInfoIndex, 0, data, len, NULL, 279 true) == len; 280 } 281 282 // Generate a key that can be used inside the cache. 283 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) { 284 // Strip out the reference, username, and password sections of the URL. 285 std::string url = HttpUtil::SpecForRequest(request->url); 286 287 DCHECK(mode_ != DISABLE); 288 if (mode_ == NORMAL) { 289 // No valid URL can begin with numerals, so we should not have to worry 290 // about collisions with normal URLs. 291 if (request->upload_data && request->upload_data->identifier()) { 292 url.insert(0, StringPrintf("%" PRId64 "/", 293 request->upload_data->identifier())); 294 } 295 return url; 296 } 297 298 // In playback and record mode, we cache everything. 299 300 // Lazily initialize. 301 if (playback_cache_map_ == NULL) 302 playback_cache_map_.reset(new PlaybackCacheMap()); 303 304 // Each time we request an item from the cache, we tag it with a 305 // generation number. During playback, multiple fetches for the same 306 // item will use the same generation number and pull the proper 307 // instance of an URL from the cache. 308 int generation = 0; 309 DCHECK(playback_cache_map_ != NULL); 310 if (playback_cache_map_->find(url) != playback_cache_map_->end()) 311 generation = (*playback_cache_map_)[url]; 312 (*playback_cache_map_)[url] = generation + 1; 313 314 // The key into the cache is GENERATION # + METHOD + URL. 315 std::string result = IntToString(generation); 316 result.append(request->method); 317 result.append(url); 318 return result; 319 } 320 321 int HttpCache::DoomEntry(const std::string& key, CompletionCallback* callback) { 322 // Need to abandon the ActiveEntry, but any transaction attached to the entry 323 // should not be impacted. Dooming an entry only means that it will no 324 // longer be returned by FindActiveEntry (and it will also be destroyed once 325 // all consumers are finished with the entry). 326 ActiveEntriesMap::iterator it = active_entries_.find(key); 327 if (it == active_entries_.end()) { 328 return AsyncDoomEntry(key, callback); 329 } 330 331 ActiveEntry* entry = it->second; 332 active_entries_.erase(it); 333 334 // We keep track of doomed entries so that we can ensure that they are 335 // cleaned up properly when the cache is destroyed. 336 doomed_entries_.insert(entry); 337 338 entry->disk_entry->Doom(); 339 entry->doomed = true; 340 341 DCHECK(entry->writer || !entry->readers.empty()); 342 return OK; 343 } 344 345 int HttpCache::AsyncDoomEntry(const std::string& key, 346 CompletionCallback* callback) { 347 DCHECK(callback); 348 WorkItem* item = new WorkItem(NULL, callback, WI_DOOM_ENTRY); 349 NewEntry* new_entry = GetNewEntry(key); 350 if (new_entry->writer) { 351 new_entry->pending_queue.push_back(item); 352 return ERR_IO_PENDING; 353 } 354 355 DCHECK(new_entry->pending_queue.empty()); 356 357 new_entry->writer = item; 358 BackendCallback* my_callback = new BackendCallback(this, new_entry); 359 360 int rv = disk_cache_->DoomEntry(key, my_callback); 361 if (rv != ERR_IO_PENDING) { 362 item->ClearCallback(); 363 my_callback->Run(rv); 364 } 365 366 return rv; 367 } 368 369 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) { 370 DCHECK(entry->doomed); 371 DCHECK(!entry->writer); 372 DCHECK(entry->readers.empty()); 373 DCHECK(entry->pending_queue.empty()); 374 375 ActiveEntriesSet::iterator it = doomed_entries_.find(entry); 376 DCHECK(it != doomed_entries_.end()); 377 doomed_entries_.erase(it); 378 379 delete entry; 380 } 381 382 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) { 383 ActiveEntriesMap::const_iterator it = active_entries_.find(key); 384 return it != active_entries_.end() ? it->second : NULL; 385 } 386 387 HttpCache::NewEntry* HttpCache::GetNewEntry(const std::string& key) { 388 DCHECK(!FindActiveEntry(key)); 389 390 NewEntriesMap::const_iterator it = new_entries_.find(key); 391 if (it != new_entries_.end()) 392 return it->second; 393 394 NewEntry* entry = new NewEntry(); 395 new_entries_[key] = entry; 396 return entry; 397 } 398 399 void HttpCache::DeleteNewEntry(NewEntry* entry) { 400 std::string key; 401 if (entry->disk_entry) 402 key = entry->disk_entry->GetKey(); 403 404 if (!key.empty()) { 405 NewEntriesMap::iterator it = new_entries_.find(key); 406 DCHECK(it != new_entries_.end()); 407 new_entries_.erase(it); 408 } else { 409 for (NewEntriesMap::iterator it = new_entries_.begin(); 410 it != new_entries_.end(); ++it) { 411 if (it->second == entry) { 412 new_entries_.erase(it); 413 break; 414 } 415 } 416 } 417 418 delete entry; 419 } 420 421 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry, 422 CompletionCallback* callback) { 423 ActiveEntry* active_entry = FindActiveEntry(key); 424 if (active_entry) { 425 *entry = active_entry; 426 return OK; 427 } 428 429 WorkItem* item = new WorkItem(entry, callback, WI_OPEN_ENTRY); 430 NewEntry* new_entry = GetNewEntry(key); 431 if (new_entry->writer) { 432 new_entry->pending_queue.push_back(item); 433 return ERR_IO_PENDING; 434 } 435 436 DCHECK(new_entry->pending_queue.empty()); 437 438 new_entry->writer = item; 439 BackendCallback* my_callback = new BackendCallback(this, new_entry); 440 441 int rv = disk_cache_->OpenEntry(key, &(new_entry->disk_entry), my_callback); 442 if (rv != ERR_IO_PENDING) { 443 item->ClearCallback(); 444 my_callback->Run(rv); 445 } 446 447 return rv; 448 } 449 450 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry, 451 CompletionCallback* callback) { 452 DCHECK(!FindActiveEntry(key)); 453 454 WorkItem* item = new WorkItem(entry, callback, WI_CREATE_ENTRY); 455 NewEntry* new_entry = GetNewEntry(key); 456 if (new_entry->writer) { 457 new_entry->pending_queue.push_back(item); 458 return ERR_IO_PENDING; 459 } 460 461 DCHECK(new_entry->pending_queue.empty()); 462 463 new_entry->writer = item; 464 BackendCallback* my_callback = new BackendCallback(this, new_entry); 465 466 int rv = disk_cache_->CreateEntry(key, &(new_entry->disk_entry), my_callback); 467 if (rv != ERR_IO_PENDING) { 468 item->ClearCallback(); 469 my_callback->Run(rv); 470 } 471 472 return rv; 473 } 474 475 void HttpCache::DestroyEntry(ActiveEntry* entry) { 476 if (entry->doomed) { 477 FinalizeDoomedEntry(entry); 478 } else { 479 DeactivateEntry(entry); 480 } 481 } 482 483 HttpCache::ActiveEntry* HttpCache::ActivateEntry( 484 const std::string& key, 485 disk_cache::Entry* disk_entry) { 486 DCHECK(!FindActiveEntry(key)); 487 ActiveEntry* entry = new ActiveEntry(disk_entry); 488 active_entries_[key] = entry; 489 return entry; 490 } 491 492 void HttpCache::DeactivateEntry(ActiveEntry* entry) { 493 DCHECK(!entry->will_process_pending_queue); 494 DCHECK(!entry->doomed); 495 DCHECK(!entry->writer); 496 DCHECK(entry->readers.empty()); 497 DCHECK(entry->pending_queue.empty()); 498 499 std::string key = entry->disk_entry->GetKey(); 500 if (key.empty()) 501 return SlowDeactivateEntry(entry); 502 503 ActiveEntriesMap::iterator it = active_entries_.find(key); 504 DCHECK(it != active_entries_.end()); 505 DCHECK(it->second == entry); 506 507 active_entries_.erase(it); 508 delete entry; 509 } 510 511 // We don't know this entry's key so we have to find it without it. 512 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) { 513 for (ActiveEntriesMap::iterator it = active_entries_.begin(); 514 it != active_entries_.end(); ++it) { 515 if (it->second == entry) { 516 active_entries_.erase(it); 517 delete entry; 518 break; 519 } 520 } 521 } 522 523 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) { 524 DCHECK(entry); 525 526 // We implement a basic reader/writer lock for the disk cache entry. If 527 // there is already a writer, then everyone has to wait for the writer to 528 // finish before they can access the cache entry. There can be multiple 529 // readers. 530 // 531 // NOTE: If the transaction can only write, then the entry should not be in 532 // use (since any existing entry should have already been doomed). 533 534 if (entry->writer || entry->will_process_pending_queue) { 535 entry->pending_queue.push_back(trans); 536 return ERR_IO_PENDING; 537 } 538 539 if (trans->mode() & Transaction::WRITE) { 540 // transaction needs exclusive access to the entry 541 if (entry->readers.empty()) { 542 entry->writer = trans; 543 } else { 544 entry->pending_queue.push_back(trans); 545 return ERR_IO_PENDING; 546 } 547 } else { 548 // transaction needs read access to the entry 549 entry->readers.push_back(trans); 550 } 551 552 // We do this before calling EntryAvailable to force any further calls to 553 // AddTransactionToEntry to add their transaction to the pending queue, which 554 // ensures FIFO ordering. 555 if (!entry->writer && !entry->pending_queue.empty()) 556 ProcessPendingQueue(entry); 557 558 return trans->EntryAvailable(entry); 559 } 560 561 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans, 562 bool cancel) { 563 // If we already posted a task to move on to the next transaction and this was 564 // the writer, there is nothing to cancel. 565 if (entry->will_process_pending_queue && entry->readers.empty()) 566 return; 567 568 if (entry->writer) { 569 DCHECK(trans == entry->writer); 570 571 // Assume there was a failure. 572 bool success = false; 573 if (cancel) { 574 DCHECK(entry->disk_entry); 575 // This is a successful operation in the sense that we want to keep the 576 // entry. 577 success = trans->AddTruncatedFlag(); 578 } 579 DoneWritingToEntry(entry, success); 580 } else { 581 DoneReadingFromEntry(entry, trans); 582 } 583 } 584 585 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) { 586 DCHECK(entry->readers.empty()); 587 588 entry->writer = NULL; 589 590 if (success) { 591 ProcessPendingQueue(entry); 592 } else { 593 DCHECK(!entry->will_process_pending_queue); 594 595 // We failed to create this entry. 596 TransactionList pending_queue; 597 pending_queue.swap(entry->pending_queue); 598 599 entry->disk_entry->Doom(); 600 DestroyEntry(entry); 601 602 // We need to do something about these pending entries, which now need to 603 // be added to a new entry. 604 while (!pending_queue.empty()) { 605 pending_queue.front()->AddToEntry(); 606 pending_queue.pop_front(); 607 } 608 } 609 } 610 611 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) { 612 DCHECK(!entry->writer); 613 614 TransactionList::iterator it = 615 std::find(entry->readers.begin(), entry->readers.end(), trans); 616 DCHECK(it != entry->readers.end()); 617 618 entry->readers.erase(it); 619 620 ProcessPendingQueue(entry); 621 } 622 623 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) { 624 DCHECK(entry->writer); 625 DCHECK(entry->writer->mode() == Transaction::READ_WRITE); 626 DCHECK(entry->readers.empty()); 627 628 Transaction* trans = entry->writer; 629 630 entry->writer = NULL; 631 entry->readers.push_back(trans); 632 633 ProcessPendingQueue(entry); 634 } 635 636 void HttpCache::RemovePendingTransaction(Transaction* trans, 637 CompletionCallback* cb) { 638 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key()); 639 bool found = false; 640 if (i != active_entries_.end()) 641 found = RemovePendingTransactionFromEntry(i->second, trans); 642 643 if (found) 644 return; 645 646 NewEntriesMap::const_iterator j = new_entries_.find(trans->key()); 647 if (j != new_entries_.end()) 648 found = RemovePendingCallbackFromNewEntry(j->second, cb); 649 650 ActiveEntriesSet::iterator k = doomed_entries_.begin(); 651 for (; k != doomed_entries_.end() && !found; ++k) 652 found = RemovePendingTransactionFromEntry(*k, trans); 653 654 DCHECK(found) << "Pending transaction not found"; 655 } 656 657 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry, 658 Transaction* trans) { 659 TransactionList& pending_queue = entry->pending_queue; 660 661 TransactionList::iterator j = 662 find(pending_queue.begin(), pending_queue.end(), trans); 663 if (j == pending_queue.end()) 664 return false; 665 666 pending_queue.erase(j); 667 return true; 668 } 669 670 bool HttpCache::RemovePendingCallbackFromNewEntry(NewEntry* entry, 671 CompletionCallback* cb) { 672 if (entry->writer->Matches(cb)) { 673 entry->writer->ClearCallback(); 674 entry->writer->ClearEntry(); 675 return true; 676 } 677 WorkItemList& pending_queue = entry->pending_queue; 678 679 WorkItemList::iterator it = pending_queue.begin(); 680 for (; it != pending_queue.end(); ++it) { 681 if ((*it)->Matches(cb)) { 682 delete *it; 683 pending_queue.erase(it); 684 return true; 685 } 686 } 687 return false; 688 } 689 690 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) { 691 // Multiple readers may finish with an entry at once, so we want to batch up 692 // calls to OnProcessPendingQueue. This flag also tells us that we should 693 // not delete the entry before OnProcessPendingQueue runs. 694 if (entry->will_process_pending_queue) 695 return; 696 entry->will_process_pending_queue = true; 697 698 MessageLoop::current()->PostTask(FROM_HERE, 699 task_factory_.NewRunnableMethod(&HttpCache::OnProcessPendingQueue, 700 entry)); 701 } 702 703 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) { 704 entry->will_process_pending_queue = false; 705 DCHECK(!entry->writer); 706 707 // If no one is interested in this entry, then we can de-activate it. 708 if (entry->pending_queue.empty()) { 709 if (entry->readers.empty()) 710 DestroyEntry(entry); 711 return; 712 } 713 714 // Promote next transaction from the pending queue. 715 Transaction* next = entry->pending_queue.front(); 716 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty()) 717 return; // Have to wait. 718 719 entry->pending_queue.erase(entry->pending_queue.begin()); 720 721 AddTransactionToEntry(entry, next); 722 } 723 724 void HttpCache::OnIOComplete(int result, NewEntry* new_entry) { 725 scoped_ptr<WorkItem> item(new_entry->writer); 726 WorkItemOperation op = item->operation(); 727 bool fail_requests = false; 728 729 ActiveEntry* entry = NULL; 730 std::string key; 731 if (result == OK) { 732 if (op == WI_DOOM_ENTRY) { 733 // Anything after a Doom has to be restarted. 734 fail_requests = true; 735 } else if (item->IsValid()) { 736 key = new_entry->disk_entry->GetKey(); 737 entry = ActivateEntry(key, new_entry->disk_entry); 738 } else { 739 // The writer transaction is gone. 740 if (op == WI_CREATE_ENTRY) 741 new_entry->disk_entry->Doom(); 742 new_entry->disk_entry->Close(); 743 fail_requests = true; 744 } 745 } 746 747 // We are about to notify a bunch of transactions, and they may decide to 748 // re-issue a request (or send a different one). If we don't delete new_entry, 749 // the new request will be appended to the end of the list, and we'll see it 750 // again from this point before it has a chance to complete (and we'll be 751 // messing out the request order). The down side is that if for some reason 752 // notifying request A ends up cancelling request B (for the same key), we 753 // won't find request B anywhere (because it would be in a local variable 754 // here) and that's bad. If there is a chance for that to happen, we'll have 755 // to move the callback used to be a CancelableCallback. By the way, for this 756 // to happen the action (to cancel B) has to be synchronous to the 757 // notification for request A. 758 WorkItemList pending_items; 759 pending_items.swap(new_entry->pending_queue); 760 DeleteNewEntry(new_entry); 761 762 item->NotifyTransaction(result, entry); 763 764 while (!pending_items.empty()) { 765 item.reset(pending_items.front()); 766 pending_items.pop_front(); 767 768 if (item->operation() == WI_DOOM_ENTRY) { 769 // A queued doom request is always a race. 770 fail_requests = true; 771 } else if (result == OK) { 772 entry = FindActiveEntry(key); 773 if (!entry) 774 fail_requests = true; 775 } 776 777 if (fail_requests) { 778 item->NotifyTransaction(ERR_CACHE_RACE, NULL); 779 continue; 780 } 781 782 if (item->operation() == WI_CREATE_ENTRY) { 783 if (result == OK) { 784 // A second Create request, but the first request succeded. 785 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL); 786 } else { 787 if (op != WI_CREATE_ENTRY) { 788 // Failed Open followed by a Create. 789 item->NotifyTransaction(ERR_CACHE_RACE, NULL); 790 fail_requests = true; 791 } else { 792 item->NotifyTransaction(result, entry); 793 } 794 } 795 } else { 796 if (op == WI_CREATE_ENTRY && result != OK) { 797 // Failed Create followed by an Open. 798 item->NotifyTransaction(ERR_CACHE_RACE, NULL); 799 fail_requests = true; 800 } else { 801 item->NotifyTransaction(result, entry); 802 } 803 } 804 } 805 } 806 807 void HttpCache::CloseCurrentConnections() { 808 net::HttpNetworkLayer* network = 809 static_cast<net::HttpNetworkLayer*>(network_layer_.get()); 810 HttpNetworkSession* session = network->GetSession(); 811 if (session) { 812 session->tcp_socket_pool()->CloseIdleSockets(); 813 if (session->flip_session_pool()) 814 session->flip_session_pool()->CloseAllSessions(); 815 session->ReplaceTCPSocketPool(); 816 } 817 } 818 819 //----------------------------------------------------------------------------- 820 821 } // namespace net 822