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