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