• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/http/http_cache.h"
6 
7 #include <algorithm>
8 
9 #include "base/compiler_specific.h"
10 
11 #if defined(OS_POSIX)
12 #include <unistd.h>
13 #endif
14 
15 #include "base/format_macros.h"
16 #include "base/message_loop.h"
17 #include "base/pickle.h"
18 #include "base/ref_counted.h"
19 #include "base/string_util.h"
20 #include "net/base/io_buffer.h"
21 #include "net/base/net_errors.h"
22 #include "net/disk_cache/disk_cache.h"
23 #include "net/flip/flip_session_pool.h"
24 #include "net/http/http_cache_transaction.h"
25 #include "net/http/http_network_layer.h"
26 #include "net/http/http_network_session.h"
27 #include "net/http/http_request_info.h"
28 #include "net/http/http_response_headers.h"
29 #include "net/http/http_response_info.h"
30 
31 namespace net {
32 
33 // disk cache entry data indices.
34 enum {
35   kResponseInfoIndex,
36   kResponseContentIndex
37 };
38 
39 //-----------------------------------------------------------------------------
40 
ActiveEntry(disk_cache::Entry * e)41 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* e)
42     : disk_entry(e),
43       writer(NULL),
44       will_process_pending_queue(false),
45       doomed(false) {
46 }
47 
~ActiveEntry()48 HttpCache::ActiveEntry::~ActiveEntry() {
49   if (disk_entry)
50     disk_entry->Close();
51 }
52 
53 //-----------------------------------------------------------------------------
54 
55 // This structure keeps track of work items that are attempting to create or
56 // open cache entries.
57 struct HttpCache::NewEntry {
NewEntrynet::HttpCache::NewEntry58   NewEntry() : disk_entry(NULL), writer(NULL) {}
~NewEntrynet::HttpCache::NewEntry59   ~NewEntry() {}
60 
61   disk_cache::Entry* disk_entry;
62   WorkItem* writer;
63   WorkItemList pending_queue;
64 };
65 
66 //-----------------------------------------------------------------------------
67 
68 // The type of operation represented by a work item.
69 enum WorkItemOperation {
70   WI_OPEN_ENTRY,
71   WI_CREATE_ENTRY,
72   WI_DOOM_ENTRY
73 };
74 
75 // A work item encapsulates a single request for cache entry with all the
76 // information needed to complete that request.
77 class HttpCache::WorkItem {
78  public:
WorkItem(ActiveEntry ** entry,CompletionCallback * callback,WorkItemOperation operation)79   WorkItem(ActiveEntry** entry, CompletionCallback* callback,
80            WorkItemOperation operation)
81       : entry_(entry), callback_(callback), operation_(operation) {}
~WorkItem()82   ~WorkItem() {}
operation()83   WorkItemOperation operation() { return operation_; }
84 
85   // Calls back the transaction with the result of the operation.
NotifyTransaction(int result,ActiveEntry * entry)86   void NotifyTransaction(int result, ActiveEntry* entry) {
87     if (entry_)
88       *entry_ = entry;
89     if (callback_)
90       callback_->Run(result);
91   }
92 
ClearCallback()93   void ClearCallback() { callback_ = NULL; }
ClearEntry()94   void ClearEntry() { entry_ = NULL; }
Matches(CompletionCallback * cb) const95   bool Matches(CompletionCallback* cb) const { return cb == callback_; }
IsValid() const96   bool IsValid() const { return callback_ || entry_; }
97 
98  private:
99   ActiveEntry** entry_;
100   CompletionCallback* callback_;
101   WorkItemOperation operation_;
102 };
103 
104 //-----------------------------------------------------------------------------
105 
106 // This class is a specialized type of CompletionCallback that allows us to
107 // pass multiple arguments to the completion routine.
108 class HttpCache::BackendCallback : public CallbackRunner<Tuple1<int> > {
109  public:
BackendCallback(HttpCache * cache,NewEntry * entry)110   BackendCallback(HttpCache* cache, NewEntry* entry)
111       : cache_(cache), entry_(entry) {}
~BackendCallback()112   ~BackendCallback() {}
113 
RunWithParams(const Tuple1<int> & params)114   virtual void RunWithParams(const Tuple1<int>& params) {
115     cache_->OnIOComplete(params.a, entry_);
116     delete this;
117   }
118 
119  private:
120   HttpCache* cache_;
121   NewEntry* entry_;
122   DISALLOW_COPY_AND_ASSIGN(BackendCallback);
123 };
124 
125 //-----------------------------------------------------------------------------
126 
HttpCache(NetworkChangeNotifier * network_change_notifier,HostResolver * host_resolver,ProxyService * proxy_service,SSLConfigService * ssl_config_service,const FilePath & cache_dir,int cache_size)127 HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier,
128                      HostResolver* host_resolver,
129                      ProxyService* proxy_service,
130                      SSLConfigService* ssl_config_service,
131                      const FilePath& cache_dir,
132                      int cache_size)
133     : disk_cache_dir_(cache_dir),
134       mode_(NORMAL),
135       type_(DISK_CACHE),
136       network_layer_(HttpNetworkLayer::CreateFactory(
137           network_change_notifier, host_resolver, proxy_service,
138           ssl_config_service)),
139       ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
140       enable_range_support_(true),
141       cache_size_(cache_size) {
142 }
143 
HttpCache(HttpNetworkSession * session,const FilePath & cache_dir,int cache_size)144 HttpCache::HttpCache(HttpNetworkSession* session,
145                      const FilePath& cache_dir,
146                      int cache_size)
147     : disk_cache_dir_(cache_dir),
148       mode_(NORMAL),
149       type_(DISK_CACHE),
150       network_layer_(HttpNetworkLayer::CreateFactory(session)),
151       ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
152       enable_range_support_(true),
153       cache_size_(cache_size) {
154 }
155 
HttpCache(NetworkChangeNotifier * network_change_notifier,HostResolver * host_resolver,ProxyService * proxy_service,SSLConfigService * ssl_config_service,int cache_size)156 HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier,
157                      HostResolver* host_resolver,
158                      ProxyService* proxy_service,
159                      SSLConfigService* ssl_config_service,
160                      int cache_size)
161     : mode_(NORMAL),
162       type_(MEMORY_CACHE),
163       network_layer_(HttpNetworkLayer::CreateFactory(
164           network_change_notifier, host_resolver, proxy_service,
165           ssl_config_service)),
166       ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
167       enable_range_support_(true),
168       cache_size_(cache_size) {
169 }
170 
HttpCache(HttpTransactionFactory * network_layer,disk_cache::Backend * disk_cache)171 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
172                      disk_cache::Backend* disk_cache)
173     : mode_(NORMAL),
174       type_(DISK_CACHE),
175       network_layer_(network_layer),
176       disk_cache_(disk_cache),
177       ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
178       enable_range_support_(true),
179       cache_size_(0) {
180 }
181 
~HttpCache()182 HttpCache::~HttpCache() {
183   // If we have any active entries remaining, then we need to deactivate them.
184   // We may have some pending calls to OnProcessPendingQueue, but since those
185   // won't run (due to our destruction), we can simply ignore the corresponding
186   // will_process_pending_queue flag.
187   while (!active_entries_.empty()) {
188     ActiveEntry* entry = active_entries_.begin()->second;
189     entry->will_process_pending_queue = false;
190     entry->pending_queue.clear();
191     entry->readers.clear();
192     entry->writer = NULL;
193     DeactivateEntry(entry);
194   }
195 
196   ActiveEntriesSet::iterator it = doomed_entries_.begin();
197   for (; it != doomed_entries_.end(); ++it)
198     delete *it;
199 }
200 
GetBackend()201 disk_cache::Backend* HttpCache::GetBackend() {
202   if (disk_cache_.get())
203     return disk_cache_.get();
204 
205   DCHECK_GE(cache_size_, 0);
206   if (type_ == MEMORY_CACHE) {
207     // We may end up with no folder name and no cache if the initialization
208     // of the disk cache fails. We want to be sure that what we wanted to have
209     // was an in-memory cache.
210     disk_cache_.reset(disk_cache::CreateInMemoryCacheBackend(cache_size_));
211   } else if (!disk_cache_dir_.empty()) {
212     disk_cache_.reset(disk_cache::CreateCacheBackend(disk_cache_dir_, true,
213         cache_size_, type_));
214     disk_cache_dir_ = FilePath();  // Reclaim memory.
215   }
216   return disk_cache_.get();
217 }
218 
CreateTransaction(scoped_ptr<HttpTransaction> * trans)219 int HttpCache::CreateTransaction(scoped_ptr<HttpTransaction>* trans) {
220   // Do lazy initialization of disk cache if needed.
221   GetBackend();
222   trans->reset(new HttpCache::Transaction(this, enable_range_support_));
223   return OK;
224 }
225 
GetCache()226 HttpCache* HttpCache::GetCache() {
227   return this;
228 }
229 
GetSession()230 HttpNetworkSession* HttpCache::GetSession() {
231   net::HttpNetworkLayer* network =
232       static_cast<net::HttpNetworkLayer*>(network_layer_.get());
233   return network->GetSession();
234 }
235 
Suspend(bool suspend)236 void HttpCache::Suspend(bool suspend) {
237   network_layer_->Suspend(suspend);
238 }
239 
240 // static
ParseResponseInfo(const char * data,int len,HttpResponseInfo * response_info,bool * response_truncated)241 bool HttpCache::ParseResponseInfo(const char* data, int len,
242                                   HttpResponseInfo* response_info,
243                                   bool* response_truncated) {
244   Pickle pickle(data, len);
245   return response_info->InitFromPickle(pickle, response_truncated);
246 }
247 
248 // static
ReadResponseInfo(disk_cache::Entry * disk_entry,HttpResponseInfo * response_info,bool * response_truncated)249 bool HttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry,
250                                  HttpResponseInfo* response_info,
251                                  bool* response_truncated) {
252   int size = disk_entry->GetDataSize(kResponseInfoIndex);
253 
254   scoped_refptr<IOBuffer> buffer = new IOBuffer(size);
255   int rv = disk_entry->ReadData(kResponseInfoIndex, 0, buffer, size, NULL);
256   if (rv != size) {
257     DLOG(ERROR) << "ReadData failed: " << rv;
258     return false;
259   }
260 
261   return ParseResponseInfo(buffer->data(), size, response_info,
262                            response_truncated);
263 }
264 
265 // static
WriteResponseInfo(disk_cache::Entry * disk_entry,const HttpResponseInfo * response_info,bool skip_transient_headers,bool response_truncated)266 bool HttpCache::WriteResponseInfo(disk_cache::Entry* disk_entry,
267                                   const HttpResponseInfo* response_info,
268                                   bool skip_transient_headers,
269                                   bool response_truncated) {
270   Pickle pickle;
271   response_info->Persist(
272       &pickle, skip_transient_headers, response_truncated);
273 
274   scoped_refptr<WrappedIOBuffer> data = new WrappedIOBuffer(
275       reinterpret_cast<const char*>(pickle.data()));
276   int len = static_cast<int>(pickle.size());
277 
278   return disk_entry->WriteData(kResponseInfoIndex, 0, data, len, NULL,
279                                true) == len;
280 }
281 
282 // Generate a key that can be used inside the cache.
GenerateCacheKey(const HttpRequestInfo * request)283 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
284   // Strip out the reference, username, and password sections of the URL.
285   std::string url = HttpUtil::SpecForRequest(request->url);
286 
287   DCHECK(mode_ != DISABLE);
288   if (mode_ == NORMAL) {
289     // No valid URL can begin with numerals, so we should not have to worry
290     // about collisions with normal URLs.
291     if (request->upload_data && request->upload_data->identifier()) {
292       url.insert(0, StringPrintf("%" PRId64 "/",
293                                  request->upload_data->identifier()));
294     }
295     return url;
296   }
297 
298   // In playback and record mode, we cache everything.
299 
300   // Lazily initialize.
301   if (playback_cache_map_ == NULL)
302     playback_cache_map_.reset(new PlaybackCacheMap());
303 
304   // Each time we request an item from the cache, we tag it with a
305   // generation number.  During playback, multiple fetches for the same
306   // item will use the same generation number and pull the proper
307   // instance of an URL from the cache.
308   int generation = 0;
309   DCHECK(playback_cache_map_ != NULL);
310   if (playback_cache_map_->find(url) != playback_cache_map_->end())
311     generation = (*playback_cache_map_)[url];
312   (*playback_cache_map_)[url] = generation + 1;
313 
314   // The key into the cache is GENERATION # + METHOD + URL.
315   std::string result = IntToString(generation);
316   result.append(request->method);
317   result.append(url);
318   return result;
319 }
320 
DoomEntry(const std::string & key,CompletionCallback * callback)321 int HttpCache::DoomEntry(const std::string& key, CompletionCallback* callback) {
322   // Need to abandon the ActiveEntry, but any transaction attached to the entry
323   // should not be impacted.  Dooming an entry only means that it will no
324   // longer be returned by FindActiveEntry (and it will also be destroyed once
325   // all consumers are finished with the entry).
326   ActiveEntriesMap::iterator it = active_entries_.find(key);
327   if (it == active_entries_.end()) {
328     return AsyncDoomEntry(key, callback);
329   }
330 
331   ActiveEntry* entry = it->second;
332   active_entries_.erase(it);
333 
334   // We keep track of doomed entries so that we can ensure that they are
335   // cleaned up properly when the cache is destroyed.
336   doomed_entries_.insert(entry);
337 
338   entry->disk_entry->Doom();
339   entry->doomed = true;
340 
341   DCHECK(entry->writer || !entry->readers.empty());
342   return OK;
343 }
344 
AsyncDoomEntry(const std::string & key,CompletionCallback * callback)345 int HttpCache::AsyncDoomEntry(const std::string& key,
346                               CompletionCallback* callback) {
347   DCHECK(callback);
348   WorkItem* item = new WorkItem(NULL, callback, WI_DOOM_ENTRY);
349   NewEntry* new_entry = GetNewEntry(key);
350   if (new_entry->writer) {
351     new_entry->pending_queue.push_back(item);
352     return ERR_IO_PENDING;
353   }
354 
355   DCHECK(new_entry->pending_queue.empty());
356 
357   new_entry->writer = item;
358   BackendCallback* my_callback = new BackendCallback(this, new_entry);
359 
360   int rv = disk_cache_->DoomEntry(key, my_callback);
361   if (rv != ERR_IO_PENDING) {
362     item->ClearCallback();
363     my_callback->Run(rv);
364   }
365 
366   return rv;
367 }
368 
FinalizeDoomedEntry(ActiveEntry * entry)369 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
370   DCHECK(entry->doomed);
371   DCHECK(!entry->writer);
372   DCHECK(entry->readers.empty());
373   DCHECK(entry->pending_queue.empty());
374 
375   ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
376   DCHECK(it != doomed_entries_.end());
377   doomed_entries_.erase(it);
378 
379   delete entry;
380 }
381 
FindActiveEntry(const std::string & key)382 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
383   ActiveEntriesMap::const_iterator it = active_entries_.find(key);
384   return it != active_entries_.end() ? it->second : NULL;
385 }
386 
GetNewEntry(const std::string & key)387 HttpCache::NewEntry* HttpCache::GetNewEntry(const std::string& key) {
388   DCHECK(!FindActiveEntry(key));
389 
390   NewEntriesMap::const_iterator it = new_entries_.find(key);
391   if (it != new_entries_.end())
392     return it->second;
393 
394   NewEntry* entry = new NewEntry();
395   new_entries_[key] = entry;
396   return entry;
397 }
398 
DeleteNewEntry(NewEntry * entry)399 void HttpCache::DeleteNewEntry(NewEntry* entry) {
400   std::string key;
401   if (entry->disk_entry)
402     key = entry->disk_entry->GetKey();
403 
404   if (!key.empty()) {
405     NewEntriesMap::iterator it = new_entries_.find(key);
406     DCHECK(it != new_entries_.end());
407     new_entries_.erase(it);
408   } else {
409     for (NewEntriesMap::iterator it = new_entries_.begin();
410          it != new_entries_.end(); ++it) {
411       if (it->second == entry) {
412         new_entries_.erase(it);
413         break;
414       }
415     }
416   }
417 
418   delete entry;
419 }
420 
OpenEntry(const std::string & key,ActiveEntry ** entry,CompletionCallback * callback)421 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
422                          CompletionCallback* callback) {
423   ActiveEntry* active_entry = FindActiveEntry(key);
424   if (active_entry) {
425     *entry = active_entry;
426     return OK;
427   }
428 
429   WorkItem* item = new WorkItem(entry, callback, WI_OPEN_ENTRY);
430   NewEntry* new_entry = GetNewEntry(key);
431   if (new_entry->writer) {
432     new_entry->pending_queue.push_back(item);
433     return ERR_IO_PENDING;
434   }
435 
436   DCHECK(new_entry->pending_queue.empty());
437 
438   new_entry->writer = item;
439   BackendCallback* my_callback = new BackendCallback(this, new_entry);
440 
441   int rv = disk_cache_->OpenEntry(key, &(new_entry->disk_entry), my_callback);
442   if (rv != ERR_IO_PENDING) {
443     item->ClearCallback();
444     my_callback->Run(rv);
445   }
446 
447   return rv;
448 }
449 
CreateEntry(const std::string & key,ActiveEntry ** entry,CompletionCallback * callback)450 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
451                            CompletionCallback* callback) {
452   DCHECK(!FindActiveEntry(key));
453 
454   WorkItem* item = new WorkItem(entry, callback, WI_CREATE_ENTRY);
455   NewEntry* new_entry = GetNewEntry(key);
456   if (new_entry->writer) {
457     new_entry->pending_queue.push_back(item);
458     return ERR_IO_PENDING;
459   }
460 
461   DCHECK(new_entry->pending_queue.empty());
462 
463   new_entry->writer = item;
464   BackendCallback* my_callback = new BackendCallback(this, new_entry);
465 
466   int rv = disk_cache_->CreateEntry(key, &(new_entry->disk_entry), my_callback);
467   if (rv != ERR_IO_PENDING) {
468     item->ClearCallback();
469     my_callback->Run(rv);
470   }
471 
472   return rv;
473 }
474 
DestroyEntry(ActiveEntry * entry)475 void HttpCache::DestroyEntry(ActiveEntry* entry) {
476   if (entry->doomed) {
477     FinalizeDoomedEntry(entry);
478   } else {
479     DeactivateEntry(entry);
480   }
481 }
482 
ActivateEntry(const std::string & key,disk_cache::Entry * disk_entry)483 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
484     const std::string& key,
485     disk_cache::Entry* disk_entry) {
486   DCHECK(!FindActiveEntry(key));
487   ActiveEntry* entry = new ActiveEntry(disk_entry);
488   active_entries_[key] = entry;
489   return entry;
490 }
491 
DeactivateEntry(ActiveEntry * entry)492 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
493   DCHECK(!entry->will_process_pending_queue);
494   DCHECK(!entry->doomed);
495   DCHECK(!entry->writer);
496   DCHECK(entry->readers.empty());
497   DCHECK(entry->pending_queue.empty());
498 
499   std::string key = entry->disk_entry->GetKey();
500   if (key.empty())
501     return SlowDeactivateEntry(entry);
502 
503   ActiveEntriesMap::iterator it = active_entries_.find(key);
504   DCHECK(it != active_entries_.end());
505   DCHECK(it->second == entry);
506 
507   active_entries_.erase(it);
508   delete entry;
509 }
510 
511 // We don't know this entry's key so we have to find it without it.
SlowDeactivateEntry(ActiveEntry * entry)512 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
513   for (ActiveEntriesMap::iterator it = active_entries_.begin();
514        it != active_entries_.end(); ++it) {
515     if (it->second == entry) {
516       active_entries_.erase(it);
517       delete entry;
518       break;
519     }
520   }
521 }
522 
AddTransactionToEntry(ActiveEntry * entry,Transaction * trans)523 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
524   DCHECK(entry);
525 
526   // We implement a basic reader/writer lock for the disk cache entry.  If
527   // there is already a writer, then everyone has to wait for the writer to
528   // finish before they can access the cache entry.  There can be multiple
529   // readers.
530   //
531   // NOTE: If the transaction can only write, then the entry should not be in
532   // use (since any existing entry should have already been doomed).
533 
534   if (entry->writer || entry->will_process_pending_queue) {
535     entry->pending_queue.push_back(trans);
536     return ERR_IO_PENDING;
537   }
538 
539   if (trans->mode() & Transaction::WRITE) {
540     // transaction needs exclusive access to the entry
541     if (entry->readers.empty()) {
542       entry->writer = trans;
543     } else {
544       entry->pending_queue.push_back(trans);
545       return ERR_IO_PENDING;
546     }
547   } else {
548     // transaction needs read access to the entry
549     entry->readers.push_back(trans);
550   }
551 
552   // We do this before calling EntryAvailable to force any further calls to
553   // AddTransactionToEntry to add their transaction to the pending queue, which
554   // ensures FIFO ordering.
555   if (!entry->writer && !entry->pending_queue.empty())
556     ProcessPendingQueue(entry);
557 
558   return trans->EntryAvailable(entry);
559 }
560 
DoneWithEntry(ActiveEntry * entry,Transaction * trans,bool cancel)561 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
562                               bool cancel) {
563   // If we already posted a task to move on to the next transaction and this was
564   // the writer, there is nothing to cancel.
565   if (entry->will_process_pending_queue && entry->readers.empty())
566     return;
567 
568   if (entry->writer) {
569     DCHECK(trans == entry->writer);
570 
571     // Assume there was a failure.
572     bool success = false;
573     if (cancel) {
574       DCHECK(entry->disk_entry);
575       // This is a successful operation in the sense that we want to keep the
576       // entry.
577       success = trans->AddTruncatedFlag();
578     }
579     DoneWritingToEntry(entry, success);
580   } else {
581     DoneReadingFromEntry(entry, trans);
582   }
583 }
584 
DoneWritingToEntry(ActiveEntry * entry,bool success)585 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
586   DCHECK(entry->readers.empty());
587 
588   entry->writer = NULL;
589 
590   if (success) {
591     ProcessPendingQueue(entry);
592   } else {
593     DCHECK(!entry->will_process_pending_queue);
594 
595     // We failed to create this entry.
596     TransactionList pending_queue;
597     pending_queue.swap(entry->pending_queue);
598 
599     entry->disk_entry->Doom();
600     DestroyEntry(entry);
601 
602     // We need to do something about these pending entries, which now need to
603     // be added to a new entry.
604     while (!pending_queue.empty()) {
605       pending_queue.front()->AddToEntry();
606       pending_queue.pop_front();
607     }
608   }
609 }
610 
DoneReadingFromEntry(ActiveEntry * entry,Transaction * trans)611 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
612   DCHECK(!entry->writer);
613 
614   TransactionList::iterator it =
615       std::find(entry->readers.begin(), entry->readers.end(), trans);
616   DCHECK(it != entry->readers.end());
617 
618   entry->readers.erase(it);
619 
620   ProcessPendingQueue(entry);
621 }
622 
ConvertWriterToReader(ActiveEntry * entry)623 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
624   DCHECK(entry->writer);
625   DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
626   DCHECK(entry->readers.empty());
627 
628   Transaction* trans = entry->writer;
629 
630   entry->writer = NULL;
631   entry->readers.push_back(trans);
632 
633   ProcessPendingQueue(entry);
634 }
635 
RemovePendingTransaction(Transaction * trans,CompletionCallback * cb)636 void HttpCache::RemovePendingTransaction(Transaction* trans,
637                                          CompletionCallback* cb) {
638   ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
639   bool found = false;
640   if (i != active_entries_.end())
641     found = RemovePendingTransactionFromEntry(i->second, trans);
642 
643   if (found)
644     return;
645 
646   NewEntriesMap::const_iterator j = new_entries_.find(trans->key());
647   if (j != new_entries_.end())
648     found = RemovePendingCallbackFromNewEntry(j->second, cb);
649 
650   ActiveEntriesSet::iterator k = doomed_entries_.begin();
651   for (; k != doomed_entries_.end() && !found; ++k)
652     found = RemovePendingTransactionFromEntry(*k, trans);
653 
654   DCHECK(found) << "Pending transaction not found";
655 }
656 
RemovePendingTransactionFromEntry(ActiveEntry * entry,Transaction * trans)657 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
658                                                   Transaction* trans) {
659   TransactionList& pending_queue = entry->pending_queue;
660 
661   TransactionList::iterator j =
662       find(pending_queue.begin(), pending_queue.end(), trans);
663   if (j == pending_queue.end())
664     return false;
665 
666   pending_queue.erase(j);
667   return true;
668 }
669 
RemovePendingCallbackFromNewEntry(NewEntry * entry,CompletionCallback * cb)670 bool HttpCache::RemovePendingCallbackFromNewEntry(NewEntry* entry,
671                                                   CompletionCallback* cb) {
672   if (entry->writer->Matches(cb)) {
673     entry->writer->ClearCallback();
674     entry->writer->ClearEntry();
675     return true;
676   }
677   WorkItemList& pending_queue = entry->pending_queue;
678 
679   WorkItemList::iterator it = pending_queue.begin();
680   for (; it != pending_queue.end(); ++it) {
681     if ((*it)->Matches(cb)) {
682       delete *it;
683       pending_queue.erase(it);
684       return true;
685     }
686   }
687   return false;
688 }
689 
ProcessPendingQueue(ActiveEntry * entry)690 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
691   // Multiple readers may finish with an entry at once, so we want to batch up
692   // calls to OnProcessPendingQueue.  This flag also tells us that we should
693   // not delete the entry before OnProcessPendingQueue runs.
694   if (entry->will_process_pending_queue)
695     return;
696   entry->will_process_pending_queue = true;
697 
698   MessageLoop::current()->PostTask(FROM_HERE,
699       task_factory_.NewRunnableMethod(&HttpCache::OnProcessPendingQueue,
700                                       entry));
701 }
702 
OnProcessPendingQueue(ActiveEntry * entry)703 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
704   entry->will_process_pending_queue = false;
705   DCHECK(!entry->writer);
706 
707   // If no one is interested in this entry, then we can de-activate it.
708   if (entry->pending_queue.empty()) {
709     if (entry->readers.empty())
710       DestroyEntry(entry);
711     return;
712   }
713 
714   // Promote next transaction from the pending queue.
715   Transaction* next = entry->pending_queue.front();
716   if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
717     return;  // Have to wait.
718 
719   entry->pending_queue.erase(entry->pending_queue.begin());
720 
721   AddTransactionToEntry(entry, next);
722 }
723 
OnIOComplete(int result,NewEntry * new_entry)724 void HttpCache::OnIOComplete(int result, NewEntry* new_entry) {
725   scoped_ptr<WorkItem> item(new_entry->writer);
726   WorkItemOperation op = item->operation();
727   bool fail_requests = false;
728 
729   ActiveEntry* entry = NULL;
730   std::string key;
731   if (result == OK) {
732     if (op == WI_DOOM_ENTRY) {
733       // Anything after a Doom has to be restarted.
734       fail_requests = true;
735     } else if (item->IsValid()) {
736       key = new_entry->disk_entry->GetKey();
737       entry = ActivateEntry(key, new_entry->disk_entry);
738     } else {
739       // The writer transaction is gone.
740       if (op == WI_CREATE_ENTRY)
741         new_entry->disk_entry->Doom();
742       new_entry->disk_entry->Close();
743       fail_requests = true;
744     }
745   }
746 
747   // We are about to notify a bunch of transactions, and they may decide to
748   // re-issue a request (or send a different one). If we don't delete new_entry,
749   // the new request will be appended to the end of the list, and we'll see it
750   // again from this point before it has a chance to complete (and we'll be
751   // messing out the request order). The down side is that if for some reason
752   // notifying request A ends up cancelling request B (for the same key), we
753   // won't find request B anywhere (because it would be in a local variable
754   // here) and that's bad. If there is a chance for that to happen, we'll have
755   // to move the callback used to be a CancelableCallback. By the way, for this
756   // to happen the action (to cancel B) has to be synchronous to the
757   // notification for request A.
758   WorkItemList pending_items;
759   pending_items.swap(new_entry->pending_queue);
760   DeleteNewEntry(new_entry);
761 
762   item->NotifyTransaction(result, entry);
763 
764   while (!pending_items.empty()) {
765     item.reset(pending_items.front());
766     pending_items.pop_front();
767 
768     if (item->operation() == WI_DOOM_ENTRY) {
769       // A queued doom request is always a race.
770       fail_requests = true;
771     } else if (result == OK) {
772       entry = FindActiveEntry(key);
773       if (!entry)
774         fail_requests = true;
775     }
776 
777     if (fail_requests) {
778       item->NotifyTransaction(ERR_CACHE_RACE, NULL);
779       continue;
780     }
781 
782     if (item->operation() == WI_CREATE_ENTRY) {
783       if (result == OK) {
784         // A second Create request, but the first request succeded.
785         item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
786       } else {
787         if (op != WI_CREATE_ENTRY) {
788           // Failed Open followed by a Create.
789           item->NotifyTransaction(ERR_CACHE_RACE, NULL);
790           fail_requests = true;
791         } else {
792           item->NotifyTransaction(result, entry);
793         }
794       }
795     } else {
796        if (op == WI_CREATE_ENTRY && result != OK) {
797         // Failed Create followed by an Open.
798         item->NotifyTransaction(ERR_CACHE_RACE, NULL);
799         fail_requests = true;
800       } else {
801         item->NotifyTransaction(result, entry);
802       }
803     }
804   }
805 }
806 
CloseCurrentConnections()807 void HttpCache::CloseCurrentConnections() {
808   net::HttpNetworkLayer* network =
809       static_cast<net::HttpNetworkLayer*>(network_layer_.get());
810   HttpNetworkSession* session = network->GetSession();
811   if (session) {
812     session->tcp_socket_pool()->CloseIdleSockets();
813     if (session->flip_session_pool())
814       session->flip_session_pool()->CloseAllSessions();
815     session->ReplaceTCPSocketPool();
816   }
817 }
818 
819 //-----------------------------------------------------------------------------
820 
821 }  // namespace net
822