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