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