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