Home | History | Annotate | Download | only in http
      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