Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / net / http / http_cache.cc
blob796aeea501414bdacb9efa9aecd625a6702a85aa
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/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/compiler_specific.h"
13 #include "base/files/file_util.h"
14 #include "base/format_macros.h"
15 #include "base/location.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/metrics/field_trial.h"
18 #include "base/metrics/histogram_macros.h"
19 #include "base/pickle.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/thread_task_runner_handle.h"
26 #include "base/threading/worker_pool.h"
27 #include "base/time/default_clock.h"
28 #include "base/time/time.h"
29 #include "net/base/cache_type.h"
30 #include "net/base/io_buffer.h"
31 #include "net/base/load_flags.h"
32 #include "net/base/net_errors.h"
33 #include "net/base/upload_data_stream.h"
34 #include "net/disk_cache/disk_cache.h"
35 #include "net/http/disk_based_cert_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"
46 #if defined(OS_POSIX)
47 #include <unistd.h>
48 #endif
50 namespace {
52 bool UseCertCache() {
53 return base::FieldTrialList::FindFullName("CertCacheTrial") ==
54 "ExperimentGroup";
57 } // namespace
59 namespace net {
61 HttpCache::DefaultBackend::DefaultBackend(
62 CacheType type,
63 BackendType backend_type,
64 const base::FilePath& path,
65 int max_bytes,
66 const scoped_refptr<base::SingleThreadTaskRunner>& thread)
67 : type_(type),
68 backend_type_(backend_type),
69 path_(path),
70 max_bytes_(max_bytes),
71 thread_(thread) {
74 HttpCache::DefaultBackend::~DefaultBackend() {}
76 // static
77 HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) {
78 return new DefaultBackend(MEMORY_CACHE, CACHE_BACKEND_DEFAULT,
79 base::FilePath(), max_bytes, NULL);
82 int HttpCache::DefaultBackend::CreateBackend(
83 NetLog* net_log, scoped_ptr<disk_cache::Backend>* backend,
84 const CompletionCallback& callback) {
85 DCHECK_GE(max_bytes_, 0);
86 return disk_cache::CreateCacheBackend(type_,
87 backend_type_,
88 path_,
89 max_bytes_,
90 true,
91 thread_,
92 net_log,
93 backend,
94 callback);
97 //-----------------------------------------------------------------------------
99 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
100 : disk_entry(entry),
101 writer(NULL),
102 will_process_pending_queue(false),
103 doomed(false) {
106 HttpCache::ActiveEntry::~ActiveEntry() {
107 if (disk_entry) {
108 disk_entry->Close();
109 disk_entry = NULL;
113 //-----------------------------------------------------------------------------
115 // This structure keeps track of work items that are attempting to create or
116 // open cache entries or the backend itself.
117 struct HttpCache::PendingOp {
118 PendingOp() : disk_entry(NULL), writer(NULL) {}
119 ~PendingOp() {}
121 disk_cache::Entry* disk_entry;
122 scoped_ptr<disk_cache::Backend> backend;
123 WorkItem* writer;
124 CompletionCallback callback; // BackendCallback.
125 WorkItemList pending_queue;
128 //-----------------------------------------------------------------------------
130 // The type of operation represented by a work item.
131 enum WorkItemOperation {
132 WI_CREATE_BACKEND,
133 WI_OPEN_ENTRY,
134 WI_CREATE_ENTRY,
135 WI_DOOM_ENTRY
138 // A work item encapsulates a single request to the backend with all the
139 // information needed to complete that request.
140 class HttpCache::WorkItem {
141 public:
142 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
143 : operation_(operation),
144 trans_(trans),
145 entry_(entry),
146 backend_(NULL) {}
147 WorkItem(WorkItemOperation operation,
148 Transaction* trans,
149 const CompletionCallback& cb,
150 disk_cache::Backend** backend)
151 : operation_(operation),
152 trans_(trans),
153 entry_(NULL),
154 callback_(cb),
155 backend_(backend) {}
156 ~WorkItem() {}
158 // Calls back the transaction with the result of the operation.
159 void NotifyTransaction(int result, ActiveEntry* entry) {
160 DCHECK(!entry || entry->disk_entry);
161 if (entry_)
162 *entry_ = entry;
163 if (trans_)
164 trans_->io_callback().Run(result);
167 // Notifies the caller about the operation completion. Returns true if the
168 // callback was invoked.
169 bool DoCallback(int result, disk_cache::Backend* backend) {
170 if (backend_)
171 *backend_ = backend;
172 if (!callback_.is_null()) {
173 callback_.Run(result);
174 return true;
176 return false;
179 WorkItemOperation operation() { return operation_; }
180 void ClearTransaction() { trans_ = NULL; }
181 void ClearEntry() { entry_ = NULL; }
182 void ClearCallback() { callback_.Reset(); }
183 bool Matches(Transaction* trans) const { return trans == trans_; }
184 bool IsValid() const { return trans_ || entry_ || !callback_.is_null(); }
186 private:
187 WorkItemOperation operation_;
188 Transaction* trans_;
189 ActiveEntry** entry_;
190 CompletionCallback callback_; // User callback.
191 disk_cache::Backend** backend_;
194 //-----------------------------------------------------------------------------
196 // This class encapsulates a transaction whose only purpose is to write metadata
197 // to a given entry.
198 class HttpCache::MetadataWriter {
199 public:
200 explicit MetadataWriter(HttpCache::Transaction* trans)
201 : transaction_(trans),
202 verified_(false),
203 buf_len_(0) {
206 ~MetadataWriter() {}
208 // Implements the bulk of HttpCache::WriteMetadata.
209 void Write(const GURL& url,
210 base::Time expected_response_time,
211 IOBuffer* buf,
212 int buf_len);
214 private:
215 void VerifyResponse(int result);
216 void SelfDestroy();
217 void OnIOComplete(int result);
219 scoped_ptr<HttpCache::Transaction> transaction_;
220 bool verified_;
221 scoped_refptr<IOBuffer> buf_;
222 int buf_len_;
223 base::Time expected_response_time_;
224 HttpRequestInfo request_info_;
225 DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
228 void HttpCache::MetadataWriter::Write(const GURL& url,
229 base::Time expected_response_time,
230 IOBuffer* buf,
231 int buf_len) {
232 DCHECK_GT(buf_len, 0);
233 DCHECK(buf);
234 DCHECK(buf->data());
235 request_info_.url = url;
236 request_info_.method = "GET";
237 request_info_.load_flags = LOAD_ONLY_FROM_CACHE;
239 expected_response_time_ = expected_response_time;
240 buf_ = buf;
241 buf_len_ = buf_len;
242 verified_ = false;
244 int rv = transaction_->Start(
245 &request_info_,
246 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)),
247 BoundNetLog());
248 if (rv != ERR_IO_PENDING)
249 VerifyResponse(rv);
252 void HttpCache::MetadataWriter::VerifyResponse(int result) {
253 verified_ = true;
254 if (result != OK)
255 return SelfDestroy();
257 const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
258 DCHECK(response_info->was_cached);
259 if (response_info->response_time != expected_response_time_)
260 return SelfDestroy();
262 result = transaction_->WriteMetadata(
263 buf_.get(),
264 buf_len_,
265 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)));
266 if (result != ERR_IO_PENDING)
267 SelfDestroy();
270 void HttpCache::MetadataWriter::SelfDestroy() {
271 delete this;
274 void HttpCache::MetadataWriter::OnIOComplete(int result) {
275 if (!verified_)
276 return VerifyResponse(result);
277 SelfDestroy();
280 //-----------------------------------------------------------------------------
282 class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory {
283 public:
284 explicit QuicServerInfoFactoryAdaptor(HttpCache* http_cache)
285 : http_cache_(http_cache) {
288 QuicServerInfo* GetForServer(const QuicServerId& server_id) override {
289 return new DiskCacheBasedQuicServerInfo(server_id, http_cache_);
292 private:
293 HttpCache* const http_cache_;
296 //-----------------------------------------------------------------------------
297 HttpCache::HttpCache(const HttpNetworkSession::Params& params,
298 BackendFactory* backend_factory)
299 : net_log_(params.net_log),
300 backend_factory_(backend_factory),
301 building_backend_(false),
302 bypass_lock_for_test_(false),
303 fail_conditionalization_for_test_(false),
304 mode_(NORMAL),
305 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))),
306 clock_(new base::DefaultClock()),
307 weak_factory_(this) {
308 SetupQuicServerInfoFactory(network_layer_->GetSession());
312 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
313 // |session| is shared.
314 HttpCache::HttpCache(HttpNetworkSession* session,
315 BackendFactory* backend_factory)
316 : net_log_(session->net_log()),
317 backend_factory_(backend_factory),
318 building_backend_(false),
319 bypass_lock_for_test_(false),
320 fail_conditionalization_for_test_(false),
321 mode_(NORMAL),
322 network_layer_(new HttpNetworkLayer(session)),
323 clock_(new base::DefaultClock()),
324 weak_factory_(this) {
327 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
328 NetLog* net_log,
329 BackendFactory* backend_factory)
330 : net_log_(net_log),
331 backend_factory_(backend_factory),
332 building_backend_(false),
333 bypass_lock_for_test_(false),
334 fail_conditionalization_for_test_(false),
335 mode_(NORMAL),
336 network_layer_(network_layer),
337 clock_(new base::DefaultClock()),
338 weak_factory_(this) {
339 SetupQuicServerInfoFactory(network_layer_->GetSession());
342 HttpCache::~HttpCache() {
343 // Transactions should see an invalid cache after this point; otherwise they
344 // could see an inconsistent object (half destroyed).
345 weak_factory_.InvalidateWeakPtrs();
347 // If we have any active entries remaining, then we need to deactivate them.
348 // We may have some pending calls to OnProcessPendingQueue, but since those
349 // won't run (due to our destruction), we can simply ignore the corresponding
350 // will_process_pending_queue flag.
351 while (!active_entries_.empty()) {
352 ActiveEntry* entry = active_entries_.begin()->second;
353 entry->will_process_pending_queue = false;
354 entry->pending_queue.clear();
355 entry->readers.clear();
356 entry->writer = NULL;
357 DeactivateEntry(entry);
360 STLDeleteElements(&doomed_entries_);
362 // Before deleting pending_ops_, we have to make sure that the disk cache is
363 // done with said operations, or it will attempt to use deleted data.
364 cert_cache_.reset();
365 disk_cache_.reset();
367 PendingOpsMap::iterator pending_it = pending_ops_.begin();
368 for (; pending_it != pending_ops_.end(); ++pending_it) {
369 // We are not notifying the transactions about the cache going away, even
370 // though they are waiting for a callback that will never fire.
371 PendingOp* pending_op = pending_it->second;
372 delete pending_op->writer;
373 bool delete_pending_op = true;
374 if (building_backend_) {
375 // If we don't have a backend, when its construction finishes it will
376 // deliver the callbacks.
377 if (!pending_op->callback.is_null()) {
378 // If not null, the callback will delete the pending operation later.
379 delete_pending_op = false;
381 } else {
382 pending_op->callback.Reset();
385 STLDeleteElements(&pending_op->pending_queue);
386 if (delete_pending_op)
387 delete pending_op;
391 int HttpCache::GetBackend(disk_cache::Backend** backend,
392 const CompletionCallback& callback) {
393 DCHECK(!callback.is_null());
395 if (disk_cache_.get()) {
396 *backend = disk_cache_.get();
397 return OK;
400 return CreateBackend(backend, callback);
403 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
404 return disk_cache_.get();
407 // static
408 bool HttpCache::ParseResponseInfo(const char* data, int len,
409 HttpResponseInfo* response_info,
410 bool* response_truncated) {
411 base::Pickle pickle(data, len);
412 return response_info->InitFromPickle(pickle, response_truncated);
415 void HttpCache::WriteMetadata(const GURL& url,
416 RequestPriority priority,
417 base::Time expected_response_time,
418 IOBuffer* buf,
419 int buf_len) {
420 if (!buf_len)
421 return;
423 // Do lazy initialization of disk cache if needed.
424 if (!disk_cache_.get()) {
425 // We don't care about the result.
426 CreateBackend(NULL, CompletionCallback());
429 HttpCache::Transaction* trans =
430 new HttpCache::Transaction(priority, this);
431 MetadataWriter* writer = new MetadataWriter(trans);
433 // The writer will self destruct when done.
434 writer->Write(url, expected_response_time, buf, buf_len);
437 void HttpCache::CloseAllConnections() {
438 HttpNetworkSession* session = GetSession();
439 if (session)
440 session->CloseAllConnections();
443 void HttpCache::CloseIdleConnections() {
444 HttpNetworkSession* session = GetSession();
445 if (session)
446 session->CloseIdleConnections();
449 void HttpCache::OnExternalCacheHit(const GURL& url,
450 const std::string& http_method) {
451 if (!disk_cache_.get() || mode_ == DISABLE)
452 return;
454 HttpRequestInfo request_info;
455 request_info.url = url;
456 request_info.method = http_method;
457 std::string key = GenerateCacheKey(&request_info);
458 disk_cache_->OnExternalCacheHit(key);
461 int HttpCache::CreateTransaction(RequestPriority priority,
462 scoped_ptr<HttpTransaction>* trans) {
463 // Do lazy initialization of disk cache if needed.
464 if (!disk_cache_.get()) {
465 // We don't care about the result.
466 CreateBackend(NULL, CompletionCallback());
469 HttpCache::Transaction* transaction =
470 new HttpCache::Transaction(priority, this);
471 if (bypass_lock_for_test_)
472 transaction->BypassLockForTest();
473 if (fail_conditionalization_for_test_)
474 transaction->FailConditionalizationForTest();
476 trans->reset(transaction);
477 return OK;
480 HttpCache* HttpCache::GetCache() {
481 return this;
484 HttpNetworkSession* HttpCache::GetSession() {
485 return network_layer_->GetSession();
488 scoped_ptr<HttpTransactionFactory>
489 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
490 scoped_ptr<HttpTransactionFactory> new_network_layer) {
491 scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass());
492 network_layer_ = new_network_layer.Pass();
493 return old_network_layer.Pass();
496 //-----------------------------------------------------------------------------
498 int HttpCache::CreateBackend(disk_cache::Backend** backend,
499 const CompletionCallback& callback) {
500 if (!backend_factory_.get())
501 return ERR_FAILED;
503 building_backend_ = true;
505 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
506 backend));
508 // This is the only operation that we can do that is not related to any given
509 // entry, so we use an empty key for it.
510 PendingOp* pending_op = GetPendingOp(std::string());
511 if (pending_op->writer) {
512 if (!callback.is_null())
513 pending_op->pending_queue.push_back(item.release());
514 return ERR_IO_PENDING;
517 DCHECK(pending_op->pending_queue.empty());
519 pending_op->writer = item.release();
520 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
521 GetWeakPtr(), pending_op);
523 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
524 pending_op->callback);
525 if (rv != ERR_IO_PENDING) {
526 pending_op->writer->ClearCallback();
527 pending_op->callback.Run(rv);
530 return rv;
533 int HttpCache::GetBackendForTransaction(Transaction* trans) {
534 if (disk_cache_.get())
535 return OK;
537 if (!building_backend_)
538 return ERR_FAILED;
540 WorkItem* item =
541 new WorkItem(WI_CREATE_BACKEND, trans, CompletionCallback(), NULL);
542 PendingOp* pending_op = GetPendingOp(std::string());
543 DCHECK(pending_op->writer);
544 pending_op->pending_queue.push_back(item);
545 return ERR_IO_PENDING;
548 // Generate a key that can be used inside the cache.
549 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
550 // Strip out the reference, username, and password sections of the URL.
551 std::string url = HttpUtil::SpecForRequest(request->url);
553 DCHECK_NE(DISABLE, mode_);
554 // No valid URL can begin with numerals, so we should not have to worry
555 // about collisions with normal URLs.
556 if (request->upload_data_stream &&
557 request->upload_data_stream->identifier()) {
558 url.insert(0,
559 base::StringPrintf("%" PRId64 "/",
560 request->upload_data_stream->identifier()));
562 return url;
565 void HttpCache::DoomActiveEntry(const std::string& key) {
566 ActiveEntriesMap::iterator it = active_entries_.find(key);
567 if (it == active_entries_.end())
568 return;
570 // This is not a performance critical operation, this is handling an error
571 // condition so it is OK to look up the entry again.
572 int rv = DoomEntry(key, NULL);
573 DCHECK_EQ(OK, rv);
576 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
577 // Need to abandon the ActiveEntry, but any transaction attached to the entry
578 // should not be impacted. Dooming an entry only means that it will no
579 // longer be returned by FindActiveEntry (and it will also be destroyed once
580 // all consumers are finished with the entry).
581 ActiveEntriesMap::iterator it = active_entries_.find(key);
582 if (it == active_entries_.end()) {
583 DCHECK(trans);
584 return AsyncDoomEntry(key, trans);
587 ActiveEntry* entry = it->second;
588 active_entries_.erase(it);
590 // We keep track of doomed entries so that we can ensure that they are
591 // cleaned up properly when the cache is destroyed.
592 doomed_entries_.insert(entry);
594 entry->disk_entry->Doom();
595 entry->doomed = true;
597 DCHECK(entry->writer || !entry->readers.empty() ||
598 entry->will_process_pending_queue);
599 return OK;
602 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
603 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
604 PendingOp* pending_op = GetPendingOp(key);
605 if (pending_op->writer) {
606 pending_op->pending_queue.push_back(item);
607 return ERR_IO_PENDING;
610 DCHECK(pending_op->pending_queue.empty());
612 pending_op->writer = item;
613 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
614 GetWeakPtr(), pending_op);
616 int rv = disk_cache_->DoomEntry(key, pending_op->callback);
617 if (rv != ERR_IO_PENDING) {
618 item->ClearTransaction();
619 pending_op->callback.Run(rv);
622 return rv;
625 void HttpCache::DoomMainEntryForUrl(const GURL& url) {
626 if (!disk_cache_)
627 return;
629 HttpRequestInfo temp_info;
630 temp_info.url = url;
631 temp_info.method = "GET";
632 std::string key = GenerateCacheKey(&temp_info);
634 // Defer to DoomEntry if there is an active entry, otherwise call
635 // AsyncDoomEntry without triggering a callback.
636 if (active_entries_.count(key))
637 DoomEntry(key, NULL);
638 else
639 AsyncDoomEntry(key, NULL);
642 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
643 DCHECK(entry->doomed);
644 DCHECK(!entry->writer);
645 DCHECK(entry->readers.empty());
646 DCHECK(entry->pending_queue.empty());
648 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
649 DCHECK(it != doomed_entries_.end());
650 doomed_entries_.erase(it);
652 delete entry;
655 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
656 ActiveEntriesMap::const_iterator it = active_entries_.find(key);
657 return it != active_entries_.end() ? it->second : NULL;
660 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
661 disk_cache::Entry* disk_entry) {
662 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
663 ActiveEntry* entry = new ActiveEntry(disk_entry);
664 active_entries_[disk_entry->GetKey()] = entry;
665 return entry;
668 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
669 DCHECK(!entry->will_process_pending_queue);
670 DCHECK(!entry->doomed);
671 DCHECK(!entry->writer);
672 DCHECK(entry->disk_entry);
673 DCHECK(entry->readers.empty());
674 DCHECK(entry->pending_queue.empty());
676 std::string key = entry->disk_entry->GetKey();
677 if (key.empty())
678 return SlowDeactivateEntry(entry);
680 ActiveEntriesMap::iterator it = active_entries_.find(key);
681 DCHECK(it != active_entries_.end());
682 DCHECK(it->second == entry);
684 active_entries_.erase(it);
685 delete entry;
688 // We don't know this entry's key so we have to find it without it.
689 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
690 for (ActiveEntriesMap::iterator it = active_entries_.begin();
691 it != active_entries_.end(); ++it) {
692 if (it->second == entry) {
693 active_entries_.erase(it);
694 delete entry;
695 break;
700 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
701 DCHECK(!FindActiveEntry(key));
703 PendingOpsMap::const_iterator it = pending_ops_.find(key);
704 if (it != pending_ops_.end())
705 return it->second;
707 PendingOp* operation = new PendingOp();
708 pending_ops_[key] = operation;
709 return operation;
712 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
713 std::string key;
714 if (pending_op->disk_entry)
715 key = pending_op->disk_entry->GetKey();
717 if (!key.empty()) {
718 PendingOpsMap::iterator it = pending_ops_.find(key);
719 DCHECK(it != pending_ops_.end());
720 pending_ops_.erase(it);
721 } else {
722 for (PendingOpsMap::iterator it = pending_ops_.begin();
723 it != pending_ops_.end(); ++it) {
724 if (it->second == pending_op) {
725 pending_ops_.erase(it);
726 break;
730 DCHECK(pending_op->pending_queue.empty());
732 delete pending_op;
735 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
736 Transaction* trans) {
737 ActiveEntry* active_entry = FindActiveEntry(key);
738 if (active_entry) {
739 *entry = active_entry;
740 return OK;
743 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
744 PendingOp* pending_op = GetPendingOp(key);
745 if (pending_op->writer) {
746 pending_op->pending_queue.push_back(item);
747 return ERR_IO_PENDING;
750 DCHECK(pending_op->pending_queue.empty());
752 pending_op->writer = item;
753 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
754 GetWeakPtr(), pending_op);
756 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry),
757 pending_op->callback);
758 if (rv != ERR_IO_PENDING) {
759 item->ClearTransaction();
760 pending_op->callback.Run(rv);
763 return rv;
766 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
767 Transaction* trans) {
768 if (FindActiveEntry(key)) {
769 return ERR_CACHE_RACE;
772 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
773 PendingOp* pending_op = GetPendingOp(key);
774 if (pending_op->writer) {
775 pending_op->pending_queue.push_back(item);
776 return ERR_IO_PENDING;
779 DCHECK(pending_op->pending_queue.empty());
781 pending_op->writer = item;
782 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
783 GetWeakPtr(), pending_op);
785 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
786 pending_op->callback);
787 if (rv != ERR_IO_PENDING) {
788 item->ClearTransaction();
789 pending_op->callback.Run(rv);
792 return rv;
795 void HttpCache::DestroyEntry(ActiveEntry* entry) {
796 if (entry->doomed) {
797 FinalizeDoomedEntry(entry);
798 } else {
799 DeactivateEntry(entry);
803 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
804 DCHECK(entry);
805 DCHECK(entry->disk_entry);
807 // We implement a basic reader/writer lock for the disk cache entry. If
808 // there is already a writer, then everyone has to wait for the writer to
809 // finish before they can access the cache entry. There can be multiple
810 // readers.
812 // NOTE: If the transaction can only write, then the entry should not be in
813 // use (since any existing entry should have already been doomed).
815 if (entry->writer || entry->will_process_pending_queue) {
816 entry->pending_queue.push_back(trans);
817 return ERR_IO_PENDING;
820 if (trans->mode() & Transaction::WRITE) {
821 // transaction needs exclusive access to the entry
822 if (entry->readers.empty()) {
823 entry->writer = trans;
824 } else {
825 entry->pending_queue.push_back(trans);
826 return ERR_IO_PENDING;
828 } else {
829 // transaction needs read access to the entry
830 entry->readers.push_back(trans);
833 // We do this before calling EntryAvailable to force any further calls to
834 // AddTransactionToEntry to add their transaction to the pending queue, which
835 // ensures FIFO ordering.
836 if (!entry->writer && !entry->pending_queue.empty())
837 ProcessPendingQueue(entry);
839 return OK;
842 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
843 bool cancel) {
844 // If we already posted a task to move on to the next transaction and this was
845 // the writer, there is nothing to cancel.
846 if (entry->will_process_pending_queue && entry->readers.empty())
847 return;
849 if (entry->writer) {
850 DCHECK(trans == entry->writer);
852 // Assume there was a failure.
853 bool success = false;
854 if (cancel) {
855 DCHECK(entry->disk_entry);
856 // This is a successful operation in the sense that we want to keep the
857 // entry.
858 success = trans->AddTruncatedFlag();
859 // The previous operation may have deleted the entry.
860 if (!trans->entry())
861 return;
863 DoneWritingToEntry(entry, success);
864 } else {
865 DoneReadingFromEntry(entry, trans);
869 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
870 DCHECK(entry->readers.empty());
872 entry->writer = NULL;
874 if (success) {
875 ProcessPendingQueue(entry);
876 } else {
877 DCHECK(!entry->will_process_pending_queue);
879 // We failed to create this entry.
880 TransactionList pending_queue;
881 pending_queue.swap(entry->pending_queue);
883 entry->disk_entry->Doom();
884 DestroyEntry(entry);
886 // We need to do something about these pending entries, which now need to
887 // be added to a new entry.
888 while (!pending_queue.empty()) {
889 // ERR_CACHE_RACE causes the transaction to restart the whole process.
890 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
891 pending_queue.pop_front();
896 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
897 DCHECK(!entry->writer);
899 TransactionList::iterator it =
900 std::find(entry->readers.begin(), entry->readers.end(), trans);
901 DCHECK(it != entry->readers.end());
903 entry->readers.erase(it);
905 ProcessPendingQueue(entry);
908 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
909 DCHECK(entry->writer);
910 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
911 DCHECK(entry->readers.empty());
913 Transaction* trans = entry->writer;
915 entry->writer = NULL;
916 entry->readers.push_back(trans);
918 ProcessPendingQueue(entry);
921 LoadState HttpCache::GetLoadStateForPendingTransaction(
922 const Transaction* trans) {
923 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
924 if (i == active_entries_.end()) {
925 // If this is really a pending transaction, and it is not part of
926 // active_entries_, we should be creating the backend or the entry.
927 return LOAD_STATE_WAITING_FOR_CACHE;
930 Transaction* writer = i->second->writer;
931 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
934 void HttpCache::RemovePendingTransaction(Transaction* trans) {
935 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
936 bool found = false;
937 if (i != active_entries_.end())
938 found = RemovePendingTransactionFromEntry(i->second, trans);
940 if (found)
941 return;
943 if (building_backend_) {
944 PendingOpsMap::const_iterator j = pending_ops_.find(std::string());
945 if (j != pending_ops_.end())
946 found = RemovePendingTransactionFromPendingOp(j->second, trans);
948 if (found)
949 return;
952 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
953 if (j != pending_ops_.end())
954 found = RemovePendingTransactionFromPendingOp(j->second, trans);
956 if (found)
957 return;
959 ActiveEntriesSet::iterator k = doomed_entries_.begin();
960 for (; k != doomed_entries_.end() && !found; ++k)
961 found = RemovePendingTransactionFromEntry(*k, trans);
963 DCHECK(found) << "Pending transaction not found";
966 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
967 Transaction* trans) {
968 TransactionList& pending_queue = entry->pending_queue;
970 TransactionList::iterator j =
971 find(pending_queue.begin(), pending_queue.end(), trans);
972 if (j == pending_queue.end())
973 return false;
975 pending_queue.erase(j);
976 return true;
979 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
980 Transaction* trans) {
981 if (pending_op->writer->Matches(trans)) {
982 pending_op->writer->ClearTransaction();
983 pending_op->writer->ClearEntry();
984 return true;
986 WorkItemList& pending_queue = pending_op->pending_queue;
988 WorkItemList::iterator it = pending_queue.begin();
989 for (; it != pending_queue.end(); ++it) {
990 if ((*it)->Matches(trans)) {
991 delete *it;
992 pending_queue.erase(it);
993 return true;
996 return false;
999 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) {
1000 if (session &&
1001 !session->quic_stream_factory()->has_quic_server_info_factory()) {
1002 DCHECK(!quic_server_info_factory_);
1003 quic_server_info_factory_.reset(new QuicServerInfoFactoryAdaptor(this));
1004 session->quic_stream_factory()->set_quic_server_info_factory(
1005 quic_server_info_factory_.get());
1009 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
1010 // Multiple readers may finish with an entry at once, so we want to batch up
1011 // calls to OnProcessPendingQueue. This flag also tells us that we should
1012 // not delete the entry before OnProcessPendingQueue runs.
1013 if (entry->will_process_pending_queue)
1014 return;
1015 entry->will_process_pending_queue = true;
1017 base::ThreadTaskRunnerHandle::Get()->PostTask(
1018 FROM_HERE,
1019 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1022 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1023 entry->will_process_pending_queue = false;
1024 DCHECK(!entry->writer);
1026 // If no one is interested in this entry, then we can deactivate it.
1027 if (entry->pending_queue.empty()) {
1028 if (entry->readers.empty())
1029 DestroyEntry(entry);
1030 return;
1033 // Promote next transaction from the pending queue.
1034 Transaction* next = entry->pending_queue.front();
1035 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1036 return; // Have to wait.
1038 entry->pending_queue.erase(entry->pending_queue.begin());
1040 int rv = AddTransactionToEntry(entry, next);
1041 if (rv != ERR_IO_PENDING) {
1042 next->io_callback().Run(rv);
1046 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1047 WorkItemOperation op = pending_op->writer->operation();
1049 // Completing the creation of the backend is simpler than the other cases.
1050 if (op == WI_CREATE_BACKEND)
1051 return OnBackendCreated(result, pending_op);
1053 scoped_ptr<WorkItem> item(pending_op->writer);
1054 bool fail_requests = false;
1056 ActiveEntry* entry = NULL;
1057 std::string key;
1058 if (result == OK) {
1059 if (op == WI_DOOM_ENTRY) {
1060 // Anything after a Doom has to be restarted.
1061 fail_requests = true;
1062 } else if (item->IsValid()) {
1063 key = pending_op->disk_entry->GetKey();
1064 entry = ActivateEntry(pending_op->disk_entry);
1065 } else {
1066 // The writer transaction is gone.
1067 if (op == WI_CREATE_ENTRY)
1068 pending_op->disk_entry->Doom();
1069 pending_op->disk_entry->Close();
1070 pending_op->disk_entry = NULL;
1071 fail_requests = true;
1075 // We are about to notify a bunch of transactions, and they may decide to
1076 // re-issue a request (or send a different one). If we don't delete
1077 // pending_op, the new request will be appended to the end of the list, and
1078 // we'll see it again from this point before it has a chance to complete (and
1079 // we'll be messing out the request order). The down side is that if for some
1080 // reason notifying request A ends up cancelling request B (for the same key),
1081 // we won't find request B anywhere (because it would be in a local variable
1082 // here) and that's bad. If there is a chance for that to happen, we'll have
1083 // to move the callback used to be a CancelableCallback. By the way, for this
1084 // to happen the action (to cancel B) has to be synchronous to the
1085 // notification for request A.
1086 WorkItemList pending_items;
1087 pending_items.swap(pending_op->pending_queue);
1088 DeletePendingOp(pending_op);
1090 item->NotifyTransaction(result, entry);
1092 while (!pending_items.empty()) {
1093 item.reset(pending_items.front());
1094 pending_items.pop_front();
1096 if (item->operation() == WI_DOOM_ENTRY) {
1097 // A queued doom request is always a race.
1098 fail_requests = true;
1099 } else if (result == OK) {
1100 entry = FindActiveEntry(key);
1101 if (!entry)
1102 fail_requests = true;
1105 if (fail_requests) {
1106 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1107 continue;
1110 if (item->operation() == WI_CREATE_ENTRY) {
1111 if (result == OK) {
1112 // A second Create request, but the first request succeeded.
1113 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1114 } else {
1115 if (op != WI_CREATE_ENTRY) {
1116 // Failed Open followed by a Create.
1117 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1118 fail_requests = true;
1119 } else {
1120 item->NotifyTransaction(result, entry);
1123 } else {
1124 if (op == WI_CREATE_ENTRY && result != OK) {
1125 // Failed Create followed by an Open.
1126 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1127 fail_requests = true;
1128 } else {
1129 item->NotifyTransaction(result, entry);
1135 // static
1136 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
1137 PendingOp* pending_op,
1138 int rv) {
1139 if (cache.get()) {
1140 cache->OnIOComplete(rv, pending_op);
1141 } else {
1142 // The callback was cancelled so we should delete the pending_op that
1143 // was used with this callback.
1144 delete pending_op;
1148 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1149 scoped_ptr<WorkItem> item(pending_op->writer);
1150 WorkItemOperation op = item->operation();
1151 DCHECK_EQ(WI_CREATE_BACKEND, op);
1153 // We don't need the callback anymore.
1154 pending_op->callback.Reset();
1156 if (backend_factory_.get()) {
1157 // We may end up calling OnBackendCreated multiple times if we have pending
1158 // work items. The first call saves the backend and releases the factory,
1159 // and the last call clears building_backend_.
1160 backend_factory_.reset(); // Reclaim memory.
1161 if (result == OK) {
1162 disk_cache_ = pending_op->backend.Pass();
1163 if (UseCertCache())
1164 cert_cache_.reset(new DiskBasedCertCache(disk_cache_.get()));
1168 if (!pending_op->pending_queue.empty()) {
1169 WorkItem* pending_item = pending_op->pending_queue.front();
1170 pending_op->pending_queue.pop_front();
1171 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1173 // We want to process a single callback at a time, because the cache may
1174 // go away from the callback.
1175 pending_op->writer = pending_item;
1177 base::ThreadTaskRunnerHandle::Get()->PostTask(
1178 FROM_HERE, base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(),
1179 result, pending_op));
1180 } else {
1181 building_backend_ = false;
1182 DeletePendingOp(pending_op);
1185 // The cache may be gone when we return from the callback.
1186 if (!item->DoCallback(result, disk_cache_.get()))
1187 item->NotifyTransaction(result, NULL);
1190 } // namespace net