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