- add sources.
[platform/framework/web/crosswalk.git] / src / net / http / http_cache.cc
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
44 namespace {
45
46 // Adaptor to delete a file on a worker thread.
47 void DeletePath(base::FilePath path) {
48   base::DeleteFile(path, false);
49 }
50
51 }  // namespace
52
53 namespace net {
54
55 HttpCache::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
67 HttpCache::DefaultBackend::~DefaultBackend() {}
68
69 // static
70 HttpCache::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
75 int 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
92 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
93     : disk_entry(entry),
94       writer(NULL),
95       will_process_pending_queue(false),
96       doomed(false) {
97 }
98
99 HttpCache::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.
110 struct 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.
124 enum 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.
133 class 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.
189 class 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
217 void 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
240 void 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
258 void HttpCache::MetadataWriter::SelfDestroy() {
259   delete this;
260 }
261
262 void HttpCache::MetadataWriter::OnIOComplete(int result) {
263   if (!verified_)
264     return VerifyResponse(result);
265   SelfDestroy();
266 }
267
268 //-----------------------------------------------------------------------------
269
270 HttpCache::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
280 HttpCache::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
289 HttpCache::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
299 HttpCache::~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
343 int 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
355 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
356   return disk_cache_.get();
357 }
358
359 // static
360 bool 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
367 void 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
389 void 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
397 void 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
405 void 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
417 void 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
423 int 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
436 HttpCache* HttpCache::GetCache() {
437   return this;
438 }
439
440 HttpNetworkSession* HttpCache::GetSession() {
441   net::HttpNetworkLayer* network =
442       static_cast<net::HttpNetworkLayer*>(network_layer_.get());
443   return network->GetSession();
444 }
445
446 //-----------------------------------------------------------------------------
447
448 int 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
483 int 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.
499 std::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
538 void 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
549 int 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
574 int 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
597 void 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
614 void 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
627 HttpCache::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
632 HttpCache::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
640 void 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.
661 void 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
672 HttpCache::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
684 void 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
707 int 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
738 int 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
767 void HttpCache::DestroyEntry(ActiveEntry* entry) {
768   if (entry->doomed) {
769     FinalizeDoomedEntry(entry);
770   } else {
771     DeactivateEntry(entry);
772   }
773 }
774
775 int 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
814 void 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
841 void 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
868 void 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
880 void 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
893 LoadState 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
906 void 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
938 bool 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
951 bool 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
971 void 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
984 void 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
1008 void 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
1098 void 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
1110 void 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