Rewrite AndroidSyncSettings to be significantly simpler.
[chromium-blink-merge.git] / net / http / http_cache.cc
blob5260410356d1bbf835e64a01ae8dce83042aa01f
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 // Adaptor to delete a file on a worker thread.
60 void DeletePath(base::FilePath path) {
61 base::DeleteFile(path, false);
64 } // namespace
66 namespace net {
68 HttpCache::DefaultBackend::DefaultBackend(
69 CacheType type,
70 BackendType backend_type,
71 const base::FilePath& path,
72 int max_bytes,
73 const scoped_refptr<base::SingleThreadTaskRunner>& thread)
74 : type_(type),
75 backend_type_(backend_type),
76 path_(path),
77 max_bytes_(max_bytes),
78 thread_(thread) {
81 HttpCache::DefaultBackend::~DefaultBackend() {}
83 // static
84 HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) {
85 return new DefaultBackend(MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT,
86 base::FilePath(), max_bytes, NULL);
89 int HttpCache::DefaultBackend::CreateBackend(
90 NetLog* net_log, scoped_ptr<disk_cache::Backend>* backend,
91 const CompletionCallback& callback) {
92 DCHECK_GE(max_bytes_, 0);
93 return disk_cache::CreateCacheBackend(type_,
94 backend_type_,
95 path_,
96 max_bytes_,
97 true,
98 thread_,
99 net_log,
100 backend,
101 callback);
104 //-----------------------------------------------------------------------------
106 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
107 : disk_entry(entry),
108 writer(NULL),
109 will_process_pending_queue(false),
110 doomed(false) {
113 HttpCache::ActiveEntry::~ActiveEntry() {
114 if (disk_entry) {
115 disk_entry->Close();
116 disk_entry = NULL;
120 //-----------------------------------------------------------------------------
122 // This structure keeps track of work items that are attempting to create or
123 // open cache entries or the backend itself.
124 struct HttpCache::PendingOp {
125 PendingOp() : disk_entry(NULL), writer(NULL) {}
126 ~PendingOp() {}
128 disk_cache::Entry* disk_entry;
129 scoped_ptr<disk_cache::Backend> backend;
130 WorkItem* writer;
131 CompletionCallback callback; // BackendCallback.
132 WorkItemList pending_queue;
135 //-----------------------------------------------------------------------------
137 // The type of operation represented by a work item.
138 enum WorkItemOperation {
139 WI_CREATE_BACKEND,
140 WI_OPEN_ENTRY,
141 WI_CREATE_ENTRY,
142 WI_DOOM_ENTRY
145 // A work item encapsulates a single request to the backend with all the
146 // information needed to complete that request.
147 class HttpCache::WorkItem {
148 public:
149 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
150 : operation_(operation),
151 trans_(trans),
152 entry_(entry),
153 backend_(NULL) {}
154 WorkItem(WorkItemOperation operation, Transaction* trans,
155 const net::CompletionCallback& cb, disk_cache::Backend** backend)
156 : operation_(operation),
157 trans_(trans),
158 entry_(NULL),
159 callback_(cb),
160 backend_(backend) {}
161 ~WorkItem() {}
163 // Calls back the transaction with the result of the operation.
164 void NotifyTransaction(int result, ActiveEntry* entry) {
165 DCHECK(!entry || entry->disk_entry);
166 if (entry_)
167 *entry_ = entry;
168 if (trans_)
169 trans_->io_callback().Run(result);
172 // Notifies the caller about the operation completion. Returns true if the
173 // callback was invoked.
174 bool DoCallback(int result, disk_cache::Backend* backend) {
175 if (backend_)
176 *backend_ = backend;
177 if (!callback_.is_null()) {
178 callback_.Run(result);
179 return true;
181 return false;
184 WorkItemOperation operation() { return operation_; }
185 void ClearTransaction() { trans_ = NULL; }
186 void ClearEntry() { entry_ = NULL; }
187 void ClearCallback() { callback_.Reset(); }
188 bool Matches(Transaction* trans) const { return trans == trans_; }
189 bool IsValid() const { return trans_ || entry_ || !callback_.is_null(); }
191 private:
192 WorkItemOperation operation_;
193 Transaction* trans_;
194 ActiveEntry** entry_;
195 net::CompletionCallback callback_; // User callback.
196 disk_cache::Backend** backend_;
199 //-----------------------------------------------------------------------------
201 // This class encapsulates a transaction whose only purpose is to write metadata
202 // to a given entry.
203 class HttpCache::MetadataWriter {
204 public:
205 explicit MetadataWriter(HttpCache::Transaction* trans)
206 : transaction_(trans),
207 verified_(false),
208 buf_len_(0) {
211 ~MetadataWriter() {}
213 // Implements the bulk of HttpCache::WriteMetadata.
214 void Write(const GURL& url,
215 base::Time expected_response_time,
216 IOBuffer* buf,
217 int buf_len);
219 private:
220 void VerifyResponse(int result);
221 void SelfDestroy();
222 void OnIOComplete(int result);
224 scoped_ptr<HttpCache::Transaction> transaction_;
225 bool verified_;
226 scoped_refptr<IOBuffer> buf_;
227 int buf_len_;
228 base::Time expected_response_time_;
229 HttpRequestInfo request_info_;
230 DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
233 void HttpCache::MetadataWriter::Write(const GURL& url,
234 base::Time expected_response_time,
235 IOBuffer* buf,
236 int buf_len) {
237 DCHECK_GT(buf_len, 0);
238 DCHECK(buf);
239 DCHECK(buf->data());
240 request_info_.url = url;
241 request_info_.method = "GET";
242 request_info_.load_flags = LOAD_ONLY_FROM_CACHE;
244 expected_response_time_ = expected_response_time;
245 buf_ = buf;
246 buf_len_ = buf_len;
247 verified_ = false;
249 int rv = transaction_->Start(
250 &request_info_,
251 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)),
252 BoundNetLog());
253 if (rv != ERR_IO_PENDING)
254 VerifyResponse(rv);
257 void HttpCache::MetadataWriter::VerifyResponse(int result) {
258 verified_ = true;
259 if (result != OK)
260 return SelfDestroy();
262 const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
263 DCHECK(response_info->was_cached);
264 if (response_info->response_time != expected_response_time_)
265 return SelfDestroy();
267 result = transaction_->WriteMetadata(
268 buf_.get(),
269 buf_len_,
270 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)));
271 if (result != ERR_IO_PENDING)
272 SelfDestroy();
275 void HttpCache::MetadataWriter::SelfDestroy() {
276 delete this;
279 void HttpCache::MetadataWriter::OnIOComplete(int result) {
280 if (!verified_)
281 return VerifyResponse(result);
282 SelfDestroy();
285 //-----------------------------------------------------------------------------
287 class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory {
288 public:
289 explicit QuicServerInfoFactoryAdaptor(HttpCache* http_cache)
290 : http_cache_(http_cache) {
293 QuicServerInfo* GetForServer(const QuicServerId& server_id) override {
294 return new DiskCacheBasedQuicServerInfo(server_id, http_cache_);
297 private:
298 HttpCache* const http_cache_;
301 //-----------------------------------------------------------------------------
303 class HttpCache::AsyncValidation {
304 public:
305 AsyncValidation(const HttpRequestInfo& original_request, HttpCache* cache)
306 : request_(original_request), cache_(cache) {}
307 ~AsyncValidation() {}
309 void Start(const BoundNetLog& net_log,
310 scoped_ptr<Transaction> transaction,
311 NetworkDelegate* network_delegate);
313 private:
314 void OnStarted(int result);
315 void DoRead();
316 void OnRead(int result);
318 // Terminate this request with net error code |result|. Logs the transaction
319 // result and asks HttpCache to delete this object.
320 // If there was a client or server certificate error, it cannot be recovered
321 // asynchronously, so we need to prevent future attempts to asynchronously
322 // fetch the resource. In this case, the cache entry is doomed.
323 void Terminate(int result);
325 HttpRequestInfo request_;
326 scoped_refptr<IOBuffer> buf_;
327 CompletionCallback read_callback_;
328 scoped_ptr<Transaction> transaction_;
329 base::Time start_time_;
331 // The HttpCache object owns this object. This object is always deleted before
332 // the pointer to the cache becomes invalid.
333 HttpCache* cache_;
335 DISALLOW_COPY_AND_ASSIGN(AsyncValidation);
338 void HttpCache::AsyncValidation::Start(const BoundNetLog& net_log,
339 scoped_ptr<Transaction> transaction,
340 NetworkDelegate* network_delegate) {
341 transaction_ = transaction.Pass();
342 if (network_delegate) {
343 // This code is necessary to enable async transactions to pass over the
344 // data-reduction proxy. This is a violation of the "once-and-only-once"
345 // principle, since it copies code from URLRequestHttpJob. We cannot use the
346 // original callback passed to HttpCache::Transaction by URLRequestHttpJob
347 // as it will only be valid as long as the URLRequestHttpJob object is
348 // alive, and that object will be deleted as soon as the synchronous request
349 // completes.
351 // This code is also an encapsulation violation. We are exploiting the fact
352 // that the |request| parameter to NotifyBeforeSendProxyHeaders() is never
353 // actually used for anything, and so can be NULL.
355 // TODO(ricea): Do this better.
356 transaction_->SetBeforeProxyHeadersSentCallback(
357 base::Bind(&NetworkDelegate::NotifyBeforeSendProxyHeaders,
358 base::Unretained(network_delegate),
359 static_cast<URLRequest*>(NULL)));
360 // The above use of base::Unretained is safe because the NetworkDelegate has
361 // to live at least as long as the HttpNetworkSession which has to live as
362 // least as long as the HttpNetworkLayer which has to live at least as long
363 // this HttpCache object.
366 DCHECK_EQ(0, request_.load_flags & LOAD_ASYNC_REVALIDATION);
367 request_.load_flags |= LOAD_ASYNC_REVALIDATION;
368 start_time_ = cache_->clock()->Now();
369 // This use of base::Unretained is safe because |transaction_| is owned by
370 // this object.
371 read_callback_ = base::Bind(&AsyncValidation::OnRead, base::Unretained(this));
372 // This use of base::Unretained is safe as above.
373 int rv = transaction_->Start(
374 &request_,
375 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)),
376 net_log);
378 if (rv == ERR_IO_PENDING)
379 return;
381 OnStarted(rv);
384 void HttpCache::AsyncValidation::OnStarted(int result) {
385 if (result != OK) {
386 DVLOG(1) << "Asynchronous transaction start failed for " << request_.url;
387 Terminate(result);
388 return;
391 while (transaction_->IsReadyToRestartForAuth()) {
392 // This code is based on URLRequestHttpJob::RestartTransactionWithAuth,
393 // however when we do this here cookies on the response will not be
394 // stored. Fortunately only a tiny number of sites set cookies on 401
395 // responses, and none of them use stale-while-revalidate.
396 result = transaction_->RestartWithAuth(
397 AuthCredentials(),
398 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)));
399 if (result == ERR_IO_PENDING)
400 return;
401 if (result != OK) {
402 DVLOG(1) << "Synchronous transaction restart with auth failed for "
403 << request_.url;
404 Terminate(result);
405 return;
409 DoRead();
412 void HttpCache::AsyncValidation::DoRead() {
413 const size_t kBufSize = 4096;
414 if (!buf_.get())
415 buf_ = new IOBuffer(kBufSize);
417 int rv = 0;
418 do {
419 rv = transaction_->Read(buf_.get(), kBufSize, read_callback_);
420 } while (rv > 0);
422 if (rv == ERR_IO_PENDING)
423 return;
425 OnRead(rv);
428 void HttpCache::AsyncValidation::OnRead(int result) {
429 if (result > 0) {
430 DoRead();
431 return;
433 Terminate(result);
436 void HttpCache::AsyncValidation::Terminate(int result) {
437 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED || IsCertificateError(result)) {
438 // We should not attempt to access this resource asynchronously again until
439 // the certificate problem has been resolved.
440 // TODO(ricea): For ERR_SSL_CLIENT_AUTH_CERT_NEEDED, mark the entry as
441 // requiring synchronous revalidation rather than just deleting it. Other
442 // certificate errors cause the resource to be considered uncacheable
443 // anyway.
444 cache_->DoomEntry(transaction_->key(), transaction_.get());
446 base::TimeDelta duration = cache_->clock()->Now() - start_time_;
447 UMA_HISTOGRAM_TIMES("HttpCache.AsyncValidationDuration", duration);
448 transaction_->net_log().EndEventWithNetErrorCode(
449 NetLog::TYPE_ASYNC_REVALIDATION, result);
450 cache_->DeleteAsyncValidation(cache_->GenerateCacheKey(&request_));
451 // |this| is deleted.
454 //-----------------------------------------------------------------------------
455 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params,
456 BackendFactory* backend_factory)
457 : net_log_(params.net_log),
458 backend_factory_(backend_factory),
459 building_backend_(false),
460 bypass_lock_for_test_(false),
461 fail_conditionalization_for_test_(false),
462 use_stale_while_revalidate_(params.use_stale_while_revalidate),
463 mode_(NORMAL),
464 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))),
465 clock_(new base::DefaultClock()),
466 weak_factory_(this) {
467 SetupQuicServerInfoFactory(network_layer_->GetSession());
471 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
472 // |session| is shared.
473 HttpCache::HttpCache(HttpNetworkSession* session,
474 BackendFactory* backend_factory)
475 : net_log_(session->net_log()),
476 backend_factory_(backend_factory),
477 building_backend_(false),
478 bypass_lock_for_test_(false),
479 fail_conditionalization_for_test_(false),
480 use_stale_while_revalidate_(session->params().use_stale_while_revalidate),
481 mode_(NORMAL),
482 network_layer_(new HttpNetworkLayer(session)),
483 clock_(new base::DefaultClock()),
484 weak_factory_(this) {
487 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
488 NetLog* net_log,
489 BackendFactory* backend_factory)
490 : net_log_(net_log),
491 backend_factory_(backend_factory),
492 building_backend_(false),
493 bypass_lock_for_test_(false),
494 fail_conditionalization_for_test_(false),
495 use_stale_while_revalidate_(false),
496 mode_(NORMAL),
497 network_layer_(network_layer),
498 clock_(new base::DefaultClock()),
499 weak_factory_(this) {
500 SetupQuicServerInfoFactory(network_layer_->GetSession());
501 HttpNetworkSession* session = network_layer_->GetSession();
502 if (session)
503 use_stale_while_revalidate_ = session->params().use_stale_while_revalidate;
506 HttpCache::~HttpCache() {
507 // Transactions should see an invalid cache after this point; otherwise they
508 // could see an inconsistent object (half destroyed).
509 weak_factory_.InvalidateWeakPtrs();
511 // If we have any active entries remaining, then we need to deactivate them.
512 // We may have some pending calls to OnProcessPendingQueue, but since those
513 // won't run (due to our destruction), we can simply ignore the corresponding
514 // will_process_pending_queue flag.
515 while (!active_entries_.empty()) {
516 ActiveEntry* entry = active_entries_.begin()->second;
517 entry->will_process_pending_queue = false;
518 entry->pending_queue.clear();
519 entry->readers.clear();
520 entry->writer = NULL;
521 DeactivateEntry(entry);
524 STLDeleteElements(&doomed_entries_);
525 STLDeleteValues(&async_validations_);
527 // Before deleting pending_ops_, we have to make sure that the disk cache is
528 // done with said operations, or it will attempt to use deleted data.
529 cert_cache_.reset();
530 disk_cache_.reset();
532 PendingOpsMap::iterator pending_it = pending_ops_.begin();
533 for (; pending_it != pending_ops_.end(); ++pending_it) {
534 // We are not notifying the transactions about the cache going away, even
535 // though they are waiting for a callback that will never fire.
536 PendingOp* pending_op = pending_it->second;
537 delete pending_op->writer;
538 bool delete_pending_op = true;
539 if (building_backend_) {
540 // If we don't have a backend, when its construction finishes it will
541 // deliver the callbacks.
542 if (!pending_op->callback.is_null()) {
543 // If not null, the callback will delete the pending operation later.
544 delete_pending_op = false;
546 } else {
547 pending_op->callback.Reset();
550 STLDeleteElements(&pending_op->pending_queue);
551 if (delete_pending_op)
552 delete pending_op;
556 int HttpCache::GetBackend(disk_cache::Backend** backend,
557 const CompletionCallback& callback) {
558 DCHECK(!callback.is_null());
560 if (disk_cache_.get()) {
561 *backend = disk_cache_.get();
562 return OK;
565 return CreateBackend(backend, callback);
568 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
569 return disk_cache_.get();
572 // static
573 bool HttpCache::ParseResponseInfo(const char* data, int len,
574 HttpResponseInfo* response_info,
575 bool* response_truncated) {
576 Pickle pickle(data, len);
577 return response_info->InitFromPickle(pickle, response_truncated);
580 void HttpCache::WriteMetadata(const GURL& url,
581 RequestPriority priority,
582 base::Time expected_response_time,
583 IOBuffer* buf,
584 int buf_len) {
585 if (!buf_len)
586 return;
588 // Do lazy initialization of disk cache if needed.
589 if (!disk_cache_.get()) {
590 // We don't care about the result.
591 CreateBackend(NULL, net::CompletionCallback());
594 HttpCache::Transaction* trans =
595 new HttpCache::Transaction(priority, this);
596 MetadataWriter* writer = new MetadataWriter(trans);
598 // The writer will self destruct when done.
599 writer->Write(url, expected_response_time, buf, buf_len);
602 void HttpCache::CloseAllConnections() {
603 HttpNetworkSession* session = GetSession();
604 if (session)
605 session->CloseAllConnections();
608 void HttpCache::CloseIdleConnections() {
609 HttpNetworkSession* session = GetSession();
610 if (session)
611 session->CloseIdleConnections();
614 void HttpCache::OnExternalCacheHit(const GURL& url,
615 const std::string& http_method) {
616 if (!disk_cache_.get() || mode_ == DISABLE)
617 return;
619 HttpRequestInfo request_info;
620 request_info.url = url;
621 request_info.method = http_method;
622 std::string key = GenerateCacheKey(&request_info);
623 disk_cache_->OnExternalCacheHit(key);
626 void HttpCache::InitializeInfiniteCache(const base::FilePath& path) {
627 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
628 return;
629 base::WorkerPool::PostTask(FROM_HERE, base::Bind(&DeletePath, path), true);
632 int HttpCache::CreateTransaction(RequestPriority priority,
633 scoped_ptr<HttpTransaction>* trans) {
634 // Do lazy initialization of disk cache if needed.
635 if (!disk_cache_.get()) {
636 // We don't care about the result.
637 CreateBackend(NULL, net::CompletionCallback());
640 HttpCache::Transaction* transaction =
641 new HttpCache::Transaction(priority, this);
642 if (bypass_lock_for_test_)
643 transaction->BypassLockForTest();
644 if (fail_conditionalization_for_test_)
645 transaction->FailConditionalizationForTest();
647 trans->reset(transaction);
648 return OK;
651 HttpCache* HttpCache::GetCache() {
652 return this;
655 HttpNetworkSession* HttpCache::GetSession() {
656 return network_layer_->GetSession();
659 scoped_ptr<HttpTransactionFactory>
660 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
661 scoped_ptr<HttpTransactionFactory> new_network_layer) {
662 scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass());
663 network_layer_ = new_network_layer.Pass();
664 return old_network_layer.Pass();
667 //-----------------------------------------------------------------------------
669 int HttpCache::CreateBackend(disk_cache::Backend** backend,
670 const net::CompletionCallback& callback) {
671 if (!backend_factory_.get())
672 return ERR_FAILED;
674 building_backend_ = true;
676 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
677 backend));
679 // This is the only operation that we can do that is not related to any given
680 // entry, so we use an empty key for it.
681 PendingOp* pending_op = GetPendingOp(std::string());
682 if (pending_op->writer) {
683 if (!callback.is_null())
684 pending_op->pending_queue.push_back(item.release());
685 return ERR_IO_PENDING;
688 DCHECK(pending_op->pending_queue.empty());
690 pending_op->writer = item.release();
691 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
692 GetWeakPtr(), pending_op);
694 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
695 pending_op->callback);
696 if (rv != ERR_IO_PENDING) {
697 pending_op->writer->ClearCallback();
698 pending_op->callback.Run(rv);
701 return rv;
704 int HttpCache::GetBackendForTransaction(Transaction* trans) {
705 if (disk_cache_.get())
706 return OK;
708 if (!building_backend_)
709 return ERR_FAILED;
711 WorkItem* item = new WorkItem(
712 WI_CREATE_BACKEND, trans, net::CompletionCallback(), NULL);
713 PendingOp* pending_op = GetPendingOp(std::string());
714 DCHECK(pending_op->writer);
715 pending_op->pending_queue.push_back(item);
716 return ERR_IO_PENDING;
719 // Generate a key that can be used inside the cache.
720 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
721 // Strip out the reference, username, and password sections of the URL.
722 std::string url = HttpUtil::SpecForRequest(request->url);
724 DCHECK(mode_ != DISABLE);
725 if (mode_ == NORMAL) {
726 // No valid URL can begin with numerals, so we should not have to worry
727 // about collisions with normal URLs.
728 if (request->upload_data_stream &&
729 request->upload_data_stream->identifier()) {
730 url.insert(0, base::StringPrintf(
731 "%" PRId64 "/", request->upload_data_stream->identifier()));
733 return url;
736 // In playback and record mode, we cache everything.
738 // Lazily initialize.
739 if (playback_cache_map_ == NULL)
740 playback_cache_map_.reset(new PlaybackCacheMap());
742 // Each time we request an item from the cache, we tag it with a
743 // generation number. During playback, multiple fetches for the same
744 // item will use the same generation number and pull the proper
745 // instance of an URL from the cache.
746 int generation = 0;
747 DCHECK(playback_cache_map_ != NULL);
748 if (playback_cache_map_->find(url) != playback_cache_map_->end())
749 generation = (*playback_cache_map_)[url];
750 (*playback_cache_map_)[url] = generation + 1;
752 // The key into the cache is GENERATION # + METHOD + URL.
753 std::string result = base::IntToString(generation);
754 result.append(request->method);
755 result.append(url);
756 return result;
759 void HttpCache::DoomActiveEntry(const std::string& key) {
760 ActiveEntriesMap::iterator it = active_entries_.find(key);
761 if (it == active_entries_.end())
762 return;
764 // This is not a performance critical operation, this is handling an error
765 // condition so it is OK to look up the entry again.
766 int rv = DoomEntry(key, NULL);
767 DCHECK_EQ(OK, rv);
770 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
771 // Need to abandon the ActiveEntry, but any transaction attached to the entry
772 // should not be impacted. Dooming an entry only means that it will no
773 // longer be returned by FindActiveEntry (and it will also be destroyed once
774 // all consumers are finished with the entry).
775 ActiveEntriesMap::iterator it = active_entries_.find(key);
776 if (it == active_entries_.end()) {
777 DCHECK(trans);
778 return AsyncDoomEntry(key, trans);
781 ActiveEntry* entry = it->second;
782 active_entries_.erase(it);
784 // We keep track of doomed entries so that we can ensure that they are
785 // cleaned up properly when the cache is destroyed.
786 doomed_entries_.insert(entry);
788 entry->disk_entry->Doom();
789 entry->doomed = true;
791 DCHECK(entry->writer || !entry->readers.empty() ||
792 entry->will_process_pending_queue);
793 return OK;
796 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
797 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
798 PendingOp* pending_op = GetPendingOp(key);
799 if (pending_op->writer) {
800 pending_op->pending_queue.push_back(item);
801 return ERR_IO_PENDING;
804 DCHECK(pending_op->pending_queue.empty());
806 pending_op->writer = item;
807 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
808 GetWeakPtr(), pending_op);
810 int rv = disk_cache_->DoomEntry(key, pending_op->callback);
811 if (rv != ERR_IO_PENDING) {
812 item->ClearTransaction();
813 pending_op->callback.Run(rv);
816 return rv;
819 void HttpCache::DoomMainEntryForUrl(const GURL& url) {
820 if (!disk_cache_)
821 return;
823 HttpRequestInfo temp_info;
824 temp_info.url = url;
825 temp_info.method = "GET";
826 std::string key = GenerateCacheKey(&temp_info);
828 // Defer to DoomEntry if there is an active entry, otherwise call
829 // AsyncDoomEntry without triggering a callback.
830 if (active_entries_.count(key))
831 DoomEntry(key, NULL);
832 else
833 AsyncDoomEntry(key, NULL);
836 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
837 DCHECK(entry->doomed);
838 DCHECK(!entry->writer);
839 DCHECK(entry->readers.empty());
840 DCHECK(entry->pending_queue.empty());
842 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
843 DCHECK(it != doomed_entries_.end());
844 doomed_entries_.erase(it);
846 delete entry;
849 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
850 ActiveEntriesMap::const_iterator it = active_entries_.find(key);
851 return it != active_entries_.end() ? it->second : NULL;
854 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
855 disk_cache::Entry* disk_entry) {
856 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
857 ActiveEntry* entry = new ActiveEntry(disk_entry);
858 active_entries_[disk_entry->GetKey()] = entry;
859 return entry;
862 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
863 DCHECK(!entry->will_process_pending_queue);
864 DCHECK(!entry->doomed);
865 DCHECK(!entry->writer);
866 DCHECK(entry->disk_entry);
867 DCHECK(entry->readers.empty());
868 DCHECK(entry->pending_queue.empty());
870 std::string key = entry->disk_entry->GetKey();
871 if (key.empty())
872 return SlowDeactivateEntry(entry);
874 ActiveEntriesMap::iterator it = active_entries_.find(key);
875 DCHECK(it != active_entries_.end());
876 DCHECK(it->second == entry);
878 active_entries_.erase(it);
879 delete entry;
882 // We don't know this entry's key so we have to find it without it.
883 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
884 for (ActiveEntriesMap::iterator it = active_entries_.begin();
885 it != active_entries_.end(); ++it) {
886 if (it->second == entry) {
887 active_entries_.erase(it);
888 delete entry;
889 break;
894 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
895 DCHECK(!FindActiveEntry(key));
897 PendingOpsMap::const_iterator it = pending_ops_.find(key);
898 if (it != pending_ops_.end())
899 return it->second;
901 PendingOp* operation = new PendingOp();
902 pending_ops_[key] = operation;
903 return operation;
906 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
907 std::string key;
908 if (pending_op->disk_entry)
909 key = pending_op->disk_entry->GetKey();
911 if (!key.empty()) {
912 PendingOpsMap::iterator it = pending_ops_.find(key);
913 DCHECK(it != pending_ops_.end());
914 pending_ops_.erase(it);
915 } else {
916 for (PendingOpsMap::iterator it = pending_ops_.begin();
917 it != pending_ops_.end(); ++it) {
918 if (it->second == pending_op) {
919 pending_ops_.erase(it);
920 break;
924 DCHECK(pending_op->pending_queue.empty());
926 delete pending_op;
929 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
930 Transaction* trans) {
931 ActiveEntry* active_entry = FindActiveEntry(key);
932 if (active_entry) {
933 *entry = active_entry;
934 return OK;
937 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
938 PendingOp* pending_op = GetPendingOp(key);
939 if (pending_op->writer) {
940 pending_op->pending_queue.push_back(item);
941 return ERR_IO_PENDING;
944 DCHECK(pending_op->pending_queue.empty());
946 pending_op->writer = item;
947 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
948 GetWeakPtr(), pending_op);
950 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry),
951 pending_op->callback);
952 if (rv != ERR_IO_PENDING) {
953 item->ClearTransaction();
954 pending_op->callback.Run(rv);
957 return rv;
960 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
961 Transaction* trans) {
962 if (FindActiveEntry(key)) {
963 return ERR_CACHE_RACE;
966 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
967 PendingOp* pending_op = GetPendingOp(key);
968 if (pending_op->writer) {
969 pending_op->pending_queue.push_back(item);
970 return ERR_IO_PENDING;
973 DCHECK(pending_op->pending_queue.empty());
975 pending_op->writer = item;
976 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
977 GetWeakPtr(), pending_op);
979 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
980 pending_op->callback);
981 if (rv != ERR_IO_PENDING) {
982 item->ClearTransaction();
983 pending_op->callback.Run(rv);
986 return rv;
989 void HttpCache::DestroyEntry(ActiveEntry* entry) {
990 if (entry->doomed) {
991 FinalizeDoomedEntry(entry);
992 } else {
993 DeactivateEntry(entry);
997 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
998 DCHECK(entry);
999 DCHECK(entry->disk_entry);
1001 // We implement a basic reader/writer lock for the disk cache entry. If
1002 // there is already a writer, then everyone has to wait for the writer to
1003 // finish before they can access the cache entry. There can be multiple
1004 // readers.
1006 // NOTE: If the transaction can only write, then the entry should not be in
1007 // use (since any existing entry should have already been doomed).
1009 if (entry->writer || entry->will_process_pending_queue) {
1010 entry->pending_queue.push_back(trans);
1011 return ERR_IO_PENDING;
1014 if (trans->mode() & Transaction::WRITE) {
1015 // transaction needs exclusive access to the entry
1016 if (entry->readers.empty()) {
1017 entry->writer = trans;
1018 } else {
1019 entry->pending_queue.push_back(trans);
1020 return ERR_IO_PENDING;
1022 } else {
1023 // transaction needs read access to the entry
1024 entry->readers.push_back(trans);
1027 // We do this before calling EntryAvailable to force any further calls to
1028 // AddTransactionToEntry to add their transaction to the pending queue, which
1029 // ensures FIFO ordering.
1030 if (!entry->writer && !entry->pending_queue.empty())
1031 ProcessPendingQueue(entry);
1033 return OK;
1036 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
1037 bool cancel) {
1038 // If we already posted a task to move on to the next transaction and this was
1039 // the writer, there is nothing to cancel.
1040 if (entry->will_process_pending_queue && entry->readers.empty())
1041 return;
1043 if (entry->writer) {
1044 DCHECK(trans == entry->writer);
1046 // Assume there was a failure.
1047 bool success = false;
1048 if (cancel) {
1049 DCHECK(entry->disk_entry);
1050 // This is a successful operation in the sense that we want to keep the
1051 // entry.
1052 success = trans->AddTruncatedFlag();
1053 // The previous operation may have deleted the entry.
1054 if (!trans->entry())
1055 return;
1057 DoneWritingToEntry(entry, success);
1058 } else {
1059 DoneReadingFromEntry(entry, trans);
1063 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
1064 DCHECK(entry->readers.empty());
1066 entry->writer = NULL;
1068 if (success) {
1069 ProcessPendingQueue(entry);
1070 } else {
1071 DCHECK(!entry->will_process_pending_queue);
1073 // We failed to create this entry.
1074 TransactionList pending_queue;
1075 pending_queue.swap(entry->pending_queue);
1077 entry->disk_entry->Doom();
1078 DestroyEntry(entry);
1080 // We need to do something about these pending entries, which now need to
1081 // be added to a new entry.
1082 while (!pending_queue.empty()) {
1083 // ERR_CACHE_RACE causes the transaction to restart the whole process.
1084 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
1085 pending_queue.pop_front();
1090 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
1091 DCHECK(!entry->writer);
1093 TransactionList::iterator it =
1094 std::find(entry->readers.begin(), entry->readers.end(), trans);
1095 DCHECK(it != entry->readers.end());
1097 entry->readers.erase(it);
1099 ProcessPendingQueue(entry);
1102 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
1103 DCHECK(entry->writer);
1104 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
1105 DCHECK(entry->readers.empty());
1107 Transaction* trans = entry->writer;
1109 entry->writer = NULL;
1110 entry->readers.push_back(trans);
1112 ProcessPendingQueue(entry);
1115 LoadState HttpCache::GetLoadStateForPendingTransaction(
1116 const Transaction* trans) {
1117 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
1118 if (i == active_entries_.end()) {
1119 // If this is really a pending transaction, and it is not part of
1120 // active_entries_, we should be creating the backend or the entry.
1121 return LOAD_STATE_WAITING_FOR_CACHE;
1124 Transaction* writer = i->second->writer;
1125 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
1128 void HttpCache::RemovePendingTransaction(Transaction* trans) {
1129 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
1130 bool found = false;
1131 if (i != active_entries_.end())
1132 found = RemovePendingTransactionFromEntry(i->second, trans);
1134 if (found)
1135 return;
1137 if (building_backend_) {
1138 PendingOpsMap::const_iterator j = pending_ops_.find(std::string());
1139 if (j != pending_ops_.end())
1140 found = RemovePendingTransactionFromPendingOp(j->second, trans);
1142 if (found)
1143 return;
1146 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
1147 if (j != pending_ops_.end())
1148 found = RemovePendingTransactionFromPendingOp(j->second, trans);
1150 if (found)
1151 return;
1153 ActiveEntriesSet::iterator k = doomed_entries_.begin();
1154 for (; k != doomed_entries_.end() && !found; ++k)
1155 found = RemovePendingTransactionFromEntry(*k, trans);
1157 DCHECK(found) << "Pending transaction not found";
1160 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
1161 Transaction* trans) {
1162 TransactionList& pending_queue = entry->pending_queue;
1164 TransactionList::iterator j =
1165 find(pending_queue.begin(), pending_queue.end(), trans);
1166 if (j == pending_queue.end())
1167 return false;
1169 pending_queue.erase(j);
1170 return true;
1173 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
1174 Transaction* trans) {
1175 if (pending_op->writer->Matches(trans)) {
1176 pending_op->writer->ClearTransaction();
1177 pending_op->writer->ClearEntry();
1178 return true;
1180 WorkItemList& pending_queue = pending_op->pending_queue;
1182 WorkItemList::iterator it = pending_queue.begin();
1183 for (; it != pending_queue.end(); ++it) {
1184 if ((*it)->Matches(trans)) {
1185 delete *it;
1186 pending_queue.erase(it);
1187 return true;
1190 return false;
1193 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) {
1194 if (session &&
1195 !session->quic_stream_factory()->has_quic_server_info_factory()) {
1196 DCHECK(!quic_server_info_factory_);
1197 quic_server_info_factory_.reset(new QuicServerInfoFactoryAdaptor(this));
1198 session->quic_stream_factory()->set_quic_server_info_factory(
1199 quic_server_info_factory_.get());
1203 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
1204 // Multiple readers may finish with an entry at once, so we want to batch up
1205 // calls to OnProcessPendingQueue. This flag also tells us that we should
1206 // not delete the entry before OnProcessPendingQueue runs.
1207 if (entry->will_process_pending_queue)
1208 return;
1209 entry->will_process_pending_queue = true;
1211 base::MessageLoop::current()->PostTask(
1212 FROM_HERE,
1213 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1216 void HttpCache::PerformAsyncValidation(const HttpRequestInfo& original_request,
1217 const BoundNetLog& net_log) {
1218 DCHECK(use_stale_while_revalidate_);
1219 std::string key = GenerateCacheKey(&original_request);
1220 AsyncValidation* async_validation =
1221 new AsyncValidation(original_request, this);
1222 typedef AsyncValidationMap::value_type AsyncValidationKeyValue;
1223 bool insert_ok =
1224 async_validations_.insert(AsyncValidationKeyValue(key, async_validation))
1225 .second;
1226 if (!insert_ok) {
1227 DVLOG(1) << "Harmless race condition detected on URL "
1228 << original_request.url << "; discarding redundant revalidation.";
1229 delete async_validation;
1230 return;
1232 HttpNetworkSession* network_session = GetSession();
1233 NetworkDelegate* network_delegate = NULL;
1234 if (network_session)
1235 network_delegate = network_session->network_delegate();
1236 scoped_ptr<HttpTransaction> transaction;
1237 CreateTransaction(IDLE, &transaction);
1238 scoped_ptr<Transaction> downcast_transaction(
1239 static_cast<Transaction*>(transaction.release()));
1240 async_validation->Start(
1241 net_log, downcast_transaction.Pass(), network_delegate);
1242 // |async_validation| may have been deleted here.
1245 void HttpCache::DeleteAsyncValidation(const std::string& url) {
1246 AsyncValidationMap::iterator it = async_validations_.find(url);
1247 CHECK(it != async_validations_.end()); // security-critical invariant
1248 AsyncValidation* async_validation = it->second;
1249 async_validations_.erase(it);
1250 delete async_validation;
1253 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1254 entry->will_process_pending_queue = false;
1255 DCHECK(!entry->writer);
1257 // If no one is interested in this entry, then we can deactivate it.
1258 if (entry->pending_queue.empty()) {
1259 if (entry->readers.empty())
1260 DestroyEntry(entry);
1261 return;
1264 // Promote next transaction from the pending queue.
1265 Transaction* next = entry->pending_queue.front();
1266 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1267 return; // Have to wait.
1269 entry->pending_queue.erase(entry->pending_queue.begin());
1271 int rv = AddTransactionToEntry(entry, next);
1272 if (rv != ERR_IO_PENDING) {
1273 next->io_callback().Run(rv);
1277 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1278 WorkItemOperation op = pending_op->writer->operation();
1280 // Completing the creation of the backend is simpler than the other cases.
1281 if (op == WI_CREATE_BACKEND)
1282 return OnBackendCreated(result, pending_op);
1284 scoped_ptr<WorkItem> item(pending_op->writer);
1285 bool fail_requests = false;
1287 ActiveEntry* entry = NULL;
1288 std::string key;
1289 if (result == OK) {
1290 if (op == WI_DOOM_ENTRY) {
1291 // Anything after a Doom has to be restarted.
1292 fail_requests = true;
1293 } else if (item->IsValid()) {
1294 key = pending_op->disk_entry->GetKey();
1295 entry = ActivateEntry(pending_op->disk_entry);
1296 } else {
1297 // The writer transaction is gone.
1298 if (op == WI_CREATE_ENTRY)
1299 pending_op->disk_entry->Doom();
1300 pending_op->disk_entry->Close();
1301 pending_op->disk_entry = NULL;
1302 fail_requests = true;
1306 // We are about to notify a bunch of transactions, and they may decide to
1307 // re-issue a request (or send a different one). If we don't delete
1308 // pending_op, the new request will be appended to the end of the list, and
1309 // we'll see it again from this point before it has a chance to complete (and
1310 // we'll be messing out the request order). The down side is that if for some
1311 // reason notifying request A ends up cancelling request B (for the same key),
1312 // we won't find request B anywhere (because it would be in a local variable
1313 // here) and that's bad. If there is a chance for that to happen, we'll have
1314 // to move the callback used to be a CancelableCallback. By the way, for this
1315 // to happen the action (to cancel B) has to be synchronous to the
1316 // notification for request A.
1317 WorkItemList pending_items;
1318 pending_items.swap(pending_op->pending_queue);
1319 DeletePendingOp(pending_op);
1321 item->NotifyTransaction(result, entry);
1323 while (!pending_items.empty()) {
1324 item.reset(pending_items.front());
1325 pending_items.pop_front();
1327 if (item->operation() == WI_DOOM_ENTRY) {
1328 // A queued doom request is always a race.
1329 fail_requests = true;
1330 } else if (result == OK) {
1331 entry = FindActiveEntry(key);
1332 if (!entry)
1333 fail_requests = true;
1336 if (fail_requests) {
1337 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1338 continue;
1341 if (item->operation() == WI_CREATE_ENTRY) {
1342 if (result == OK) {
1343 // A second Create request, but the first request succeeded.
1344 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1345 } else {
1346 if (op != WI_CREATE_ENTRY) {
1347 // Failed Open followed by a Create.
1348 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1349 fail_requests = true;
1350 } else {
1351 item->NotifyTransaction(result, entry);
1354 } else {
1355 if (op == WI_CREATE_ENTRY && result != OK) {
1356 // Failed Create followed by an Open.
1357 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1358 fail_requests = true;
1359 } else {
1360 item->NotifyTransaction(result, entry);
1366 // static
1367 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
1368 PendingOp* pending_op,
1369 int rv) {
1370 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
1371 tracked_objects::ScopedTracker tracking_profile(
1372 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1373 "422516 HttpCache::OnPendingOpComplete"));
1375 if (cache.get()) {
1376 cache->OnIOComplete(rv, pending_op);
1377 } else {
1378 // The callback was cancelled so we should delete the pending_op that
1379 // was used with this callback.
1380 delete pending_op;
1384 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1385 scoped_ptr<WorkItem> item(pending_op->writer);
1386 WorkItemOperation op = item->operation();
1387 DCHECK_EQ(WI_CREATE_BACKEND, op);
1389 // We don't need the callback anymore.
1390 pending_op->callback.Reset();
1392 if (backend_factory_.get()) {
1393 // We may end up calling OnBackendCreated multiple times if we have pending
1394 // work items. The first call saves the backend and releases the factory,
1395 // and the last call clears building_backend_.
1396 backend_factory_.reset(); // Reclaim memory.
1397 if (result == OK) {
1398 disk_cache_ = pending_op->backend.Pass();
1399 if (UseCertCache())
1400 cert_cache_.reset(new DiskBasedCertCache(disk_cache_.get()));
1404 if (!pending_op->pending_queue.empty()) {
1405 WorkItem* pending_item = pending_op->pending_queue.front();
1406 pending_op->pending_queue.pop_front();
1407 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1409 // We want to process a single callback at a time, because the cache may
1410 // go away from the callback.
1411 pending_op->writer = pending_item;
1413 base::MessageLoop::current()->PostTask(
1414 FROM_HERE,
1415 base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(),
1416 result, pending_op));
1417 } else {
1418 building_backend_ = false;
1419 DeletePendingOp(pending_op);
1422 // The cache may be gone when we return from the callback.
1423 if (!item->DoCallback(result, disk_cache_.get()))
1424 item->NotifyTransaction(result, NULL);
1427 } // namespace net