Don't add an aura tooltip to bubble close buttons on Windows.
[chromium-blink-merge.git] / net / http / http_cache.cc
blobcef2928af3bdfb8fd1bd8115ca5de539501b09e5
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/files/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/metrics/histogram.h"
25 #include "base/pickle.h"
26 #include "base/profiler/scoped_tracker.h"
27 #include "base/stl_util.h"
28 #include "base/strings/string_number_conversions.h"
29 #include "base/strings/string_util.h"
30 #include "base/strings/stringprintf.h"
31 #include "base/threading/worker_pool.h"
32 #include "base/time/default_clock.h"
33 #include "base/time/time.h"
34 #include "net/base/cache_type.h"
35 #include "net/base/io_buffer.h"
36 #include "net/base/load_flags.h"
37 #include "net/base/net_errors.h"
38 #include "net/base/network_delegate.h"
39 #include "net/base/upload_data_stream.h"
40 #include "net/disk_cache/disk_cache.h"
41 #include "net/http/disk_based_cert_cache.h"
42 #include "net/http/disk_cache_based_quic_server_info.h"
43 #include "net/http/http_cache_transaction.h"
44 #include "net/http/http_network_layer.h"
45 #include "net/http/http_network_session.h"
46 #include "net/http/http_request_info.h"
47 #include "net/http/http_response_headers.h"
48 #include "net/http/http_response_info.h"
49 #include "net/http/http_util.h"
50 #include "net/quic/crypto/quic_server_info.h"
52 namespace {
54 bool UseCertCache() {
55 return base::FieldTrialList::FindFullName("CertCacheTrial") ==
56 "ExperimentGroup";
59 } // namespace
61 namespace net {
63 HttpCache::DefaultBackend::DefaultBackend(
64 CacheType type,
65 BackendType backend_type,
66 const base::FilePath& path,
67 int max_bytes,
68 const scoped_refptr<base::SingleThreadTaskRunner>& thread)
69 : type_(type),
70 backend_type_(backend_type),
71 path_(path),
72 max_bytes_(max_bytes),
73 thread_(thread) {
76 HttpCache::DefaultBackend::~DefaultBackend() {}
78 // static
79 HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) {
80 return new DefaultBackend(MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT,
81 base::FilePath(), max_bytes, NULL);
84 int HttpCache::DefaultBackend::CreateBackend(
85 NetLog* net_log, scoped_ptr<disk_cache::Backend>* backend,
86 const CompletionCallback& callback) {
87 DCHECK_GE(max_bytes_, 0);
88 return disk_cache::CreateCacheBackend(type_,
89 backend_type_,
90 path_,
91 max_bytes_,
92 true,
93 thread_,
94 net_log,
95 backend,
96 callback);
99 //-----------------------------------------------------------------------------
101 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
102 : disk_entry(entry),
103 writer(NULL),
104 will_process_pending_queue(false),
105 doomed(false) {
108 HttpCache::ActiveEntry::~ActiveEntry() {
109 if (disk_entry) {
110 disk_entry->Close();
111 disk_entry = NULL;
115 //-----------------------------------------------------------------------------
117 // This structure keeps track of work items that are attempting to create or
118 // open cache entries or the backend itself.
119 struct HttpCache::PendingOp {
120 PendingOp() : disk_entry(NULL), writer(NULL) {}
121 ~PendingOp() {}
123 disk_cache::Entry* disk_entry;
124 scoped_ptr<disk_cache::Backend> backend;
125 WorkItem* writer;
126 CompletionCallback callback; // BackendCallback.
127 WorkItemList pending_queue;
130 //-----------------------------------------------------------------------------
132 // The type of operation represented by a work item.
133 enum WorkItemOperation {
134 WI_CREATE_BACKEND,
135 WI_OPEN_ENTRY,
136 WI_CREATE_ENTRY,
137 WI_DOOM_ENTRY
140 // A work item encapsulates a single request to the backend with all the
141 // information needed to complete that request.
142 class HttpCache::WorkItem {
143 public:
144 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
145 : operation_(operation),
146 trans_(trans),
147 entry_(entry),
148 backend_(NULL) {}
149 WorkItem(WorkItemOperation operation, Transaction* trans,
150 const net::CompletionCallback& cb, 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 net::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 //-----------------------------------------------------------------------------
298 class HttpCache::AsyncValidation {
299 public:
300 AsyncValidation(const HttpRequestInfo& original_request, HttpCache* cache)
301 : request_(original_request), cache_(cache) {}
302 ~AsyncValidation() {}
304 void Start(const BoundNetLog& net_log,
305 scoped_ptr<Transaction> transaction,
306 NetworkDelegate* network_delegate);
308 private:
309 void OnStarted(int result);
310 void DoRead();
311 void OnRead(int result);
313 // Terminate this request with net error code |result|. Logs the transaction
314 // result and asks HttpCache to delete this object.
315 // If there was a client or server certificate error, it cannot be recovered
316 // asynchronously, so we need to prevent future attempts to asynchronously
317 // fetch the resource. In this case, the cache entry is doomed.
318 void Terminate(int result);
320 HttpRequestInfo request_;
321 scoped_refptr<IOBuffer> buf_;
322 CompletionCallback read_callback_;
323 scoped_ptr<Transaction> transaction_;
324 base::Time start_time_;
326 // The HttpCache object owns this object. This object is always deleted before
327 // the pointer to the cache becomes invalid.
328 HttpCache* cache_;
330 DISALLOW_COPY_AND_ASSIGN(AsyncValidation);
333 void HttpCache::AsyncValidation::Start(const BoundNetLog& net_log,
334 scoped_ptr<Transaction> transaction,
335 NetworkDelegate* network_delegate) {
336 transaction_ = transaction.Pass();
337 if (network_delegate) {
338 // This code is necessary to enable async transactions to pass over the
339 // data-reduction proxy. This is a violation of the "once-and-only-once"
340 // principle, since it copies code from URLRequestHttpJob. We cannot use the
341 // original callback passed to HttpCache::Transaction by URLRequestHttpJob
342 // as it will only be valid as long as the URLRequestHttpJob object is
343 // alive, and that object will be deleted as soon as the synchronous request
344 // completes.
346 // This code is also an encapsulation violation. We are exploiting the fact
347 // that the |request| parameter to NotifyBeforeSendProxyHeaders() is never
348 // actually used for anything, and so can be NULL.
350 // TODO(ricea): Do this better.
351 transaction_->SetBeforeProxyHeadersSentCallback(
352 base::Bind(&NetworkDelegate::NotifyBeforeSendProxyHeaders,
353 base::Unretained(network_delegate),
354 static_cast<URLRequest*>(NULL)));
355 // The above use of base::Unretained is safe because the NetworkDelegate has
356 // to live at least as long as the HttpNetworkSession which has to live as
357 // least as long as the HttpNetworkLayer which has to live at least as long
358 // this HttpCache object.
361 DCHECK_EQ(0, request_.load_flags & LOAD_ASYNC_REVALIDATION);
362 request_.load_flags |= LOAD_ASYNC_REVALIDATION;
363 start_time_ = cache_->clock()->Now();
364 // This use of base::Unretained is safe because |transaction_| is owned by
365 // this object.
366 read_callback_ = base::Bind(&AsyncValidation::OnRead, base::Unretained(this));
367 // This use of base::Unretained is safe as above.
368 int rv = transaction_->Start(
369 &request_,
370 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)),
371 net_log);
373 if (rv == ERR_IO_PENDING)
374 return;
376 OnStarted(rv);
379 void HttpCache::AsyncValidation::OnStarted(int result) {
380 if (result != OK) {
381 DVLOG(1) << "Asynchronous transaction start failed for " << request_.url;
382 Terminate(result);
383 return;
386 while (transaction_->IsReadyToRestartForAuth()) {
387 // This code is based on URLRequestHttpJob::RestartTransactionWithAuth,
388 // however when we do this here cookies on the response will not be
389 // stored. Fortunately only a tiny number of sites set cookies on 401
390 // responses, and none of them use stale-while-revalidate.
391 result = transaction_->RestartWithAuth(
392 AuthCredentials(),
393 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)));
394 if (result == ERR_IO_PENDING)
395 return;
396 if (result != OK) {
397 DVLOG(1) << "Synchronous transaction restart with auth failed for "
398 << request_.url;
399 Terminate(result);
400 return;
404 DoRead();
407 void HttpCache::AsyncValidation::DoRead() {
408 const size_t kBufSize = 4096;
409 if (!buf_.get())
410 buf_ = new IOBuffer(kBufSize);
412 int rv = 0;
413 do {
414 rv = transaction_->Read(buf_.get(), kBufSize, read_callback_);
415 } while (rv > 0);
417 if (rv == ERR_IO_PENDING)
418 return;
420 OnRead(rv);
423 void HttpCache::AsyncValidation::OnRead(int result) {
424 if (result > 0) {
425 DoRead();
426 return;
428 Terminate(result);
431 void HttpCache::AsyncValidation::Terminate(int result) {
432 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED || IsCertificateError(result)) {
433 // We should not attempt to access this resource asynchronously again until
434 // the certificate problem has been resolved.
435 // TODO(ricea): For ERR_SSL_CLIENT_AUTH_CERT_NEEDED, mark the entry as
436 // requiring synchronous revalidation rather than just deleting it. Other
437 // certificate errors cause the resource to be considered uncacheable
438 // anyway.
439 cache_->DoomEntry(transaction_->key(), transaction_.get());
441 base::TimeDelta duration = cache_->clock()->Now() - start_time_;
442 UMA_HISTOGRAM_TIMES("HttpCache.AsyncValidationDuration", duration);
443 transaction_->net_log().EndEventWithNetErrorCode(
444 NetLog::TYPE_ASYNC_REVALIDATION, result);
445 cache_->DeleteAsyncValidation(cache_->GenerateCacheKey(&request_));
446 // |this| is deleted.
449 //-----------------------------------------------------------------------------
450 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params,
451 BackendFactory* backend_factory)
452 : net_log_(params.net_log),
453 backend_factory_(backend_factory),
454 building_backend_(false),
455 bypass_lock_for_test_(false),
456 fail_conditionalization_for_test_(false),
457 use_stale_while_revalidate_(params.use_stale_while_revalidate),
458 mode_(NORMAL),
459 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))),
460 clock_(new base::DefaultClock()),
461 weak_factory_(this) {
462 SetupQuicServerInfoFactory(network_layer_->GetSession());
466 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
467 // |session| is shared.
468 HttpCache::HttpCache(HttpNetworkSession* session,
469 BackendFactory* backend_factory)
470 : net_log_(session->net_log()),
471 backend_factory_(backend_factory),
472 building_backend_(false),
473 bypass_lock_for_test_(false),
474 fail_conditionalization_for_test_(false),
475 use_stale_while_revalidate_(session->params().use_stale_while_revalidate),
476 mode_(NORMAL),
477 network_layer_(new HttpNetworkLayer(session)),
478 clock_(new base::DefaultClock()),
479 weak_factory_(this) {
482 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
483 NetLog* net_log,
484 BackendFactory* backend_factory)
485 : net_log_(net_log),
486 backend_factory_(backend_factory),
487 building_backend_(false),
488 bypass_lock_for_test_(false),
489 fail_conditionalization_for_test_(false),
490 use_stale_while_revalidate_(false),
491 mode_(NORMAL),
492 network_layer_(network_layer),
493 clock_(new base::DefaultClock()),
494 weak_factory_(this) {
495 SetupQuicServerInfoFactory(network_layer_->GetSession());
496 HttpNetworkSession* session = network_layer_->GetSession();
497 if (session)
498 use_stale_while_revalidate_ = session->params().use_stale_while_revalidate;
501 HttpCache::~HttpCache() {
502 // Transactions should see an invalid cache after this point; otherwise they
503 // could see an inconsistent object (half destroyed).
504 weak_factory_.InvalidateWeakPtrs();
506 // If we have any active entries remaining, then we need to deactivate them.
507 // We may have some pending calls to OnProcessPendingQueue, but since those
508 // won't run (due to our destruction), we can simply ignore the corresponding
509 // will_process_pending_queue flag.
510 while (!active_entries_.empty()) {
511 ActiveEntry* entry = active_entries_.begin()->second;
512 entry->will_process_pending_queue = false;
513 entry->pending_queue.clear();
514 entry->readers.clear();
515 entry->writer = NULL;
516 DeactivateEntry(entry);
519 STLDeleteElements(&doomed_entries_);
520 STLDeleteValues(&async_validations_);
522 // Before deleting pending_ops_, we have to make sure that the disk cache is
523 // done with said operations, or it will attempt to use deleted data.
524 cert_cache_.reset();
525 disk_cache_.reset();
527 PendingOpsMap::iterator pending_it = pending_ops_.begin();
528 for (; pending_it != pending_ops_.end(); ++pending_it) {
529 // We are not notifying the transactions about the cache going away, even
530 // though they are waiting for a callback that will never fire.
531 PendingOp* pending_op = pending_it->second;
532 delete pending_op->writer;
533 bool delete_pending_op = true;
534 if (building_backend_) {
535 // If we don't have a backend, when its construction finishes it will
536 // deliver the callbacks.
537 if (!pending_op->callback.is_null()) {
538 // If not null, the callback will delete the pending operation later.
539 delete_pending_op = false;
541 } else {
542 pending_op->callback.Reset();
545 STLDeleteElements(&pending_op->pending_queue);
546 if (delete_pending_op)
547 delete pending_op;
551 int HttpCache::GetBackend(disk_cache::Backend** backend,
552 const CompletionCallback& callback) {
553 DCHECK(!callback.is_null());
555 if (disk_cache_.get()) {
556 *backend = disk_cache_.get();
557 return OK;
560 return CreateBackend(backend, callback);
563 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
564 return disk_cache_.get();
567 // static
568 bool HttpCache::ParseResponseInfo(const char* data, int len,
569 HttpResponseInfo* response_info,
570 bool* response_truncated) {
571 Pickle pickle(data, len);
572 return response_info->InitFromPickle(pickle, response_truncated);
575 void HttpCache::WriteMetadata(const GURL& url,
576 RequestPriority priority,
577 base::Time expected_response_time,
578 IOBuffer* buf,
579 int buf_len) {
580 if (!buf_len)
581 return;
583 // Do lazy initialization of disk cache if needed.
584 if (!disk_cache_.get()) {
585 // We don't care about the result.
586 CreateBackend(NULL, net::CompletionCallback());
589 HttpCache::Transaction* trans =
590 new HttpCache::Transaction(priority, this);
591 MetadataWriter* writer = new MetadataWriter(trans);
593 // The writer will self destruct when done.
594 writer->Write(url, expected_response_time, buf, buf_len);
597 void HttpCache::CloseAllConnections() {
598 HttpNetworkSession* session = GetSession();
599 if (session)
600 session->CloseAllConnections();
603 void HttpCache::CloseIdleConnections() {
604 HttpNetworkSession* session = GetSession();
605 if (session)
606 session->CloseIdleConnections();
609 void HttpCache::OnExternalCacheHit(const GURL& url,
610 const std::string& http_method) {
611 if (!disk_cache_.get() || mode_ == DISABLE)
612 return;
614 HttpRequestInfo request_info;
615 request_info.url = url;
616 request_info.method = http_method;
617 std::string key = GenerateCacheKey(&request_info);
618 disk_cache_->OnExternalCacheHit(key);
621 int HttpCache::CreateTransaction(RequestPriority priority,
622 scoped_ptr<HttpTransaction>* trans) {
623 // Do lazy initialization of disk cache if needed.
624 if (!disk_cache_.get()) {
625 // We don't care about the result.
626 CreateBackend(NULL, net::CompletionCallback());
629 HttpCache::Transaction* transaction =
630 new HttpCache::Transaction(priority, this);
631 if (bypass_lock_for_test_)
632 transaction->BypassLockForTest();
633 if (fail_conditionalization_for_test_)
634 transaction->FailConditionalizationForTest();
636 trans->reset(transaction);
637 return OK;
640 HttpCache* HttpCache::GetCache() {
641 return this;
644 HttpNetworkSession* HttpCache::GetSession() {
645 return network_layer_->GetSession();
648 scoped_ptr<HttpTransactionFactory>
649 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
650 scoped_ptr<HttpTransactionFactory> new_network_layer) {
651 scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass());
652 network_layer_ = new_network_layer.Pass();
653 return old_network_layer.Pass();
656 //-----------------------------------------------------------------------------
658 int HttpCache::CreateBackend(disk_cache::Backend** backend,
659 const net::CompletionCallback& callback) {
660 if (!backend_factory_.get())
661 return ERR_FAILED;
663 building_backend_ = true;
665 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
666 backend));
668 // This is the only operation that we can do that is not related to any given
669 // entry, so we use an empty key for it.
670 PendingOp* pending_op = GetPendingOp(std::string());
671 if (pending_op->writer) {
672 if (!callback.is_null())
673 pending_op->pending_queue.push_back(item.release());
674 return ERR_IO_PENDING;
677 DCHECK(pending_op->pending_queue.empty());
679 pending_op->writer = item.release();
680 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
681 GetWeakPtr(), pending_op);
683 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
684 pending_op->callback);
685 if (rv != ERR_IO_PENDING) {
686 pending_op->writer->ClearCallback();
687 pending_op->callback.Run(rv);
690 return rv;
693 int HttpCache::GetBackendForTransaction(Transaction* trans) {
694 if (disk_cache_.get())
695 return OK;
697 if (!building_backend_)
698 return ERR_FAILED;
700 WorkItem* item = new WorkItem(
701 WI_CREATE_BACKEND, trans, net::CompletionCallback(), NULL);
702 PendingOp* pending_op = GetPendingOp(std::string());
703 DCHECK(pending_op->writer);
704 pending_op->pending_queue.push_back(item);
705 return ERR_IO_PENDING;
708 // Generate a key that can be used inside the cache.
709 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
710 // Strip out the reference, username, and password sections of the URL.
711 std::string url = HttpUtil::SpecForRequest(request->url);
713 DCHECK_NE(DISABLE, mode_);
714 // No valid URL can begin with numerals, so we should not have to worry
715 // about collisions with normal URLs.
716 if (request->upload_data_stream &&
717 request->upload_data_stream->identifier()) {
718 url.insert(0,
719 base::StringPrintf("%" PRId64 "/",
720 request->upload_data_stream->identifier()));
722 return url;
725 void HttpCache::DoomActiveEntry(const std::string& key) {
726 ActiveEntriesMap::iterator it = active_entries_.find(key);
727 if (it == active_entries_.end())
728 return;
730 // This is not a performance critical operation, this is handling an error
731 // condition so it is OK to look up the entry again.
732 int rv = DoomEntry(key, NULL);
733 DCHECK_EQ(OK, rv);
736 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
737 // Need to abandon the ActiveEntry, but any transaction attached to the entry
738 // should not be impacted. Dooming an entry only means that it will no
739 // longer be returned by FindActiveEntry (and it will also be destroyed once
740 // all consumers are finished with the entry).
741 ActiveEntriesMap::iterator it = active_entries_.find(key);
742 if (it == active_entries_.end()) {
743 DCHECK(trans);
744 return AsyncDoomEntry(key, trans);
747 ActiveEntry* entry = it->second;
748 active_entries_.erase(it);
750 // We keep track of doomed entries so that we can ensure that they are
751 // cleaned up properly when the cache is destroyed.
752 doomed_entries_.insert(entry);
754 entry->disk_entry->Doom();
755 entry->doomed = true;
757 DCHECK(entry->writer || !entry->readers.empty() ||
758 entry->will_process_pending_queue);
759 return OK;
762 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
763 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
764 PendingOp* pending_op = GetPendingOp(key);
765 if (pending_op->writer) {
766 pending_op->pending_queue.push_back(item);
767 return ERR_IO_PENDING;
770 DCHECK(pending_op->pending_queue.empty());
772 pending_op->writer = item;
773 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
774 GetWeakPtr(), pending_op);
776 int rv = disk_cache_->DoomEntry(key, pending_op->callback);
777 if (rv != ERR_IO_PENDING) {
778 item->ClearTransaction();
779 pending_op->callback.Run(rv);
782 return rv;
785 void HttpCache::DoomMainEntryForUrl(const GURL& url) {
786 if (!disk_cache_)
787 return;
789 HttpRequestInfo temp_info;
790 temp_info.url = url;
791 temp_info.method = "GET";
792 std::string key = GenerateCacheKey(&temp_info);
794 // Defer to DoomEntry if there is an active entry, otherwise call
795 // AsyncDoomEntry without triggering a callback.
796 if (active_entries_.count(key))
797 DoomEntry(key, NULL);
798 else
799 AsyncDoomEntry(key, NULL);
802 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
803 DCHECK(entry->doomed);
804 DCHECK(!entry->writer);
805 DCHECK(entry->readers.empty());
806 DCHECK(entry->pending_queue.empty());
808 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
809 DCHECK(it != doomed_entries_.end());
810 doomed_entries_.erase(it);
812 delete entry;
815 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
816 ActiveEntriesMap::const_iterator it = active_entries_.find(key);
817 return it != active_entries_.end() ? it->second : NULL;
820 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
821 disk_cache::Entry* disk_entry) {
822 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
823 ActiveEntry* entry = new ActiveEntry(disk_entry);
824 active_entries_[disk_entry->GetKey()] = entry;
825 return entry;
828 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
829 DCHECK(!entry->will_process_pending_queue);
830 DCHECK(!entry->doomed);
831 DCHECK(!entry->writer);
832 DCHECK(entry->disk_entry);
833 DCHECK(entry->readers.empty());
834 DCHECK(entry->pending_queue.empty());
836 std::string key = entry->disk_entry->GetKey();
837 if (key.empty())
838 return SlowDeactivateEntry(entry);
840 ActiveEntriesMap::iterator it = active_entries_.find(key);
841 DCHECK(it != active_entries_.end());
842 DCHECK(it->second == entry);
844 active_entries_.erase(it);
845 delete entry;
848 // We don't know this entry's key so we have to find it without it.
849 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
850 for (ActiveEntriesMap::iterator it = active_entries_.begin();
851 it != active_entries_.end(); ++it) {
852 if (it->second == entry) {
853 active_entries_.erase(it);
854 delete entry;
855 break;
860 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
861 DCHECK(!FindActiveEntry(key));
863 PendingOpsMap::const_iterator it = pending_ops_.find(key);
864 if (it != pending_ops_.end())
865 return it->second;
867 PendingOp* operation = new PendingOp();
868 pending_ops_[key] = operation;
869 return operation;
872 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
873 std::string key;
874 if (pending_op->disk_entry)
875 key = pending_op->disk_entry->GetKey();
877 if (!key.empty()) {
878 PendingOpsMap::iterator it = pending_ops_.find(key);
879 DCHECK(it != pending_ops_.end());
880 pending_ops_.erase(it);
881 } else {
882 for (PendingOpsMap::iterator it = pending_ops_.begin();
883 it != pending_ops_.end(); ++it) {
884 if (it->second == pending_op) {
885 pending_ops_.erase(it);
886 break;
890 DCHECK(pending_op->pending_queue.empty());
892 delete pending_op;
895 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
896 Transaction* trans) {
897 ActiveEntry* active_entry = FindActiveEntry(key);
898 if (active_entry) {
899 *entry = active_entry;
900 return OK;
903 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
904 PendingOp* pending_op = GetPendingOp(key);
905 if (pending_op->writer) {
906 pending_op->pending_queue.push_back(item);
907 return ERR_IO_PENDING;
910 DCHECK(pending_op->pending_queue.empty());
912 pending_op->writer = item;
913 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
914 GetWeakPtr(), pending_op);
916 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry),
917 pending_op->callback);
918 if (rv != ERR_IO_PENDING) {
919 item->ClearTransaction();
920 pending_op->callback.Run(rv);
923 return rv;
926 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
927 Transaction* trans) {
928 if (FindActiveEntry(key)) {
929 return ERR_CACHE_RACE;
932 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
933 PendingOp* pending_op = GetPendingOp(key);
934 if (pending_op->writer) {
935 pending_op->pending_queue.push_back(item);
936 return ERR_IO_PENDING;
939 DCHECK(pending_op->pending_queue.empty());
941 pending_op->writer = item;
942 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
943 GetWeakPtr(), pending_op);
945 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
946 pending_op->callback);
947 if (rv != ERR_IO_PENDING) {
948 item->ClearTransaction();
949 pending_op->callback.Run(rv);
952 return rv;
955 void HttpCache::DestroyEntry(ActiveEntry* entry) {
956 if (entry->doomed) {
957 FinalizeDoomedEntry(entry);
958 } else {
959 DeactivateEntry(entry);
963 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
964 DCHECK(entry);
965 DCHECK(entry->disk_entry);
967 // We implement a basic reader/writer lock for the disk cache entry. If
968 // there is already a writer, then everyone has to wait for the writer to
969 // finish before they can access the cache entry. There can be multiple
970 // readers.
972 // NOTE: If the transaction can only write, then the entry should not be in
973 // use (since any existing entry should have already been doomed).
975 if (entry->writer || entry->will_process_pending_queue) {
976 entry->pending_queue.push_back(trans);
977 return ERR_IO_PENDING;
980 if (trans->mode() & Transaction::WRITE) {
981 // transaction needs exclusive access to the entry
982 if (entry->readers.empty()) {
983 entry->writer = trans;
984 } else {
985 entry->pending_queue.push_back(trans);
986 return ERR_IO_PENDING;
988 } else {
989 // transaction needs read access to the entry
990 entry->readers.push_back(trans);
993 // We do this before calling EntryAvailable to force any further calls to
994 // AddTransactionToEntry to add their transaction to the pending queue, which
995 // ensures FIFO ordering.
996 if (!entry->writer && !entry->pending_queue.empty())
997 ProcessPendingQueue(entry);
999 return OK;
1002 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
1003 bool cancel) {
1004 // If we already posted a task to move on to the next transaction and this was
1005 // the writer, there is nothing to cancel.
1006 if (entry->will_process_pending_queue && entry->readers.empty())
1007 return;
1009 if (entry->writer) {
1010 DCHECK(trans == entry->writer);
1012 // Assume there was a failure.
1013 bool success = false;
1014 if (cancel) {
1015 DCHECK(entry->disk_entry);
1016 // This is a successful operation in the sense that we want to keep the
1017 // entry.
1018 success = trans->AddTruncatedFlag();
1019 // The previous operation may have deleted the entry.
1020 if (!trans->entry())
1021 return;
1023 DoneWritingToEntry(entry, success);
1024 } else {
1025 DoneReadingFromEntry(entry, trans);
1029 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
1030 DCHECK(entry->readers.empty());
1032 entry->writer = NULL;
1034 if (success) {
1035 ProcessPendingQueue(entry);
1036 } else {
1037 DCHECK(!entry->will_process_pending_queue);
1039 // We failed to create this entry.
1040 TransactionList pending_queue;
1041 pending_queue.swap(entry->pending_queue);
1043 entry->disk_entry->Doom();
1044 DestroyEntry(entry);
1046 // We need to do something about these pending entries, which now need to
1047 // be added to a new entry.
1048 while (!pending_queue.empty()) {
1049 // ERR_CACHE_RACE causes the transaction to restart the whole process.
1050 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
1051 pending_queue.pop_front();
1056 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
1057 DCHECK(!entry->writer);
1059 TransactionList::iterator it =
1060 std::find(entry->readers.begin(), entry->readers.end(), trans);
1061 DCHECK(it != entry->readers.end());
1063 entry->readers.erase(it);
1065 ProcessPendingQueue(entry);
1068 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
1069 DCHECK(entry->writer);
1070 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
1071 DCHECK(entry->readers.empty());
1073 Transaction* trans = entry->writer;
1075 entry->writer = NULL;
1076 entry->readers.push_back(trans);
1078 ProcessPendingQueue(entry);
1081 LoadState HttpCache::GetLoadStateForPendingTransaction(
1082 const Transaction* trans) {
1083 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
1084 if (i == active_entries_.end()) {
1085 // If this is really a pending transaction, and it is not part of
1086 // active_entries_, we should be creating the backend or the entry.
1087 return LOAD_STATE_WAITING_FOR_CACHE;
1090 Transaction* writer = i->second->writer;
1091 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
1094 void HttpCache::RemovePendingTransaction(Transaction* trans) {
1095 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
1096 bool found = false;
1097 if (i != active_entries_.end())
1098 found = RemovePendingTransactionFromEntry(i->second, trans);
1100 if (found)
1101 return;
1103 if (building_backend_) {
1104 PendingOpsMap::const_iterator j = pending_ops_.find(std::string());
1105 if (j != pending_ops_.end())
1106 found = RemovePendingTransactionFromPendingOp(j->second, trans);
1108 if (found)
1109 return;
1112 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
1113 if (j != pending_ops_.end())
1114 found = RemovePendingTransactionFromPendingOp(j->second, trans);
1116 if (found)
1117 return;
1119 ActiveEntriesSet::iterator k = doomed_entries_.begin();
1120 for (; k != doomed_entries_.end() && !found; ++k)
1121 found = RemovePendingTransactionFromEntry(*k, trans);
1123 DCHECK(found) << "Pending transaction not found";
1126 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
1127 Transaction* trans) {
1128 TransactionList& pending_queue = entry->pending_queue;
1130 TransactionList::iterator j =
1131 find(pending_queue.begin(), pending_queue.end(), trans);
1132 if (j == pending_queue.end())
1133 return false;
1135 pending_queue.erase(j);
1136 return true;
1139 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
1140 Transaction* trans) {
1141 if (pending_op->writer->Matches(trans)) {
1142 pending_op->writer->ClearTransaction();
1143 pending_op->writer->ClearEntry();
1144 return true;
1146 WorkItemList& pending_queue = pending_op->pending_queue;
1148 WorkItemList::iterator it = pending_queue.begin();
1149 for (; it != pending_queue.end(); ++it) {
1150 if ((*it)->Matches(trans)) {
1151 delete *it;
1152 pending_queue.erase(it);
1153 return true;
1156 return false;
1159 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) {
1160 if (session &&
1161 !session->quic_stream_factory()->has_quic_server_info_factory()) {
1162 DCHECK(!quic_server_info_factory_);
1163 quic_server_info_factory_.reset(new QuicServerInfoFactoryAdaptor(this));
1164 session->quic_stream_factory()->set_quic_server_info_factory(
1165 quic_server_info_factory_.get());
1169 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
1170 // Multiple readers may finish with an entry at once, so we want to batch up
1171 // calls to OnProcessPendingQueue. This flag also tells us that we should
1172 // not delete the entry before OnProcessPendingQueue runs.
1173 if (entry->will_process_pending_queue)
1174 return;
1175 entry->will_process_pending_queue = true;
1177 base::MessageLoop::current()->PostTask(
1178 FROM_HERE,
1179 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1182 void HttpCache::PerformAsyncValidation(const HttpRequestInfo& original_request,
1183 const BoundNetLog& net_log) {
1184 DCHECK(use_stale_while_revalidate_);
1185 std::string key = GenerateCacheKey(&original_request);
1186 AsyncValidation* async_validation =
1187 new AsyncValidation(original_request, this);
1188 typedef AsyncValidationMap::value_type AsyncValidationKeyValue;
1189 bool insert_ok =
1190 async_validations_.insert(AsyncValidationKeyValue(key, async_validation))
1191 .second;
1192 if (!insert_ok) {
1193 DVLOG(1) << "Harmless race condition detected on URL "
1194 << original_request.url << "; discarding redundant revalidation.";
1195 delete async_validation;
1196 return;
1198 HttpNetworkSession* network_session = GetSession();
1199 NetworkDelegate* network_delegate = NULL;
1200 if (network_session)
1201 network_delegate = network_session->network_delegate();
1202 scoped_ptr<HttpTransaction> transaction;
1203 CreateTransaction(IDLE, &transaction);
1204 scoped_ptr<Transaction> downcast_transaction(
1205 static_cast<Transaction*>(transaction.release()));
1206 async_validation->Start(
1207 net_log, downcast_transaction.Pass(), network_delegate);
1208 // |async_validation| may have been deleted here.
1211 void HttpCache::DeleteAsyncValidation(const std::string& url) {
1212 AsyncValidationMap::iterator it = async_validations_.find(url);
1213 CHECK(it != async_validations_.end()); // security-critical invariant
1214 AsyncValidation* async_validation = it->second;
1215 async_validations_.erase(it);
1216 delete async_validation;
1219 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1220 entry->will_process_pending_queue = false;
1221 DCHECK(!entry->writer);
1223 // If no one is interested in this entry, then we can deactivate it.
1224 if (entry->pending_queue.empty()) {
1225 if (entry->readers.empty())
1226 DestroyEntry(entry);
1227 return;
1230 // Promote next transaction from the pending queue.
1231 Transaction* next = entry->pending_queue.front();
1232 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1233 return; // Have to wait.
1235 entry->pending_queue.erase(entry->pending_queue.begin());
1237 int rv = AddTransactionToEntry(entry, next);
1238 if (rv != ERR_IO_PENDING) {
1239 next->io_callback().Run(rv);
1243 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1244 WorkItemOperation op = pending_op->writer->operation();
1246 // Completing the creation of the backend is simpler than the other cases.
1247 if (op == WI_CREATE_BACKEND)
1248 return OnBackendCreated(result, pending_op);
1250 scoped_ptr<WorkItem> item(pending_op->writer);
1251 bool fail_requests = false;
1253 ActiveEntry* entry = NULL;
1254 std::string key;
1255 if (result == OK) {
1256 if (op == WI_DOOM_ENTRY) {
1257 // Anything after a Doom has to be restarted.
1258 fail_requests = true;
1259 } else if (item->IsValid()) {
1260 key = pending_op->disk_entry->GetKey();
1261 entry = ActivateEntry(pending_op->disk_entry);
1262 } else {
1263 // The writer transaction is gone.
1264 if (op == WI_CREATE_ENTRY)
1265 pending_op->disk_entry->Doom();
1266 pending_op->disk_entry->Close();
1267 pending_op->disk_entry = NULL;
1268 fail_requests = true;
1272 // We are about to notify a bunch of transactions, and they may decide to
1273 // re-issue a request (or send a different one). If we don't delete
1274 // pending_op, the new request will be appended to the end of the list, and
1275 // we'll see it again from this point before it has a chance to complete (and
1276 // we'll be messing out the request order). The down side is that if for some
1277 // reason notifying request A ends up cancelling request B (for the same key),
1278 // we won't find request B anywhere (because it would be in a local variable
1279 // here) and that's bad. If there is a chance for that to happen, we'll have
1280 // to move the callback used to be a CancelableCallback. By the way, for this
1281 // to happen the action (to cancel B) has to be synchronous to the
1282 // notification for request A.
1283 WorkItemList pending_items;
1284 pending_items.swap(pending_op->pending_queue);
1285 DeletePendingOp(pending_op);
1287 item->NotifyTransaction(result, entry);
1289 while (!pending_items.empty()) {
1290 item.reset(pending_items.front());
1291 pending_items.pop_front();
1293 if (item->operation() == WI_DOOM_ENTRY) {
1294 // A queued doom request is always a race.
1295 fail_requests = true;
1296 } else if (result == OK) {
1297 entry = FindActiveEntry(key);
1298 if (!entry)
1299 fail_requests = true;
1302 if (fail_requests) {
1303 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1304 continue;
1307 if (item->operation() == WI_CREATE_ENTRY) {
1308 if (result == OK) {
1309 // A second Create request, but the first request succeeded.
1310 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1311 } else {
1312 if (op != WI_CREATE_ENTRY) {
1313 // Failed Open followed by a Create.
1314 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1315 fail_requests = true;
1316 } else {
1317 item->NotifyTransaction(result, entry);
1320 } else {
1321 if (op == WI_CREATE_ENTRY && result != OK) {
1322 // Failed Create followed by an Open.
1323 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1324 fail_requests = true;
1325 } else {
1326 item->NotifyTransaction(result, entry);
1332 // static
1333 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
1334 PendingOp* pending_op,
1335 int rv) {
1336 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
1337 tracked_objects::ScopedTracker tracking_profile(
1338 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1339 "422516 HttpCache::OnPendingOpComplete"));
1341 if (cache.get()) {
1342 cache->OnIOComplete(rv, pending_op);
1343 } else {
1344 // The callback was cancelled so we should delete the pending_op that
1345 // was used with this callback.
1346 delete pending_op;
1350 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1351 scoped_ptr<WorkItem> item(pending_op->writer);
1352 WorkItemOperation op = item->operation();
1353 DCHECK_EQ(WI_CREATE_BACKEND, op);
1355 // We don't need the callback anymore.
1356 pending_op->callback.Reset();
1358 if (backend_factory_.get()) {
1359 // We may end up calling OnBackendCreated multiple times if we have pending
1360 // work items. The first call saves the backend and releases the factory,
1361 // and the last call clears building_backend_.
1362 backend_factory_.reset(); // Reclaim memory.
1363 if (result == OK) {
1364 disk_cache_ = pending_op->backend.Pass();
1365 if (UseCertCache())
1366 cert_cache_.reset(new DiskBasedCertCache(disk_cache_.get()));
1370 if (!pending_op->pending_queue.empty()) {
1371 WorkItem* pending_item = pending_op->pending_queue.front();
1372 pending_op->pending_queue.pop_front();
1373 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1375 // We want to process a single callback at a time, because the cache may
1376 // go away from the callback.
1377 pending_op->writer = pending_item;
1379 base::MessageLoop::current()->PostTask(
1380 FROM_HERE,
1381 base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(),
1382 result, pending_op));
1383 } else {
1384 building_backend_ = false;
1385 DeletePendingOp(pending_op);
1388 // The cache may be gone when we return from the callback.
1389 if (!item->DoCallback(result, disk_cache_.get()))
1390 item->NotifyTransaction(result, NULL);
1393 } // namespace net