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