Lots of random cleanups, mostly for native_theme_win.cc:
[chromium-blink-merge.git] / net / http / http_cache.cc
blobc45ab4118ca0ce849114a98eb6e6cf2d0af6eda1
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.
5 #include "net/http/http_cache.h"
7 #include <algorithm>
9 #include "base/compiler_specific.h"
11 #if defined(OS_POSIX)
12 #include <unistd.h>
13 #endif
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_based_cert_cache.h"
37 #include "net/http/disk_cache_based_quic_server_info.h"
38 #include "net/http/http_cache_transaction.h"
39 #include "net/http/http_network_layer.h"
40 #include "net/http/http_network_session.h"
41 #include "net/http/http_request_info.h"
42 #include "net/http/http_response_headers.h"
43 #include "net/http/http_response_info.h"
44 #include "net/http/http_util.h"
45 #include "net/quic/crypto/quic_server_info.h"
47 namespace {
49 bool UseCertCache() {
50 return base::FieldTrialList::FindFullName("CertCacheTrial") ==
51 "ExperimentGroup";
54 // Adaptor to delete a file on a worker thread.
55 void DeletePath(base::FilePath path) {
56 base::DeleteFile(path, false);
59 } // namespace
61 namespace net {
63 HttpCache::DefaultBackend::DefaultBackend(CacheType type,
64 BackendType backend_type,
65 const base::FilePath& path,
66 int max_bytes,
67 base::MessageLoopProxy* thread)
68 : type_(type),
69 backend_type_(backend_type),
70 path_(path),
71 max_bytes_(max_bytes),
72 thread_(thread) {
75 HttpCache::DefaultBackend::~DefaultBackend() {}
77 // static
78 HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) {
79 return new DefaultBackend(MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT,
80 base::FilePath(), max_bytes, NULL);
83 int HttpCache::DefaultBackend::CreateBackend(
84 NetLog* net_log, scoped_ptr<disk_cache::Backend>* backend,
85 const CompletionCallback& callback) {
86 DCHECK_GE(max_bytes_, 0);
87 return disk_cache::CreateCacheBackend(type_,
88 backend_type_,
89 path_,
90 max_bytes_,
91 true,
92 thread_.get(),
93 net_log,
94 backend,
95 callback);
98 //-----------------------------------------------------------------------------
100 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
101 : disk_entry(entry),
102 writer(NULL),
103 will_process_pending_queue(false),
104 doomed(false) {
107 HttpCache::ActiveEntry::~ActiveEntry() {
108 if (disk_entry) {
109 disk_entry->Close();
110 disk_entry = NULL;
114 //-----------------------------------------------------------------------------
116 // This structure keeps track of work items that are attempting to create or
117 // open cache entries or the backend itself.
118 struct HttpCache::PendingOp {
119 PendingOp() : disk_entry(NULL), writer(NULL) {}
120 ~PendingOp() {}
122 disk_cache::Entry* disk_entry;
123 scoped_ptr<disk_cache::Backend> backend;
124 WorkItem* writer;
125 CompletionCallback callback; // BackendCallback.
126 WorkItemList pending_queue;
129 //-----------------------------------------------------------------------------
131 // The type of operation represented by a work item.
132 enum WorkItemOperation {
133 WI_CREATE_BACKEND,
134 WI_OPEN_ENTRY,
135 WI_CREATE_ENTRY,
136 WI_DOOM_ENTRY
139 // A work item encapsulates a single request to the backend with all the
140 // information needed to complete that request.
141 class HttpCache::WorkItem {
142 public:
143 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
144 : operation_(operation),
145 trans_(trans),
146 entry_(entry),
147 backend_(NULL) {}
148 WorkItem(WorkItemOperation operation, Transaction* trans,
149 const net::CompletionCallback& cb, disk_cache::Backend** backend)
150 : operation_(operation),
151 trans_(trans),
152 entry_(NULL),
153 callback_(cb),
154 backend_(backend) {}
155 ~WorkItem() {}
157 // Calls back the transaction with the result of the operation.
158 void NotifyTransaction(int result, ActiveEntry* entry) {
159 DCHECK(!entry || entry->disk_entry);
160 if (entry_)
161 *entry_ = entry;
162 if (trans_)
163 trans_->io_callback().Run(result);
166 // Notifies the caller about the operation completion. Returns true if the
167 // callback was invoked.
168 bool DoCallback(int result, disk_cache::Backend* backend) {
169 if (backend_)
170 *backend_ = backend;
171 if (!callback_.is_null()) {
172 callback_.Run(result);
173 return true;
175 return false;
178 WorkItemOperation operation() { return operation_; }
179 void ClearTransaction() { trans_ = NULL; }
180 void ClearEntry() { entry_ = NULL; }
181 void ClearCallback() { callback_.Reset(); }
182 bool Matches(Transaction* trans) const { return trans == trans_; }
183 bool IsValid() const { return trans_ || entry_ || !callback_.is_null(); }
185 private:
186 WorkItemOperation operation_;
187 Transaction* trans_;
188 ActiveEntry** entry_;
189 net::CompletionCallback callback_; // User callback.
190 disk_cache::Backend** backend_;
193 //-----------------------------------------------------------------------------
195 // This class encapsulates a transaction whose only purpose is to write metadata
196 // to a given entry.
197 class HttpCache::MetadataWriter {
198 public:
199 explicit MetadataWriter(HttpCache::Transaction* trans)
200 : transaction_(trans),
201 verified_(false),
202 buf_len_(0) {
205 ~MetadataWriter() {}
207 // Implements the bulk of HttpCache::WriteMetadata.
208 void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf,
209 int buf_len);
211 private:
212 void VerifyResponse(int result);
213 void SelfDestroy();
214 void OnIOComplete(int result);
216 scoped_ptr<HttpCache::Transaction> transaction_;
217 bool verified_;
218 scoped_refptr<IOBuffer> buf_;
219 int buf_len_;
220 base::Time expected_response_time_;
221 HttpRequestInfo request_info_;
222 DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
225 void HttpCache::MetadataWriter::Write(const GURL& url,
226 base::Time expected_response_time,
227 IOBuffer* buf, int buf_len) {
228 DCHECK_GT(buf_len, 0);
229 DCHECK(buf);
230 DCHECK(buf->data());
231 request_info_.url = url;
232 request_info_.method = "GET";
233 request_info_.load_flags = LOAD_ONLY_FROM_CACHE;
235 expected_response_time_ = expected_response_time;
236 buf_ = buf;
237 buf_len_ = buf_len;
238 verified_ = false;
240 int rv = transaction_->Start(
241 &request_info_,
242 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)),
243 BoundNetLog());
244 if (rv != ERR_IO_PENDING)
245 VerifyResponse(rv);
248 void HttpCache::MetadataWriter::VerifyResponse(int result) {
249 verified_ = true;
250 if (result != OK)
251 return SelfDestroy();
253 const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
254 DCHECK(response_info->was_cached);
255 if (response_info->response_time != expected_response_time_)
256 return SelfDestroy();
258 result = transaction_->WriteMetadata(
259 buf_.get(),
260 buf_len_,
261 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)));
262 if (result != ERR_IO_PENDING)
263 SelfDestroy();
266 void HttpCache::MetadataWriter::SelfDestroy() {
267 delete this;
270 void HttpCache::MetadataWriter::OnIOComplete(int result) {
271 if (!verified_)
272 return VerifyResponse(result);
273 SelfDestroy();
276 //-----------------------------------------------------------------------------
278 class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory {
279 public:
280 QuicServerInfoFactoryAdaptor(HttpCache* http_cache)
281 : http_cache_(http_cache) {
284 virtual QuicServerInfo* GetForServer(
285 const QuicServerId& server_id) OVERRIDE {
286 return new DiskCacheBasedQuicServerInfo(server_id, http_cache_);
289 private:
290 HttpCache* const http_cache_;
293 //-----------------------------------------------------------------------------
294 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params,
295 BackendFactory* backend_factory)
296 : net_log_(params.net_log),
297 backend_factory_(backend_factory),
298 building_backend_(false),
299 bypass_lock_for_test_(false),
300 mode_(NORMAL),
301 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))),
302 weak_factory_(this) {
303 SetupQuicServerInfoFactory(network_layer_->GetSession());
307 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
308 // |session| is shared.
309 HttpCache::HttpCache(HttpNetworkSession* session,
310 BackendFactory* backend_factory)
311 : net_log_(session->net_log()),
312 backend_factory_(backend_factory),
313 building_backend_(false),
314 bypass_lock_for_test_(false),
315 mode_(NORMAL),
316 network_layer_(new HttpNetworkLayer(session)),
317 weak_factory_(this) {
320 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
321 NetLog* net_log,
322 BackendFactory* backend_factory)
323 : net_log_(net_log),
324 backend_factory_(backend_factory),
325 building_backend_(false),
326 bypass_lock_for_test_(false),
327 mode_(NORMAL),
328 network_layer_(network_layer),
329 weak_factory_(this) {
330 SetupQuicServerInfoFactory(network_layer_->GetSession());
333 HttpCache::~HttpCache() {
334 // Transactions should see an invalid cache after this point; otherwise they
335 // could see an inconsistent object (half destroyed).
336 weak_factory_.InvalidateWeakPtrs();
338 // If we have any active entries remaining, then we need to deactivate them.
339 // We may have some pending calls to OnProcessPendingQueue, but since those
340 // won't run (due to our destruction), we can simply ignore the corresponding
341 // will_process_pending_queue flag.
342 while (!active_entries_.empty()) {
343 ActiveEntry* entry = active_entries_.begin()->second;
344 entry->will_process_pending_queue = false;
345 entry->pending_queue.clear();
346 entry->readers.clear();
347 entry->writer = NULL;
348 DeactivateEntry(entry);
351 STLDeleteElements(&doomed_entries_);
353 // Before deleting pending_ops_, we have to make sure that the disk cache is
354 // done with said operations, or it will attempt to use deleted data.
355 cert_cache_.reset();
356 disk_cache_.reset();
358 PendingOpsMap::iterator pending_it = pending_ops_.begin();
359 for (; pending_it != pending_ops_.end(); ++pending_it) {
360 // We are not notifying the transactions about the cache going away, even
361 // though they are waiting for a callback that will never fire.
362 PendingOp* pending_op = pending_it->second;
363 delete pending_op->writer;
364 bool delete_pending_op = true;
365 if (building_backend_) {
366 // If we don't have a backend, when its construction finishes it will
367 // deliver the callbacks.
368 if (!pending_op->callback.is_null()) {
369 // If not null, the callback will delete the pending operation later.
370 delete_pending_op = false;
372 } else {
373 pending_op->callback.Reset();
376 STLDeleteElements(&pending_op->pending_queue);
377 if (delete_pending_op)
378 delete pending_op;
382 int HttpCache::GetBackend(disk_cache::Backend** backend,
383 const CompletionCallback& callback) {
384 DCHECK(!callback.is_null());
386 if (disk_cache_.get()) {
387 *backend = disk_cache_.get();
388 return OK;
391 return CreateBackend(backend, callback);
394 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
395 return disk_cache_.get();
398 // static
399 bool HttpCache::ParseResponseInfo(const char* data, int len,
400 HttpResponseInfo* response_info,
401 bool* response_truncated) {
402 Pickle pickle(data, len);
403 return response_info->InitFromPickle(pickle, response_truncated);
406 void HttpCache::WriteMetadata(const GURL& url,
407 RequestPriority priority,
408 base::Time expected_response_time,
409 IOBuffer* buf,
410 int buf_len) {
411 if (!buf_len)
412 return;
414 // Do lazy initialization of disk cache if needed.
415 if (!disk_cache_.get()) {
416 // We don't care about the result.
417 CreateBackend(NULL, net::CompletionCallback());
420 HttpCache::Transaction* trans =
421 new HttpCache::Transaction(priority, this);
422 MetadataWriter* writer = new MetadataWriter(trans);
424 // The writer will self destruct when done.
425 writer->Write(url, expected_response_time, buf, buf_len);
428 void HttpCache::CloseAllConnections() {
429 HttpNetworkSession* session = GetSession();
430 if (session)
431 session->CloseAllConnections();
434 void HttpCache::CloseIdleConnections() {
435 HttpNetworkSession* session = GetSession();
436 if (session)
437 session->CloseIdleConnections();
440 void HttpCache::OnExternalCacheHit(const GURL& url,
441 const std::string& http_method) {
442 if (!disk_cache_.get())
443 return;
445 HttpRequestInfo request_info;
446 request_info.url = url;
447 request_info.method = http_method;
448 std::string key = GenerateCacheKey(&request_info);
449 disk_cache_->OnExternalCacheHit(key);
452 void HttpCache::InitializeInfiniteCache(const base::FilePath& path) {
453 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
454 return;
455 base::WorkerPool::PostTask(FROM_HERE, base::Bind(&DeletePath, path), true);
458 int HttpCache::CreateTransaction(RequestPriority priority,
459 scoped_ptr<HttpTransaction>* trans) {
460 // Do lazy initialization of disk cache if needed.
461 if (!disk_cache_.get()) {
462 // We don't care about the result.
463 CreateBackend(NULL, net::CompletionCallback());
466 HttpCache::Transaction* transaction =
467 new HttpCache::Transaction(priority, this);
468 if (bypass_lock_for_test_)
469 transaction->BypassLockForTest();
471 trans->reset(transaction);
472 return OK;
475 HttpCache* HttpCache::GetCache() {
476 return this;
479 HttpNetworkSession* HttpCache::GetSession() {
480 return network_layer_->GetSession();
483 scoped_ptr<HttpTransactionFactory>
484 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
485 scoped_ptr<HttpTransactionFactory> new_network_layer) {
486 scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass());
487 network_layer_ = new_network_layer.Pass();
488 return old_network_layer.Pass();
491 //-----------------------------------------------------------------------------
493 int HttpCache::CreateBackend(disk_cache::Backend** backend,
494 const net::CompletionCallback& callback) {
495 if (!backend_factory_.get())
496 return ERR_FAILED;
498 building_backend_ = true;
500 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
501 backend));
503 // This is the only operation that we can do that is not related to any given
504 // entry, so we use an empty key for it.
505 PendingOp* pending_op = GetPendingOp(std::string());
506 if (pending_op->writer) {
507 if (!callback.is_null())
508 pending_op->pending_queue.push_back(item.release());
509 return ERR_IO_PENDING;
512 DCHECK(pending_op->pending_queue.empty());
514 pending_op->writer = item.release();
515 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
516 GetWeakPtr(), pending_op);
518 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
519 pending_op->callback);
520 if (rv != ERR_IO_PENDING) {
521 pending_op->writer->ClearCallback();
522 pending_op->callback.Run(rv);
525 return rv;
528 int HttpCache::GetBackendForTransaction(Transaction* trans) {
529 if (disk_cache_.get())
530 return OK;
532 if (!building_backend_)
533 return ERR_FAILED;
535 WorkItem* item = new WorkItem(
536 WI_CREATE_BACKEND, trans, net::CompletionCallback(), NULL);
537 PendingOp* pending_op = GetPendingOp(std::string());
538 DCHECK(pending_op->writer);
539 pending_op->pending_queue.push_back(item);
540 return ERR_IO_PENDING;
543 // Generate a key that can be used inside the cache.
544 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
545 // Strip out the reference, username, and password sections of the URL.
546 std::string url = HttpUtil::SpecForRequest(request->url);
548 DCHECK(mode_ != DISABLE);
549 if (mode_ == NORMAL) {
550 // No valid URL can begin with numerals, so we should not have to worry
551 // about collisions with normal URLs.
552 if (request->upload_data_stream &&
553 request->upload_data_stream->identifier()) {
554 url.insert(0, base::StringPrintf(
555 "%" PRId64 "/", request->upload_data_stream->identifier()));
557 return url;
560 // In playback and record mode, we cache everything.
562 // Lazily initialize.
563 if (playback_cache_map_ == NULL)
564 playback_cache_map_.reset(new PlaybackCacheMap());
566 // Each time we request an item from the cache, we tag it with a
567 // generation number. During playback, multiple fetches for the same
568 // item will use the same generation number and pull the proper
569 // instance of an URL from the cache.
570 int generation = 0;
571 DCHECK(playback_cache_map_ != NULL);
572 if (playback_cache_map_->find(url) != playback_cache_map_->end())
573 generation = (*playback_cache_map_)[url];
574 (*playback_cache_map_)[url] = generation + 1;
576 // The key into the cache is GENERATION # + METHOD + URL.
577 std::string result = base::IntToString(generation);
578 result.append(request->method);
579 result.append(url);
580 return result;
583 void HttpCache::DoomActiveEntry(const std::string& key) {
584 ActiveEntriesMap::iterator it = active_entries_.find(key);
585 if (it == active_entries_.end())
586 return;
588 // This is not a performance critical operation, this is handling an error
589 // condition so it is OK to look up the entry again.
590 int rv = DoomEntry(key, NULL);
591 DCHECK_EQ(OK, rv);
594 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
595 // Need to abandon the ActiveEntry, but any transaction attached to the entry
596 // should not be impacted. Dooming an entry only means that it will no
597 // longer be returned by FindActiveEntry (and it will also be destroyed once
598 // all consumers are finished with the entry).
599 ActiveEntriesMap::iterator it = active_entries_.find(key);
600 if (it == active_entries_.end()) {
601 DCHECK(trans);
602 return AsyncDoomEntry(key, trans);
605 ActiveEntry* entry = it->second;
606 active_entries_.erase(it);
608 // We keep track of doomed entries so that we can ensure that they are
609 // cleaned up properly when the cache is destroyed.
610 doomed_entries_.insert(entry);
612 entry->disk_entry->Doom();
613 entry->doomed = true;
615 DCHECK(entry->writer || !entry->readers.empty());
616 return OK;
619 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
620 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
621 PendingOp* pending_op = GetPendingOp(key);
622 if (pending_op->writer) {
623 pending_op->pending_queue.push_back(item);
624 return ERR_IO_PENDING;
627 DCHECK(pending_op->pending_queue.empty());
629 pending_op->writer = item;
630 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
631 GetWeakPtr(), pending_op);
633 int rv = disk_cache_->DoomEntry(key, pending_op->callback);
634 if (rv != ERR_IO_PENDING) {
635 item->ClearTransaction();
636 pending_op->callback.Run(rv);
639 return rv;
642 void HttpCache::DoomMainEntryForUrl(const GURL& url) {
643 if (!disk_cache_)
644 return;
646 HttpRequestInfo temp_info;
647 temp_info.url = url;
648 temp_info.method = "GET";
649 std::string key = GenerateCacheKey(&temp_info);
651 // Defer to DoomEntry if there is an active entry, otherwise call
652 // AsyncDoomEntry without triggering a callback.
653 if (active_entries_.count(key))
654 DoomEntry(key, NULL);
655 else
656 AsyncDoomEntry(key, NULL);
659 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
660 DCHECK(entry->doomed);
661 DCHECK(!entry->writer);
662 DCHECK(entry->readers.empty());
663 DCHECK(entry->pending_queue.empty());
665 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
666 DCHECK(it != doomed_entries_.end());
667 doomed_entries_.erase(it);
669 delete entry;
672 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
673 ActiveEntriesMap::const_iterator it = active_entries_.find(key);
674 return it != active_entries_.end() ? it->second : NULL;
677 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
678 disk_cache::Entry* disk_entry) {
679 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
680 ActiveEntry* entry = new ActiveEntry(disk_entry);
681 active_entries_[disk_entry->GetKey()] = entry;
682 return entry;
685 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
686 DCHECK(!entry->will_process_pending_queue);
687 DCHECK(!entry->doomed);
688 DCHECK(!entry->writer);
689 DCHECK(entry->disk_entry);
690 DCHECK(entry->readers.empty());
691 DCHECK(entry->pending_queue.empty());
693 std::string key = entry->disk_entry->GetKey();
694 if (key.empty())
695 return SlowDeactivateEntry(entry);
697 ActiveEntriesMap::iterator it = active_entries_.find(key);
698 DCHECK(it != active_entries_.end());
699 DCHECK(it->second == entry);
701 active_entries_.erase(it);
702 delete entry;
705 // We don't know this entry's key so we have to find it without it.
706 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
707 for (ActiveEntriesMap::iterator it = active_entries_.begin();
708 it != active_entries_.end(); ++it) {
709 if (it->second == entry) {
710 active_entries_.erase(it);
711 delete entry;
712 break;
717 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
718 DCHECK(!FindActiveEntry(key));
720 PendingOpsMap::const_iterator it = pending_ops_.find(key);
721 if (it != pending_ops_.end())
722 return it->second;
724 PendingOp* operation = new PendingOp();
725 pending_ops_[key] = operation;
726 return operation;
729 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
730 std::string key;
731 if (pending_op->disk_entry)
732 key = pending_op->disk_entry->GetKey();
734 if (!key.empty()) {
735 PendingOpsMap::iterator it = pending_ops_.find(key);
736 DCHECK(it != pending_ops_.end());
737 pending_ops_.erase(it);
738 } else {
739 for (PendingOpsMap::iterator it = pending_ops_.begin();
740 it != pending_ops_.end(); ++it) {
741 if (it->second == pending_op) {
742 pending_ops_.erase(it);
743 break;
747 DCHECK(pending_op->pending_queue.empty());
749 delete pending_op;
752 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
753 Transaction* trans) {
754 ActiveEntry* active_entry = FindActiveEntry(key);
755 if (active_entry) {
756 *entry = active_entry;
757 return OK;
760 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
761 PendingOp* pending_op = GetPendingOp(key);
762 if (pending_op->writer) {
763 pending_op->pending_queue.push_back(item);
764 return ERR_IO_PENDING;
767 DCHECK(pending_op->pending_queue.empty());
769 pending_op->writer = item;
770 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
771 GetWeakPtr(), pending_op);
773 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry),
774 pending_op->callback);
775 if (rv != ERR_IO_PENDING) {
776 item->ClearTransaction();
777 pending_op->callback.Run(rv);
780 return rv;
783 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
784 Transaction* trans) {
785 if (FindActiveEntry(key)) {
786 return ERR_CACHE_RACE;
789 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
790 PendingOp* pending_op = GetPendingOp(key);
791 if (pending_op->writer) {
792 pending_op->pending_queue.push_back(item);
793 return ERR_IO_PENDING;
796 DCHECK(pending_op->pending_queue.empty());
798 pending_op->writer = item;
799 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
800 GetWeakPtr(), pending_op);
802 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
803 pending_op->callback);
804 if (rv != ERR_IO_PENDING) {
805 item->ClearTransaction();
806 pending_op->callback.Run(rv);
809 return rv;
812 void HttpCache::DestroyEntry(ActiveEntry* entry) {
813 if (entry->doomed) {
814 FinalizeDoomedEntry(entry);
815 } else {
816 DeactivateEntry(entry);
820 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
821 DCHECK(entry);
822 DCHECK(entry->disk_entry);
824 // We implement a basic reader/writer lock for the disk cache entry. If
825 // there is already a writer, then everyone has to wait for the writer to
826 // finish before they can access the cache entry. There can be multiple
827 // readers.
829 // NOTE: If the transaction can only write, then the entry should not be in
830 // use (since any existing entry should have already been doomed).
832 if (entry->writer || entry->will_process_pending_queue) {
833 entry->pending_queue.push_back(trans);
834 return ERR_IO_PENDING;
837 if (trans->mode() & Transaction::WRITE) {
838 // transaction needs exclusive access to the entry
839 if (entry->readers.empty()) {
840 entry->writer = trans;
841 } else {
842 entry->pending_queue.push_back(trans);
843 return ERR_IO_PENDING;
845 } else {
846 // transaction needs read access to the entry
847 entry->readers.push_back(trans);
850 // We do this before calling EntryAvailable to force any further calls to
851 // AddTransactionToEntry to add their transaction to the pending queue, which
852 // ensures FIFO ordering.
853 if (!entry->writer && !entry->pending_queue.empty())
854 ProcessPendingQueue(entry);
856 return OK;
859 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
860 bool cancel) {
861 // If we already posted a task to move on to the next transaction and this was
862 // the writer, there is nothing to cancel.
863 if (entry->will_process_pending_queue && entry->readers.empty())
864 return;
866 if (entry->writer) {
867 DCHECK(trans == entry->writer);
869 // Assume there was a failure.
870 bool success = false;
871 if (cancel) {
872 DCHECK(entry->disk_entry);
873 // This is a successful operation in the sense that we want to keep the
874 // entry.
875 success = trans->AddTruncatedFlag();
876 // The previous operation may have deleted the entry.
877 if (!trans->entry())
878 return;
880 DoneWritingToEntry(entry, success);
881 } else {
882 DoneReadingFromEntry(entry, trans);
886 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
887 DCHECK(entry->readers.empty());
889 entry->writer = NULL;
891 if (success) {
892 ProcessPendingQueue(entry);
893 } else {
894 DCHECK(!entry->will_process_pending_queue);
896 // We failed to create this entry.
897 TransactionList pending_queue;
898 pending_queue.swap(entry->pending_queue);
900 entry->disk_entry->Doom();
901 DestroyEntry(entry);
903 // We need to do something about these pending entries, which now need to
904 // be added to a new entry.
905 while (!pending_queue.empty()) {
906 // ERR_CACHE_RACE causes the transaction to restart the whole process.
907 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
908 pending_queue.pop_front();
913 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
914 DCHECK(!entry->writer);
916 TransactionList::iterator it =
917 std::find(entry->readers.begin(), entry->readers.end(), trans);
918 DCHECK(it != entry->readers.end());
920 entry->readers.erase(it);
922 ProcessPendingQueue(entry);
925 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
926 DCHECK(entry->writer);
927 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
928 DCHECK(entry->readers.empty());
930 Transaction* trans = entry->writer;
932 entry->writer = NULL;
933 entry->readers.push_back(trans);
935 ProcessPendingQueue(entry);
938 LoadState HttpCache::GetLoadStateForPendingTransaction(
939 const Transaction* trans) {
940 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
941 if (i == active_entries_.end()) {
942 // If this is really a pending transaction, and it is not part of
943 // active_entries_, we should be creating the backend or the entry.
944 return LOAD_STATE_WAITING_FOR_CACHE;
947 Transaction* writer = i->second->writer;
948 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
951 void HttpCache::RemovePendingTransaction(Transaction* trans) {
952 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
953 bool found = false;
954 if (i != active_entries_.end())
955 found = RemovePendingTransactionFromEntry(i->second, trans);
957 if (found)
958 return;
960 if (building_backend_) {
961 PendingOpsMap::const_iterator j = pending_ops_.find(std::string());
962 if (j != pending_ops_.end())
963 found = RemovePendingTransactionFromPendingOp(j->second, trans);
965 if (found)
966 return;
969 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
970 if (j != pending_ops_.end())
971 found = RemovePendingTransactionFromPendingOp(j->second, trans);
973 if (found)
974 return;
976 ActiveEntriesSet::iterator k = doomed_entries_.begin();
977 for (; k != doomed_entries_.end() && !found; ++k)
978 found = RemovePendingTransactionFromEntry(*k, trans);
980 DCHECK(found) << "Pending transaction not found";
983 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
984 Transaction* trans) {
985 TransactionList& pending_queue = entry->pending_queue;
987 TransactionList::iterator j =
988 find(pending_queue.begin(), pending_queue.end(), trans);
989 if (j == pending_queue.end())
990 return false;
992 pending_queue.erase(j);
993 return true;
996 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
997 Transaction* trans) {
998 if (pending_op->writer->Matches(trans)) {
999 pending_op->writer->ClearTransaction();
1000 pending_op->writer->ClearEntry();
1001 return true;
1003 WorkItemList& pending_queue = pending_op->pending_queue;
1005 WorkItemList::iterator it = pending_queue.begin();
1006 for (; it != pending_queue.end(); ++it) {
1007 if ((*it)->Matches(trans)) {
1008 delete *it;
1009 pending_queue.erase(it);
1010 return true;
1013 return false;
1016 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) {
1017 if (session && session->params().enable_quic_persist_server_info &&
1018 !session->quic_stream_factory()->has_quic_server_info_factory()) {
1019 DCHECK(!quic_server_info_factory_);
1020 quic_server_info_factory_.reset(new QuicServerInfoFactoryAdaptor(this));
1021 session->quic_stream_factory()->set_quic_server_info_factory(
1022 quic_server_info_factory_.get());
1026 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
1027 // Multiple readers may finish with an entry at once, so we want to batch up
1028 // calls to OnProcessPendingQueue. This flag also tells us that we should
1029 // not delete the entry before OnProcessPendingQueue runs.
1030 if (entry->will_process_pending_queue)
1031 return;
1032 entry->will_process_pending_queue = true;
1034 base::MessageLoop::current()->PostTask(
1035 FROM_HERE,
1036 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1039 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1040 entry->will_process_pending_queue = false;
1041 DCHECK(!entry->writer);
1043 // If no one is interested in this entry, then we can deactivate it.
1044 if (entry->pending_queue.empty()) {
1045 if (entry->readers.empty())
1046 DestroyEntry(entry);
1047 return;
1050 // Promote next transaction from the pending queue.
1051 Transaction* next = entry->pending_queue.front();
1052 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1053 return; // Have to wait.
1055 entry->pending_queue.erase(entry->pending_queue.begin());
1057 int rv = AddTransactionToEntry(entry, next);
1058 if (rv != ERR_IO_PENDING) {
1059 next->io_callback().Run(rv);
1063 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1064 WorkItemOperation op = pending_op->writer->operation();
1066 // Completing the creation of the backend is simpler than the other cases.
1067 if (op == WI_CREATE_BACKEND)
1068 return OnBackendCreated(result, pending_op);
1070 scoped_ptr<WorkItem> item(pending_op->writer);
1071 bool fail_requests = false;
1073 ActiveEntry* entry = NULL;
1074 std::string key;
1075 if (result == OK) {
1076 if (op == WI_DOOM_ENTRY) {
1077 // Anything after a Doom has to be restarted.
1078 fail_requests = true;
1079 } else if (item->IsValid()) {
1080 key = pending_op->disk_entry->GetKey();
1081 entry = ActivateEntry(pending_op->disk_entry);
1082 } else {
1083 // The writer transaction is gone.
1084 if (op == WI_CREATE_ENTRY)
1085 pending_op->disk_entry->Doom();
1086 pending_op->disk_entry->Close();
1087 pending_op->disk_entry = NULL;
1088 fail_requests = true;
1092 // We are about to notify a bunch of transactions, and they may decide to
1093 // re-issue a request (or send a different one). If we don't delete
1094 // pending_op, the new request will be appended to the end of the list, and
1095 // we'll see it again from this point before it has a chance to complete (and
1096 // we'll be messing out the request order). The down side is that if for some
1097 // reason notifying request A ends up cancelling request B (for the same key),
1098 // we won't find request B anywhere (because it would be in a local variable
1099 // here) and that's bad. If there is a chance for that to happen, we'll have
1100 // to move the callback used to be a CancelableCallback. By the way, for this
1101 // to happen the action (to cancel B) has to be synchronous to the
1102 // notification for request A.
1103 WorkItemList pending_items;
1104 pending_items.swap(pending_op->pending_queue);
1105 DeletePendingOp(pending_op);
1107 item->NotifyTransaction(result, entry);
1109 while (!pending_items.empty()) {
1110 item.reset(pending_items.front());
1111 pending_items.pop_front();
1113 if (item->operation() == WI_DOOM_ENTRY) {
1114 // A queued doom request is always a race.
1115 fail_requests = true;
1116 } else if (result == OK) {
1117 entry = FindActiveEntry(key);
1118 if (!entry)
1119 fail_requests = true;
1122 if (fail_requests) {
1123 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1124 continue;
1127 if (item->operation() == WI_CREATE_ENTRY) {
1128 if (result == OK) {
1129 // A second Create request, but the first request succeeded.
1130 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1131 } else {
1132 if (op != WI_CREATE_ENTRY) {
1133 // Failed Open followed by a Create.
1134 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1135 fail_requests = true;
1136 } else {
1137 item->NotifyTransaction(result, entry);
1140 } else {
1141 if (op == WI_CREATE_ENTRY && result != OK) {
1142 // Failed Create followed by an Open.
1143 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1144 fail_requests = true;
1145 } else {
1146 item->NotifyTransaction(result, entry);
1152 // static
1153 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
1154 PendingOp* pending_op,
1155 int rv) {
1156 if (cache.get()) {
1157 cache->OnIOComplete(rv, pending_op);
1158 } else {
1159 // The callback was cancelled so we should delete the pending_op that
1160 // was used with this callback.
1161 delete pending_op;
1165 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1166 scoped_ptr<WorkItem> item(pending_op->writer);
1167 WorkItemOperation op = item->operation();
1168 DCHECK_EQ(WI_CREATE_BACKEND, op);
1170 // We don't need the callback anymore.
1171 pending_op->callback.Reset();
1173 if (backend_factory_.get()) {
1174 // We may end up calling OnBackendCreated multiple times if we have pending
1175 // work items. The first call saves the backend and releases the factory,
1176 // and the last call clears building_backend_.
1177 backend_factory_.reset(); // Reclaim memory.
1178 if (result == OK) {
1179 disk_cache_ = pending_op->backend.Pass();
1180 if (UseCertCache())
1181 cert_cache_.reset(new DiskBasedCertCache(disk_cache_.get()));
1185 if (!pending_op->pending_queue.empty()) {
1186 WorkItem* pending_item = pending_op->pending_queue.front();
1187 pending_op->pending_queue.pop_front();
1188 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1190 // We want to process a single callback at a time, because the cache may
1191 // go away from the callback.
1192 pending_op->writer = pending_item;
1194 base::MessageLoop::current()->PostTask(
1195 FROM_HERE,
1196 base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(),
1197 result, pending_op));
1198 } else {
1199 building_backend_ = false;
1200 DeletePendingOp(pending_op);
1203 // The cache may be gone when we return from the callback.
1204 if (!item->DoCallback(result, disk_cache_.get()))
1205 item->NotifyTransaction(result, NULL);
1208 } // namespace net