• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CreateNetworkSession(HostResolver * host_resolver,CertVerifier * cert_verifier,DnsRRResolver * dnsrr_resolver,DnsCertProvenanceChecker * dns_cert_checker,ProxyService * proxy_service,SSLHostInfoFactory * ssl_host_info_factory,SSLConfigService * ssl_config_service,HttpAuthHandlerFactory * http_auth_handler_factory,NetworkDelegate * network_delegate,NetLog * net_log)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 
DefaultBackend(CacheType type,const FilePath & path,int max_bytes,base::MessageLoopProxy * thread)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 
~DefaultBackend()79 HttpCache::DefaultBackend::~DefaultBackend() {}
80 
81 // static
InMemory(int max_bytes)82 HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) {
83   return new DefaultBackend(MEMORY_CACHE, FilePath(), max_bytes, NULL);
84 }
85 
CreateBackend(NetLog * net_log,disk_cache::Backend ** backend,CompletionCallback * callback)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 
ActiveEntry(disk_cache::Entry * entry)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 
~ActiveEntry()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 {
PendingOpnet::HttpCache::PendingOp115   PendingOp() : disk_entry(NULL), backend(NULL), writer(NULL), callback(NULL) {}
~PendingOpnet::HttpCache::PendingOp116   ~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:
WorkItem(WorkItemOperation operation,Transaction * trans,ActiveEntry ** entry)139   WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
140       : operation_(operation), trans_(trans), entry_(entry), callback_(NULL),
141         backend_(NULL) {}
WorkItem(WorkItemOperation operation,Transaction * trans,CompletionCallback * cb,disk_cache::Backend ** backend)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) {}
~WorkItem()146   ~WorkItem() {}
147 
148   // Calls back the transaction with the result of the operation.
NotifyTransaction(int result,ActiveEntry * entry)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.
DoCallback(int result,disk_cache::Backend * backend)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 
operation()169   WorkItemOperation operation() { return operation_; }
ClearTransaction()170   void ClearTransaction() { trans_ = NULL; }
ClearEntry()171   void ClearEntry() { entry_ = NULL; }
ClearCallback()172   void ClearCallback() { callback_ = NULL; }
Matches(Transaction * trans) const173   bool Matches(Transaction* trans) const { return trans == trans_; }
IsValid() const174   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:
BackendCallback(HttpCache * cache,PendingOp * pending_op)190   BackendCallback(HttpCache* cache, PendingOp* pending_op)
191       : cache_(cache), pending_op_(pending_op) {}
~BackendCallback()192   ~BackendCallback() {}
193 
RunWithParams(const Tuple1<int> & params)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 
Cancel()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:
MetadataWriter(HttpCache::Transaction * trans)221   explicit MetadataWriter(HttpCache::Transaction* trans)
222       : transaction_(trans),
223         ALLOW_THIS_IN_INITIALIZER_LIST(
224             callback_(this, &MetadataWriter::OnIOComplete)) {}
~MetadataWriter()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 
Write(const GURL & url,base::Time expected_response_time,IOBuffer * buf,int buf_len)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 
VerifyResponse(int result)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 
SelfDestroy()281 void HttpCache::MetadataWriter::SelfDestroy() {
282   delete this;
283 }
284 
OnIOComplete(int result)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:
SSLHostInfoFactoryAdaptor(CertVerifier * cert_verifier,HttpCache * http_cache)295   SSLHostInfoFactoryAdaptor(CertVerifier* cert_verifier, HttpCache* http_cache)
296       : cert_verifier_(cert_verifier),
297         http_cache_(http_cache) {
298   }
299 
GetForHost(const std::string & hostname,const SSLConfig & ssl_config)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 //-----------------------------------------------------------------------------
HttpCache(HostResolver * host_resolver,CertVerifier * cert_verifier,DnsRRResolver * dnsrr_resolver,DnsCertProvenanceChecker * dns_cert_checker_,ProxyService * proxy_service,SSLConfigService * ssl_config_service,HttpAuthHandlerFactory * http_auth_handler_factory,NetworkDelegate * network_delegate,NetLog * net_log,BackendFactory * backend_factory)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 
HttpCache(HttpNetworkSession * session,BackendFactory * backend_factory)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 
HttpCache(HttpTransactionFactory * network_layer,NetLog * net_log,BackendFactory * backend_factory)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 
~HttpCache()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 
GetBackend(disk_cache::Backend ** backend,CompletionCallback * callback)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 
GetCurrentBackend()425 disk_cache::Backend* HttpCache::GetCurrentBackend() {
426   return disk_cache_.get();
427 }
428 
429 // static
ParseResponseInfo(const char * data,int len,HttpResponseInfo * response_info,bool * response_truncated)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 
WriteMetadata(const GURL & url,base::Time expected_response_time,IOBuffer * buf,int buf_len)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 
CloseAllConnections()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 
CloseIdleConnections()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 
CreateTransaction(scoped_ptr<HttpTransaction> * trans)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 
GetCache()479 HttpCache* HttpCache::GetCache() {
480   return this;
481 }
482 
GetSession()483 HttpNetworkSession* HttpCache::GetSession() {
484   net::HttpNetworkLayer* network =
485       static_cast<net::HttpNetworkLayer*>(network_layer_.get());
486   return network->GetSession();
487 }
488 
Suspend(bool suspend)489 void HttpCache::Suspend(bool suspend) {
490   network_layer_->Suspend(suspend);
491 }
492 
493 //-----------------------------------------------------------------------------
494 
CreateBackend(disk_cache::Backend ** backend,CompletionCallback * callback)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 
GetBackendForTransaction(Transaction * trans)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.
GenerateCacheKey(const HttpRequestInfo * request)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 
DoomEntry(const std::string & key,Transaction * trans)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 
AsyncDoomEntry(const std::string & key,Transaction * trans)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 
FinalizeDoomedEntry(ActiveEntry * entry)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 
FindActiveEntry(const std::string & key)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 
ActivateEntry(disk_cache::Entry * disk_entry)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 
DeactivateEntry(ActiveEntry * entry)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.
SlowDeactivateEntry(ActiveEntry * entry)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 
GetPendingOp(const std::string & key)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 
DeletePendingOp(PendingOp * pending_op)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 
OpenEntry(const std::string & key,ActiveEntry ** entry,Transaction * trans)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 
CreateEntry(const std::string & key,ActiveEntry ** entry,Transaction * trans)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 
DestroyEntry(ActiveEntry * entry)781 void HttpCache::DestroyEntry(ActiveEntry* entry) {
782   if (entry->doomed) {
783     FinalizeDoomedEntry(entry);
784   } else {
785     DeactivateEntry(entry);
786   }
787 }
788 
AddTransactionToEntry(ActiveEntry * entry,Transaction * trans)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 
DoneWithEntry(ActiveEntry * entry,Transaction * trans,bool cancel)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 
DoneWritingToEntry(ActiveEntry * entry,bool success)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 
DoneReadingFromEntry(ActiveEntry * entry,Transaction * trans)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 
ConvertWriterToReader(ActiveEntry * entry)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 
GetLoadStateForPendingTransaction(const Transaction * trans)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 
RemovePendingTransaction(Transaction * trans)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 
RemovePendingTransactionFromEntry(ActiveEntry * entry,Transaction * trans)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 
RemovePendingTransactionFromPendingOp(PendingOp * pending_op,Transaction * trans)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 
ProcessPendingQueue(ActiveEntry * entry)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 
OnProcessPendingQueue(ActiveEntry * entry)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 
OnIOComplete(int result,PendingOp * pending_op)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 
OnBackendCreated(int result,PendingOp * pending_op)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