Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / net / http / http_cache.cc
bloba34bef1158961bc78c29049ba89e0567990f99c1
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/time.h"
33 #include "net/base/cache_type.h"
34 #include "net/base/io_buffer.h"
35 #include "net/base/load_flags.h"
36 #include "net/base/net_errors.h"
37 #include "net/base/network_delegate.h"
38 #include "net/base/upload_data_stream.h"
39 #include "net/disk_cache/disk_cache.h"
40 #include "net/http/disk_based_cert_cache.h"
41 #include "net/http/disk_cache_based_quic_server_info.h"
42 #include "net/http/http_cache_transaction.h"
43 #include "net/http/http_network_layer.h"
44 #include "net/http/http_network_session.h"
45 #include "net/http/http_request_info.h"
46 #include "net/http/http_response_headers.h"
47 #include "net/http/http_response_info.h"
48 #include "net/http/http_util.h"
49 #include "net/quic/crypto/quic_server_info.h"
51 namespace {
53 bool UseCertCache() {
54 return base::FieldTrialList::FindFullName("CertCacheTrial") ==
55 "ExperimentGroup";
58 // Adaptor to delete a file on a worker thread.
59 void DeletePath(base::FilePath path) {
60 base::DeleteFile(path, false);
63 } // namespace
65 namespace net {
67 HttpCache::DefaultBackend::DefaultBackend(
68 CacheType type,
69 BackendType backend_type,
70 const base::FilePath& path,
71 int max_bytes,
72 const scoped_refptr<base::SingleThreadTaskRunner>& thread)
73 : type_(type),
74 backend_type_(backend_type),
75 path_(path),
76 max_bytes_(max_bytes),
77 thread_(thread) {
80 HttpCache::DefaultBackend::~DefaultBackend() {}
82 // static
83 HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) {
84 return new DefaultBackend(MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT,
85 base::FilePath(), max_bytes, NULL);
88 int HttpCache::DefaultBackend::CreateBackend(
89 NetLog* net_log, scoped_ptr<disk_cache::Backend>* backend,
90 const CompletionCallback& callback) {
91 DCHECK_GE(max_bytes_, 0);
92 return disk_cache::CreateCacheBackend(type_,
93 backend_type_,
94 path_,
95 max_bytes_,
96 true,
97 thread_,
98 net_log,
99 backend,
100 callback);
103 //-----------------------------------------------------------------------------
105 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
106 : disk_entry(entry),
107 writer(NULL),
108 will_process_pending_queue(false),
109 doomed(false) {
112 HttpCache::ActiveEntry::~ActiveEntry() {
113 if (disk_entry) {
114 disk_entry->Close();
115 disk_entry = NULL;
119 //-----------------------------------------------------------------------------
121 // This structure keeps track of work items that are attempting to create or
122 // open cache entries or the backend itself.
123 struct HttpCache::PendingOp {
124 PendingOp() : disk_entry(NULL), writer(NULL) {}
125 ~PendingOp() {}
127 disk_cache::Entry* disk_entry;
128 scoped_ptr<disk_cache::Backend> backend;
129 WorkItem* writer;
130 CompletionCallback callback; // BackendCallback.
131 WorkItemList pending_queue;
134 //-----------------------------------------------------------------------------
136 // The type of operation represented by a work item.
137 enum WorkItemOperation {
138 WI_CREATE_BACKEND,
139 WI_OPEN_ENTRY,
140 WI_CREATE_ENTRY,
141 WI_DOOM_ENTRY
144 // A work item encapsulates a single request to the backend with all the
145 // information needed to complete that request.
146 class HttpCache::WorkItem {
147 public:
148 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
149 : operation_(operation),
150 trans_(trans),
151 entry_(entry),
152 backend_(NULL) {}
153 WorkItem(WorkItemOperation operation, Transaction* trans,
154 const net::CompletionCallback& cb, disk_cache::Backend** backend)
155 : operation_(operation),
156 trans_(trans),
157 entry_(NULL),
158 callback_(cb),
159 backend_(backend) {}
160 ~WorkItem() {}
162 // Calls back the transaction with the result of the operation.
163 void NotifyTransaction(int result, ActiveEntry* entry) {
164 DCHECK(!entry || entry->disk_entry);
165 if (entry_)
166 *entry_ = entry;
167 if (trans_)
168 trans_->io_callback().Run(result);
171 // Notifies the caller about the operation completion. Returns true if the
172 // callback was invoked.
173 bool DoCallback(int result, disk_cache::Backend* backend) {
174 if (backend_)
175 *backend_ = backend;
176 if (!callback_.is_null()) {
177 callback_.Run(result);
178 return true;
180 return false;
183 WorkItemOperation operation() { return operation_; }
184 void ClearTransaction() { trans_ = NULL; }
185 void ClearEntry() { entry_ = NULL; }
186 void ClearCallback() { callback_.Reset(); }
187 bool Matches(Transaction* trans) const { return trans == trans_; }
188 bool IsValid() const { return trans_ || entry_ || !callback_.is_null(); }
190 private:
191 WorkItemOperation operation_;
192 Transaction* trans_;
193 ActiveEntry** entry_;
194 net::CompletionCallback callback_; // User callback.
195 disk_cache::Backend** backend_;
198 //-----------------------------------------------------------------------------
200 // This class encapsulates a transaction whose only purpose is to write metadata
201 // to a given entry.
202 class HttpCache::MetadataWriter {
203 public:
204 explicit MetadataWriter(HttpCache::Transaction* trans)
205 : transaction_(trans),
206 verified_(false),
207 buf_len_(0) {
210 ~MetadataWriter() {}
212 // Implements the bulk of HttpCache::WriteMetadata.
213 void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf,
214 int buf_len);
216 private:
217 void VerifyResponse(int result);
218 void SelfDestroy();
219 void OnIOComplete(int result);
221 scoped_ptr<HttpCache::Transaction> transaction_;
222 bool verified_;
223 scoped_refptr<IOBuffer> buf_;
224 int buf_len_;
225 base::Time expected_response_time_;
226 HttpRequestInfo request_info_;
227 DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
230 void HttpCache::MetadataWriter::Write(const GURL& url,
231 base::Time expected_response_time,
232 IOBuffer* buf, int buf_len) {
233 DCHECK_GT(buf_len, 0);
234 DCHECK(buf);
235 DCHECK(buf->data());
236 request_info_.url = url;
237 request_info_.method = "GET";
238 request_info_.load_flags = LOAD_ONLY_FROM_CACHE;
240 expected_response_time_ = expected_response_time;
241 buf_ = buf;
242 buf_len_ = buf_len;
243 verified_ = false;
245 int rv = transaction_->Start(
246 &request_info_,
247 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)),
248 BoundNetLog());
249 if (rv != ERR_IO_PENDING)
250 VerifyResponse(rv);
253 void HttpCache::MetadataWriter::VerifyResponse(int result) {
254 verified_ = true;
255 if (result != OK)
256 return SelfDestroy();
258 const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
259 DCHECK(response_info->was_cached);
260 if (response_info->response_time != expected_response_time_)
261 return SelfDestroy();
263 result = transaction_->WriteMetadata(
264 buf_.get(),
265 buf_len_,
266 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)));
267 if (result != ERR_IO_PENDING)
268 SelfDestroy();
271 void HttpCache::MetadataWriter::SelfDestroy() {
272 delete this;
275 void HttpCache::MetadataWriter::OnIOComplete(int result) {
276 if (!verified_)
277 return VerifyResponse(result);
278 SelfDestroy();
281 //-----------------------------------------------------------------------------
283 class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory {
284 public:
285 explicit QuicServerInfoFactoryAdaptor(HttpCache* http_cache)
286 : http_cache_(http_cache) {
289 QuicServerInfo* GetForServer(const QuicServerId& server_id) override {
290 return new DiskCacheBasedQuicServerInfo(server_id, http_cache_);
293 private:
294 HttpCache* const http_cache_;
297 //-----------------------------------------------------------------------------
299 class HttpCache::AsyncValidation {
300 public:
301 AsyncValidation(const HttpRequestInfo& original_request, HttpCache* cache)
302 : request_(original_request), cache_(cache) {}
303 ~AsyncValidation() {}
305 void Start(const BoundNetLog& net_log,
306 scoped_ptr<Transaction> transaction,
307 NetworkDelegate* network_delegate);
309 private:
310 void OnStarted(int result);
311 void DoRead();
312 void OnRead(int result);
314 // Terminate this request with net error code |result|. Logs the transaction
315 // result and asks HttpCache to delete this object.
316 // If there was a client or server certificate error, it cannot be recovered
317 // asynchronously, so we need to prevent future attempts to asynchronously
318 // fetch the resource. In this case, the cache entry is doomed.
319 void Terminate(int result);
321 HttpRequestInfo request_;
322 scoped_refptr<IOBuffer> buf_;
323 CompletionCallback read_callback_;
324 scoped_ptr<Transaction> transaction_;
325 base::Time start_time_;
327 // The HttpCache object owns this object. This object is always deleted before
328 // the pointer to the cache becomes invalid.
329 HttpCache* cache_;
331 DISALLOW_COPY_AND_ASSIGN(AsyncValidation);
334 void HttpCache::AsyncValidation::Start(const BoundNetLog& net_log,
335 scoped_ptr<Transaction> transaction,
336 NetworkDelegate* network_delegate) {
337 transaction_ = transaction.Pass();
338 if (network_delegate) {
339 // This code is necessary to enable async transactions to pass over the
340 // data-reduction proxy. This is a violation of the "once-and-only-once"
341 // principle, since it copies code from URLRequestHttpJob. We cannot use the
342 // original callback passed to HttpCache::Transaction by URLRequestHttpJob
343 // as it will only be valid as long as the URLRequestHttpJob object is
344 // alive, and that object will be deleted as soon as the synchronous request
345 // completes.
347 // This code is also an encapsulation violation. We are exploiting the fact
348 // that the |request| parameter to NotifyBeforeSendProxyHeaders() is never
349 // actually used for anything, and so can be NULL.
351 // TODO(ricea): Do this better.
352 transaction_->SetBeforeProxyHeadersSentCallback(
353 base::Bind(&NetworkDelegate::NotifyBeforeSendProxyHeaders,
354 base::Unretained(network_delegate),
355 static_cast<URLRequest*>(NULL)));
356 // The above use of base::Unretained is safe because the NetworkDelegate has
357 // to live at least as long as the HttpNetworkSession which has to live as
358 // least as long as the HttpNetworkLayer which has to live at least as long
359 // this HttpCache object.
362 DCHECK_EQ(0, request_.load_flags & LOAD_ASYNC_REVALIDATION);
363 request_.load_flags |= LOAD_ASYNC_REVALIDATION;
364 start_time_ = base::Time::Now();
365 // This use of base::Unretained is safe because |transaction_| is owned by
366 // this object.
367 read_callback_ = base::Bind(&AsyncValidation::OnRead, base::Unretained(this));
368 // This use of base::Unretained is safe as above.
369 int rv = transaction_->Start(
370 &request_,
371 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)),
372 net_log);
374 if (rv == ERR_IO_PENDING)
375 return;
377 OnStarted(rv);
380 void HttpCache::AsyncValidation::OnStarted(int result) {
381 if (result != OK) {
382 DVLOG(1) << "Asynchronous transaction start failed for " << request_.url;
383 Terminate(result);
384 return;
387 while (transaction_->IsReadyToRestartForAuth()) {
388 // This code is based on URLRequestHttpJob::RestartTransactionWithAuth,
389 // however when we do this here cookies on the response will not be
390 // stored. Fortunately only a tiny number of sites set cookies on 401
391 // responses, and none of them use stale-while-revalidate.
392 result = transaction_->RestartWithAuth(
393 AuthCredentials(),
394 base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)));
395 if (result == ERR_IO_PENDING)
396 return;
397 if (result != OK) {
398 DVLOG(1) << "Synchronous transaction restart with auth failed for "
399 << request_.url;
400 Terminate(result);
401 return;
405 DoRead();
408 void HttpCache::AsyncValidation::DoRead() {
409 const size_t kBufSize = 4096;
410 if (!buf_.get())
411 buf_ = new IOBuffer(kBufSize);
413 int rv = 0;
414 do {
415 rv = transaction_->Read(buf_.get(), kBufSize, read_callback_);
416 } while (rv > 0);
418 if (rv == ERR_IO_PENDING)
419 return;
421 OnRead(rv);
424 void HttpCache::AsyncValidation::OnRead(int result) {
425 if (result > 0) {
426 DoRead();
427 return;
429 Terminate(result);
432 void HttpCache::AsyncValidation::Terminate(int result) {
433 if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED || IsCertificateError(result)) {
434 // We should not attempt to access this resource asynchronously again until
435 // the certificate problem has been resolved.
436 // TODO(ricea): For ERR_SSL_CLIENT_AUTH_CERT_NEEDED, mark the entry as
437 // requiring synchronous revalidation rather than just deleting it. Other
438 // certificate errors cause the resource to be considered uncacheable
439 // anyway.
440 cache_->DoomEntry(transaction_->key(), transaction_.get());
442 base::TimeDelta duration = base::Time::Now() - start_time_;
443 UMA_HISTOGRAM_TIMES("HttpCache.AsyncValidationDuration", duration);
444 transaction_->net_log().EndEventWithNetErrorCode(
445 NetLog::TYPE_ASYNC_REVALIDATION, result);
446 cache_->DeleteAsyncValidation(cache_->GenerateCacheKey(&request_));
447 // |this| is deleted.
450 //-----------------------------------------------------------------------------
451 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params,
452 BackendFactory* backend_factory)
453 : net_log_(params.net_log),
454 backend_factory_(backend_factory),
455 building_backend_(false),
456 bypass_lock_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 weak_factory_(this) {
461 SetupQuicServerInfoFactory(network_layer_->GetSession());
465 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
466 // |session| is shared.
467 HttpCache::HttpCache(HttpNetworkSession* session,
468 BackendFactory* backend_factory)
469 : net_log_(session->net_log()),
470 backend_factory_(backend_factory),
471 building_backend_(false),
472 bypass_lock_for_test_(false),
473 use_stale_while_revalidate_(session->params().use_stale_while_revalidate),
474 mode_(NORMAL),
475 network_layer_(new HttpNetworkLayer(session)),
476 weak_factory_(this) {
479 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
480 NetLog* net_log,
481 BackendFactory* backend_factory)
482 : net_log_(net_log),
483 backend_factory_(backend_factory),
484 building_backend_(false),
485 bypass_lock_for_test_(false),
486 use_stale_while_revalidate_(false),
487 mode_(NORMAL),
488 network_layer_(network_layer),
489 weak_factory_(this) {
490 SetupQuicServerInfoFactory(network_layer_->GetSession());
491 HttpNetworkSession* session = network_layer_->GetSession();
492 if (session)
493 use_stale_while_revalidate_ = session->params().use_stale_while_revalidate;
496 HttpCache::~HttpCache() {
497 // Transactions should see an invalid cache after this point; otherwise they
498 // could see an inconsistent object (half destroyed).
499 weak_factory_.InvalidateWeakPtrs();
501 // If we have any active entries remaining, then we need to deactivate them.
502 // We may have some pending calls to OnProcessPendingQueue, but since those
503 // won't run (due to our destruction), we can simply ignore the corresponding
504 // will_process_pending_queue flag.
505 while (!active_entries_.empty()) {
506 ActiveEntry* entry = active_entries_.begin()->second;
507 entry->will_process_pending_queue = false;
508 entry->pending_queue.clear();
509 entry->readers.clear();
510 entry->writer = NULL;
511 DeactivateEntry(entry);
514 STLDeleteElements(&doomed_entries_);
515 STLDeleteValues(&async_validations_);
517 // Before deleting pending_ops_, we have to make sure that the disk cache is
518 // done with said operations, or it will attempt to use deleted data.
519 cert_cache_.reset();
520 disk_cache_.reset();
522 PendingOpsMap::iterator pending_it = pending_ops_.begin();
523 for (; pending_it != pending_ops_.end(); ++pending_it) {
524 // We are not notifying the transactions about the cache going away, even
525 // though they are waiting for a callback that will never fire.
526 PendingOp* pending_op = pending_it->second;
527 delete pending_op->writer;
528 bool delete_pending_op = true;
529 if (building_backend_) {
530 // If we don't have a backend, when its construction finishes it will
531 // deliver the callbacks.
532 if (!pending_op->callback.is_null()) {
533 // If not null, the callback will delete the pending operation later.
534 delete_pending_op = false;
536 } else {
537 pending_op->callback.Reset();
540 STLDeleteElements(&pending_op->pending_queue);
541 if (delete_pending_op)
542 delete pending_op;
546 int HttpCache::GetBackend(disk_cache::Backend** backend,
547 const CompletionCallback& callback) {
548 DCHECK(!callback.is_null());
550 if (disk_cache_.get()) {
551 *backend = disk_cache_.get();
552 return OK;
555 return CreateBackend(backend, callback);
558 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
559 return disk_cache_.get();
562 // static
563 bool HttpCache::ParseResponseInfo(const char* data, int len,
564 HttpResponseInfo* response_info,
565 bool* response_truncated) {
566 Pickle pickle(data, len);
567 return response_info->InitFromPickle(pickle, response_truncated);
570 void HttpCache::WriteMetadata(const GURL& url,
571 RequestPriority priority,
572 base::Time expected_response_time,
573 IOBuffer* buf,
574 int buf_len) {
575 if (!buf_len)
576 return;
578 // Do lazy initialization of disk cache if needed.
579 if (!disk_cache_.get()) {
580 // We don't care about the result.
581 CreateBackend(NULL, net::CompletionCallback());
584 HttpCache::Transaction* trans =
585 new HttpCache::Transaction(priority, this);
586 MetadataWriter* writer = new MetadataWriter(trans);
588 // The writer will self destruct when done.
589 writer->Write(url, expected_response_time, buf, buf_len);
592 void HttpCache::CloseAllConnections() {
593 HttpNetworkSession* session = GetSession();
594 if (session)
595 session->CloseAllConnections();
598 void HttpCache::CloseIdleConnections() {
599 HttpNetworkSession* session = GetSession();
600 if (session)
601 session->CloseIdleConnections();
604 void HttpCache::OnExternalCacheHit(const GURL& url,
605 const std::string& http_method) {
606 if (!disk_cache_.get() || mode_ == DISABLE)
607 return;
609 HttpRequestInfo request_info;
610 request_info.url = url;
611 request_info.method = http_method;
612 std::string key = GenerateCacheKey(&request_info);
613 disk_cache_->OnExternalCacheHit(key);
616 void HttpCache::InitializeInfiniteCache(const base::FilePath& path) {
617 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
618 return;
619 base::WorkerPool::PostTask(FROM_HERE, base::Bind(&DeletePath, path), true);
622 int HttpCache::CreateTransaction(RequestPriority priority,
623 scoped_ptr<HttpTransaction>* trans) {
624 // Do lazy initialization of disk cache if needed.
625 if (!disk_cache_.get()) {
626 // We don't care about the result.
627 CreateBackend(NULL, net::CompletionCallback());
630 HttpCache::Transaction* transaction =
631 new HttpCache::Transaction(priority, this);
632 if (bypass_lock_for_test_)
633 transaction->BypassLockForTest();
635 trans->reset(transaction);
636 return OK;
639 HttpCache* HttpCache::GetCache() {
640 return this;
643 HttpNetworkSession* HttpCache::GetSession() {
644 return network_layer_->GetSession();
647 scoped_ptr<HttpTransactionFactory>
648 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
649 scoped_ptr<HttpTransactionFactory> new_network_layer) {
650 scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass());
651 network_layer_ = new_network_layer.Pass();
652 return old_network_layer.Pass();
655 //-----------------------------------------------------------------------------
657 int HttpCache::CreateBackend(disk_cache::Backend** backend,
658 const net::CompletionCallback& callback) {
659 if (!backend_factory_.get())
660 return ERR_FAILED;
662 building_backend_ = true;
664 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
665 backend));
667 // This is the only operation that we can do that is not related to any given
668 // entry, so we use an empty key for it.
669 PendingOp* pending_op = GetPendingOp(std::string());
670 if (pending_op->writer) {
671 if (!callback.is_null())
672 pending_op->pending_queue.push_back(item.release());
673 return ERR_IO_PENDING;
676 DCHECK(pending_op->pending_queue.empty());
678 pending_op->writer = item.release();
679 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
680 GetWeakPtr(), pending_op);
682 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
683 pending_op->callback);
684 if (rv != ERR_IO_PENDING) {
685 pending_op->writer->ClearCallback();
686 pending_op->callback.Run(rv);
689 return rv;
692 int HttpCache::GetBackendForTransaction(Transaction* trans) {
693 if (disk_cache_.get())
694 return OK;
696 if (!building_backend_)
697 return ERR_FAILED;
699 WorkItem* item = new WorkItem(
700 WI_CREATE_BACKEND, trans, net::CompletionCallback(), NULL);
701 PendingOp* pending_op = GetPendingOp(std::string());
702 DCHECK(pending_op->writer);
703 pending_op->pending_queue.push_back(item);
704 return ERR_IO_PENDING;
707 // Generate a key that can be used inside the cache.
708 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
709 // Strip out the reference, username, and password sections of the URL.
710 std::string url = HttpUtil::SpecForRequest(request->url);
712 DCHECK(mode_ != DISABLE);
713 if (mode_ == NORMAL) {
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, base::StringPrintf(
719 "%" PRId64 "/", request->upload_data_stream->identifier()));
721 return url;
724 // In playback and record mode, we cache everything.
726 // Lazily initialize.
727 if (playback_cache_map_ == NULL)
728 playback_cache_map_.reset(new PlaybackCacheMap());
730 // Each time we request an item from the cache, we tag it with a
731 // generation number. During playback, multiple fetches for the same
732 // item will use the same generation number and pull the proper
733 // instance of an URL from the cache.
734 int generation = 0;
735 DCHECK(playback_cache_map_ != NULL);
736 if (playback_cache_map_->find(url) != playback_cache_map_->end())
737 generation = (*playback_cache_map_)[url];
738 (*playback_cache_map_)[url] = generation + 1;
740 // The key into the cache is GENERATION # + METHOD + URL.
741 std::string result = base::IntToString(generation);
742 result.append(request->method);
743 result.append(url);
744 return result;
747 void HttpCache::DoomActiveEntry(const std::string& key) {
748 ActiveEntriesMap::iterator it = active_entries_.find(key);
749 if (it == active_entries_.end())
750 return;
752 // This is not a performance critical operation, this is handling an error
753 // condition so it is OK to look up the entry again.
754 int rv = DoomEntry(key, NULL);
755 DCHECK_EQ(OK, rv);
758 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
759 // Need to abandon the ActiveEntry, but any transaction attached to the entry
760 // should not be impacted. Dooming an entry only means that it will no
761 // longer be returned by FindActiveEntry (and it will also be destroyed once
762 // all consumers are finished with the entry).
763 ActiveEntriesMap::iterator it = active_entries_.find(key);
764 if (it == active_entries_.end()) {
765 DCHECK(trans);
766 return AsyncDoomEntry(key, trans);
769 ActiveEntry* entry = it->second;
770 active_entries_.erase(it);
772 // We keep track of doomed entries so that we can ensure that they are
773 // cleaned up properly when the cache is destroyed.
774 doomed_entries_.insert(entry);
776 entry->disk_entry->Doom();
777 entry->doomed = true;
779 DCHECK(entry->writer || !entry->readers.empty() ||
780 entry->will_process_pending_queue);
781 return OK;
784 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
785 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
786 PendingOp* pending_op = GetPendingOp(key);
787 if (pending_op->writer) {
788 pending_op->pending_queue.push_back(item);
789 return ERR_IO_PENDING;
792 DCHECK(pending_op->pending_queue.empty());
794 pending_op->writer = item;
795 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
796 GetWeakPtr(), pending_op);
798 int rv = disk_cache_->DoomEntry(key, pending_op->callback);
799 if (rv != ERR_IO_PENDING) {
800 item->ClearTransaction();
801 pending_op->callback.Run(rv);
804 return rv;
807 void HttpCache::DoomMainEntryForUrl(const GURL& url) {
808 if (!disk_cache_)
809 return;
811 HttpRequestInfo temp_info;
812 temp_info.url = url;
813 temp_info.method = "GET";
814 std::string key = GenerateCacheKey(&temp_info);
816 // Defer to DoomEntry if there is an active entry, otherwise call
817 // AsyncDoomEntry without triggering a callback.
818 if (active_entries_.count(key))
819 DoomEntry(key, NULL);
820 else
821 AsyncDoomEntry(key, NULL);
824 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
825 DCHECK(entry->doomed);
826 DCHECK(!entry->writer);
827 DCHECK(entry->readers.empty());
828 DCHECK(entry->pending_queue.empty());
830 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
831 DCHECK(it != doomed_entries_.end());
832 doomed_entries_.erase(it);
834 delete entry;
837 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
838 ActiveEntriesMap::const_iterator it = active_entries_.find(key);
839 return it != active_entries_.end() ? it->second : NULL;
842 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
843 disk_cache::Entry* disk_entry) {
844 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
845 ActiveEntry* entry = new ActiveEntry(disk_entry);
846 active_entries_[disk_entry->GetKey()] = entry;
847 return entry;
850 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
851 DCHECK(!entry->will_process_pending_queue);
852 DCHECK(!entry->doomed);
853 DCHECK(!entry->writer);
854 DCHECK(entry->disk_entry);
855 DCHECK(entry->readers.empty());
856 DCHECK(entry->pending_queue.empty());
858 std::string key = entry->disk_entry->GetKey();
859 if (key.empty())
860 return SlowDeactivateEntry(entry);
862 ActiveEntriesMap::iterator it = active_entries_.find(key);
863 DCHECK(it != active_entries_.end());
864 DCHECK(it->second == entry);
866 active_entries_.erase(it);
867 delete entry;
870 // We don't know this entry's key so we have to find it without it.
871 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
872 for (ActiveEntriesMap::iterator it = active_entries_.begin();
873 it != active_entries_.end(); ++it) {
874 if (it->second == entry) {
875 active_entries_.erase(it);
876 delete entry;
877 break;
882 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
883 DCHECK(!FindActiveEntry(key));
885 PendingOpsMap::const_iterator it = pending_ops_.find(key);
886 if (it != pending_ops_.end())
887 return it->second;
889 PendingOp* operation = new PendingOp();
890 pending_ops_[key] = operation;
891 return operation;
894 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
895 std::string key;
896 if (pending_op->disk_entry)
897 key = pending_op->disk_entry->GetKey();
899 if (!key.empty()) {
900 PendingOpsMap::iterator it = pending_ops_.find(key);
901 DCHECK(it != pending_ops_.end());
902 pending_ops_.erase(it);
903 } else {
904 for (PendingOpsMap::iterator it = pending_ops_.begin();
905 it != pending_ops_.end(); ++it) {
906 if (it->second == pending_op) {
907 pending_ops_.erase(it);
908 break;
912 DCHECK(pending_op->pending_queue.empty());
914 delete pending_op;
917 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
918 Transaction* trans) {
919 ActiveEntry* active_entry = FindActiveEntry(key);
920 if (active_entry) {
921 *entry = active_entry;
922 return OK;
925 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
926 PendingOp* pending_op = GetPendingOp(key);
927 if (pending_op->writer) {
928 pending_op->pending_queue.push_back(item);
929 return ERR_IO_PENDING;
932 DCHECK(pending_op->pending_queue.empty());
934 pending_op->writer = item;
935 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
936 GetWeakPtr(), pending_op);
938 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry),
939 pending_op->callback);
940 if (rv != ERR_IO_PENDING) {
941 item->ClearTransaction();
942 pending_op->callback.Run(rv);
945 return rv;
948 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
949 Transaction* trans) {
950 if (FindActiveEntry(key)) {
951 return ERR_CACHE_RACE;
954 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
955 PendingOp* pending_op = GetPendingOp(key);
956 if (pending_op->writer) {
957 pending_op->pending_queue.push_back(item);
958 return ERR_IO_PENDING;
961 DCHECK(pending_op->pending_queue.empty());
963 pending_op->writer = item;
964 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
965 GetWeakPtr(), pending_op);
967 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
968 pending_op->callback);
969 if (rv != ERR_IO_PENDING) {
970 item->ClearTransaction();
971 pending_op->callback.Run(rv);
974 return rv;
977 void HttpCache::DestroyEntry(ActiveEntry* entry) {
978 if (entry->doomed) {
979 FinalizeDoomedEntry(entry);
980 } else {
981 DeactivateEntry(entry);
985 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
986 DCHECK(entry);
987 DCHECK(entry->disk_entry);
989 // We implement a basic reader/writer lock for the disk cache entry. If
990 // there is already a writer, then everyone has to wait for the writer to
991 // finish before they can access the cache entry. There can be multiple
992 // readers.
994 // NOTE: If the transaction can only write, then the entry should not be in
995 // use (since any existing entry should have already been doomed).
997 if (entry->writer || entry->will_process_pending_queue) {
998 entry->pending_queue.push_back(trans);
999 return ERR_IO_PENDING;
1002 if (trans->mode() & Transaction::WRITE) {
1003 // transaction needs exclusive access to the entry
1004 if (entry->readers.empty()) {
1005 entry->writer = trans;
1006 } else {
1007 entry->pending_queue.push_back(trans);
1008 return ERR_IO_PENDING;
1010 } else {
1011 // transaction needs read access to the entry
1012 entry->readers.push_back(trans);
1015 // We do this before calling EntryAvailable to force any further calls to
1016 // AddTransactionToEntry to add their transaction to the pending queue, which
1017 // ensures FIFO ordering.
1018 if (!entry->writer && !entry->pending_queue.empty())
1019 ProcessPendingQueue(entry);
1021 return OK;
1024 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
1025 bool cancel) {
1026 // If we already posted a task to move on to the next transaction and this was
1027 // the writer, there is nothing to cancel.
1028 if (entry->will_process_pending_queue && entry->readers.empty())
1029 return;
1031 if (entry->writer) {
1032 DCHECK(trans == entry->writer);
1034 // Assume there was a failure.
1035 bool success = false;
1036 if (cancel) {
1037 DCHECK(entry->disk_entry);
1038 // This is a successful operation in the sense that we want to keep the
1039 // entry.
1040 success = trans->AddTruncatedFlag();
1041 // The previous operation may have deleted the entry.
1042 if (!trans->entry())
1043 return;
1045 DoneWritingToEntry(entry, success);
1046 } else {
1047 DoneReadingFromEntry(entry, trans);
1051 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
1052 DCHECK(entry->readers.empty());
1054 entry->writer = NULL;
1056 if (success) {
1057 ProcessPendingQueue(entry);
1058 } else {
1059 DCHECK(!entry->will_process_pending_queue);
1061 // We failed to create this entry.
1062 TransactionList pending_queue;
1063 pending_queue.swap(entry->pending_queue);
1065 entry->disk_entry->Doom();
1066 DestroyEntry(entry);
1068 // We need to do something about these pending entries, which now need to
1069 // be added to a new entry.
1070 while (!pending_queue.empty()) {
1071 // ERR_CACHE_RACE causes the transaction to restart the whole process.
1072 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
1073 pending_queue.pop_front();
1078 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
1079 DCHECK(!entry->writer);
1081 TransactionList::iterator it =
1082 std::find(entry->readers.begin(), entry->readers.end(), trans);
1083 DCHECK(it != entry->readers.end());
1085 entry->readers.erase(it);
1087 ProcessPendingQueue(entry);
1090 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
1091 DCHECK(entry->writer);
1092 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
1093 DCHECK(entry->readers.empty());
1095 Transaction* trans = entry->writer;
1097 entry->writer = NULL;
1098 entry->readers.push_back(trans);
1100 ProcessPendingQueue(entry);
1103 LoadState HttpCache::GetLoadStateForPendingTransaction(
1104 const Transaction* trans) {
1105 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
1106 if (i == active_entries_.end()) {
1107 // If this is really a pending transaction, and it is not part of
1108 // active_entries_, we should be creating the backend or the entry.
1109 return LOAD_STATE_WAITING_FOR_CACHE;
1112 Transaction* writer = i->second->writer;
1113 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
1116 void HttpCache::RemovePendingTransaction(Transaction* trans) {
1117 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
1118 bool found = false;
1119 if (i != active_entries_.end())
1120 found = RemovePendingTransactionFromEntry(i->second, trans);
1122 if (found)
1123 return;
1125 if (building_backend_) {
1126 PendingOpsMap::const_iterator j = pending_ops_.find(std::string());
1127 if (j != pending_ops_.end())
1128 found = RemovePendingTransactionFromPendingOp(j->second, trans);
1130 if (found)
1131 return;
1134 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
1135 if (j != pending_ops_.end())
1136 found = RemovePendingTransactionFromPendingOp(j->second, trans);
1138 if (found)
1139 return;
1141 ActiveEntriesSet::iterator k = doomed_entries_.begin();
1142 for (; k != doomed_entries_.end() && !found; ++k)
1143 found = RemovePendingTransactionFromEntry(*k, trans);
1145 DCHECK(found) << "Pending transaction not found";
1148 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
1149 Transaction* trans) {
1150 TransactionList& pending_queue = entry->pending_queue;
1152 TransactionList::iterator j =
1153 find(pending_queue.begin(), pending_queue.end(), trans);
1154 if (j == pending_queue.end())
1155 return false;
1157 pending_queue.erase(j);
1158 return true;
1161 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
1162 Transaction* trans) {
1163 if (pending_op->writer->Matches(trans)) {
1164 pending_op->writer->ClearTransaction();
1165 pending_op->writer->ClearEntry();
1166 return true;
1168 WorkItemList& pending_queue = pending_op->pending_queue;
1170 WorkItemList::iterator it = pending_queue.begin();
1171 for (; it != pending_queue.end(); ++it) {
1172 if ((*it)->Matches(trans)) {
1173 delete *it;
1174 pending_queue.erase(it);
1175 return true;
1178 return false;
1181 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) {
1182 if (session &&
1183 !session->quic_stream_factory()->has_quic_server_info_factory()) {
1184 DCHECK(!quic_server_info_factory_);
1185 quic_server_info_factory_.reset(new QuicServerInfoFactoryAdaptor(this));
1186 session->quic_stream_factory()->set_quic_server_info_factory(
1187 quic_server_info_factory_.get());
1191 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
1192 // Multiple readers may finish with an entry at once, so we want to batch up
1193 // calls to OnProcessPendingQueue. This flag also tells us that we should
1194 // not delete the entry before OnProcessPendingQueue runs.
1195 if (entry->will_process_pending_queue)
1196 return;
1197 entry->will_process_pending_queue = true;
1199 base::MessageLoop::current()->PostTask(
1200 FROM_HERE,
1201 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1204 void HttpCache::PerformAsyncValidation(const HttpRequestInfo& original_request,
1205 const BoundNetLog& net_log) {
1206 DCHECK(use_stale_while_revalidate_);
1207 std::string key = GenerateCacheKey(&original_request);
1208 AsyncValidation* async_validation =
1209 new AsyncValidation(original_request, this);
1210 typedef AsyncValidationMap::value_type AsyncValidationKeyValue;
1211 bool insert_ok =
1212 async_validations_.insert(AsyncValidationKeyValue(key, async_validation))
1213 .second;
1214 if (!insert_ok) {
1215 DVLOG(1) << "Harmless race condition detected on URL "
1216 << original_request.url << "; discarding redundant revalidation.";
1217 delete async_validation;
1218 return;
1220 HttpNetworkSession* network_session = GetSession();
1221 NetworkDelegate* network_delegate = NULL;
1222 if (network_session)
1223 network_delegate = network_session->network_delegate();
1224 scoped_ptr<HttpTransaction> transaction;
1225 CreateTransaction(IDLE, &transaction);
1226 scoped_ptr<Transaction> downcast_transaction(
1227 static_cast<Transaction*>(transaction.release()));
1228 async_validation->Start(
1229 net_log, downcast_transaction.Pass(), network_delegate);
1230 // |async_validation| may have been deleted here.
1233 void HttpCache::DeleteAsyncValidation(const std::string& url) {
1234 AsyncValidationMap::iterator it = async_validations_.find(url);
1235 CHECK(it != async_validations_.end()); // security-critical invariant
1236 AsyncValidation* async_validation = it->second;
1237 async_validations_.erase(it);
1238 delete async_validation;
1241 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1242 entry->will_process_pending_queue = false;
1243 DCHECK(!entry->writer);
1245 // If no one is interested in this entry, then we can deactivate it.
1246 if (entry->pending_queue.empty()) {
1247 if (entry->readers.empty())
1248 DestroyEntry(entry);
1249 return;
1252 // Promote next transaction from the pending queue.
1253 Transaction* next = entry->pending_queue.front();
1254 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1255 return; // Have to wait.
1257 entry->pending_queue.erase(entry->pending_queue.begin());
1259 int rv = AddTransactionToEntry(entry, next);
1260 if (rv != ERR_IO_PENDING) {
1261 next->io_callback().Run(rv);
1265 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1266 WorkItemOperation op = pending_op->writer->operation();
1268 // Completing the creation of the backend is simpler than the other cases.
1269 if (op == WI_CREATE_BACKEND)
1270 return OnBackendCreated(result, pending_op);
1272 scoped_ptr<WorkItem> item(pending_op->writer);
1273 bool fail_requests = false;
1275 ActiveEntry* entry = NULL;
1276 std::string key;
1277 if (result == OK) {
1278 if (op == WI_DOOM_ENTRY) {
1279 // Anything after a Doom has to be restarted.
1280 fail_requests = true;
1281 } else if (item->IsValid()) {
1282 key = pending_op->disk_entry->GetKey();
1283 entry = ActivateEntry(pending_op->disk_entry);
1284 } else {
1285 // The writer transaction is gone.
1286 if (op == WI_CREATE_ENTRY)
1287 pending_op->disk_entry->Doom();
1288 pending_op->disk_entry->Close();
1289 pending_op->disk_entry = NULL;
1290 fail_requests = true;
1294 // We are about to notify a bunch of transactions, and they may decide to
1295 // re-issue a request (or send a different one). If we don't delete
1296 // pending_op, the new request will be appended to the end of the list, and
1297 // we'll see it again from this point before it has a chance to complete (and
1298 // we'll be messing out the request order). The down side is that if for some
1299 // reason notifying request A ends up cancelling request B (for the same key),
1300 // we won't find request B anywhere (because it would be in a local variable
1301 // here) and that's bad. If there is a chance for that to happen, we'll have
1302 // to move the callback used to be a CancelableCallback. By the way, for this
1303 // to happen the action (to cancel B) has to be synchronous to the
1304 // notification for request A.
1305 WorkItemList pending_items;
1306 pending_items.swap(pending_op->pending_queue);
1307 DeletePendingOp(pending_op);
1309 item->NotifyTransaction(result, entry);
1311 while (!pending_items.empty()) {
1312 item.reset(pending_items.front());
1313 pending_items.pop_front();
1315 if (item->operation() == WI_DOOM_ENTRY) {
1316 // A queued doom request is always a race.
1317 fail_requests = true;
1318 } else if (result == OK) {
1319 entry = FindActiveEntry(key);
1320 if (!entry)
1321 fail_requests = true;
1324 if (fail_requests) {
1325 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1326 continue;
1329 if (item->operation() == WI_CREATE_ENTRY) {
1330 if (result == OK) {
1331 // A second Create request, but the first request succeeded.
1332 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1333 } else {
1334 if (op != WI_CREATE_ENTRY) {
1335 // Failed Open followed by a Create.
1336 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1337 fail_requests = true;
1338 } else {
1339 item->NotifyTransaction(result, entry);
1342 } else {
1343 if (op == WI_CREATE_ENTRY && result != OK) {
1344 // Failed Create followed by an Open.
1345 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1346 fail_requests = true;
1347 } else {
1348 item->NotifyTransaction(result, entry);
1354 // static
1355 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
1356 PendingOp* pending_op,
1357 int rv) {
1358 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
1359 tracked_objects::ScopedTracker tracking_profile(
1360 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1361 "422516 HttpCache::OnPendingOpComplete"));
1363 if (cache.get()) {
1364 cache->OnIOComplete(rv, pending_op);
1365 } else {
1366 // The callback was cancelled so we should delete the pending_op that
1367 // was used with this callback.
1368 delete pending_op;
1372 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1373 scoped_ptr<WorkItem> item(pending_op->writer);
1374 WorkItemOperation op = item->operation();
1375 DCHECK_EQ(WI_CREATE_BACKEND, op);
1377 // We don't need the callback anymore.
1378 pending_op->callback.Reset();
1380 if (backend_factory_.get()) {
1381 // We may end up calling OnBackendCreated multiple times if we have pending
1382 // work items. The first call saves the backend and releases the factory,
1383 // and the last call clears building_backend_.
1384 backend_factory_.reset(); // Reclaim memory.
1385 if (result == OK) {
1386 disk_cache_ = pending_op->backend.Pass();
1387 if (UseCertCache())
1388 cert_cache_.reset(new DiskBasedCertCache(disk_cache_.get()));
1392 if (!pending_op->pending_queue.empty()) {
1393 WorkItem* pending_item = pending_op->pending_queue.front();
1394 pending_op->pending_queue.pop_front();
1395 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1397 // We want to process a single callback at a time, because the cache may
1398 // go away from the callback.
1399 pending_op->writer = pending_item;
1401 base::MessageLoop::current()->PostTask(
1402 FROM_HERE,
1403 base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(),
1404 result, pending_op));
1405 } else {
1406 building_backend_ = false;
1407 DeletePendingOp(pending_op);
1410 // The cache may be gone when we return from the callback.
1411 if (!item->DoCallback(result, disk_cache_.get()))
1412 item->NotifyTransaction(result, NULL);
1415 } // namespace net