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"
9 #include "base/compiler_specific.h"
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"
55 return base::FieldTrialList::FindFullName("CertCacheTrial") ==
59 // Adaptor to delete a file on a worker thread.
60 void DeletePath(base::FilePath path
) {
61 base::DeleteFile(path
, false);
68 HttpCache::DefaultBackend::DefaultBackend(
70 BackendType backend_type
,
71 const base::FilePath
& path
,
73 const scoped_refptr
<base::SingleThreadTaskRunner
>& thread
)
75 backend_type_(backend_type
),
77 max_bytes_(max_bytes
),
81 HttpCache::DefaultBackend::~DefaultBackend() {}
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_
,
104 //-----------------------------------------------------------------------------
106 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry
* entry
)
109 will_process_pending_queue(false),
113 HttpCache::ActiveEntry::~ActiveEntry() {
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
) {}
128 disk_cache::Entry
* disk_entry
;
129 scoped_ptr
<disk_cache::Backend
> backend
;
131 CompletionCallback callback
; // BackendCallback.
132 WorkItemList pending_queue
;
135 //-----------------------------------------------------------------------------
137 // The type of operation represented by a work item.
138 enum WorkItemOperation
{
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
{
149 WorkItem(WorkItemOperation operation
, Transaction
* trans
, ActiveEntry
** entry
)
150 : operation_(operation
),
154 WorkItem(WorkItemOperation operation
, Transaction
* trans
,
155 const net::CompletionCallback
& cb
, disk_cache::Backend
** backend
)
156 : operation_(operation
),
163 // Calls back the transaction with the result of the operation.
164 void NotifyTransaction(int result
, ActiveEntry
* entry
) {
165 DCHECK(!entry
|| entry
->disk_entry
);
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
) {
177 if (!callback_
.is_null()) {
178 callback_
.Run(result
);
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(); }
192 WorkItemOperation operation_
;
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
203 class HttpCache::MetadataWriter
{
205 explicit MetadataWriter(HttpCache::Transaction
* trans
)
206 : transaction_(trans
),
213 // Implements the bulk of HttpCache::WriteMetadata.
214 void Write(const GURL
& url
,
215 base::Time expected_response_time
,
220 void VerifyResponse(int result
);
222 void OnIOComplete(int result
);
224 scoped_ptr
<HttpCache::Transaction
> transaction_
;
226 scoped_refptr
<IOBuffer
> buf_
;
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
,
237 DCHECK_GT(buf_len
, 0);
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
;
249 int rv
= transaction_
->Start(
251 base::Bind(&MetadataWriter::OnIOComplete
, base::Unretained(this)),
253 if (rv
!= ERR_IO_PENDING
)
257 void HttpCache::MetadataWriter::VerifyResponse(int result
) {
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(
270 base::Bind(&MetadataWriter::OnIOComplete
, base::Unretained(this)));
271 if (result
!= ERR_IO_PENDING
)
275 void HttpCache::MetadataWriter::SelfDestroy() {
279 void HttpCache::MetadataWriter::OnIOComplete(int result
) {
281 return VerifyResponse(result
);
285 //-----------------------------------------------------------------------------
287 class HttpCache::QuicServerInfoFactoryAdaptor
: public QuicServerInfoFactory
{
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_
);
298 HttpCache
* const http_cache_
;
301 //-----------------------------------------------------------------------------
303 class HttpCache::AsyncValidation
{
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
);
314 void OnStarted(int result
);
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.
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
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
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(
375 base::Bind(&AsyncValidation::OnStarted
, base::Unretained(this)),
378 if (rv
== ERR_IO_PENDING
)
384 void HttpCache::AsyncValidation::OnStarted(int result
) {
386 DVLOG(1) << "Asynchronous transaction start failed for " << request_
.url
;
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(
398 base::Bind(&AsyncValidation::OnStarted
, base::Unretained(this)));
399 if (result
== ERR_IO_PENDING
)
402 DVLOG(1) << "Synchronous transaction restart with auth failed for "
412 void HttpCache::AsyncValidation::DoRead() {
413 const size_t kBufSize
= 4096;
415 buf_
= new IOBuffer(kBufSize
);
419 rv
= transaction_
->Read(buf_
.get(), kBufSize
, read_callback_
);
422 if (rv
== ERR_IO_PENDING
)
428 void HttpCache::AsyncValidation::OnRead(int 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
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
),
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
),
482 network_layer_(new HttpNetworkLayer(session
)),
483 clock_(new base::DefaultClock()),
484 weak_factory_(this) {
487 HttpCache::HttpCache(HttpTransactionFactory
* network_layer
,
489 BackendFactory
* backend_factory
)
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),
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();
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.
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;
547 pending_op
->callback
.Reset();
550 STLDeleteElements(&pending_op
->pending_queue
);
551 if (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();
565 return CreateBackend(backend
, callback
);
568 disk_cache::Backend
* HttpCache::GetCurrentBackend() const {
569 return disk_cache_
.get();
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
,
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();
605 session
->CloseAllConnections();
608 void HttpCache::CloseIdleConnections() {
609 HttpNetworkSession
* session
= GetSession();
611 session
->CloseIdleConnections();
614 void HttpCache::OnExternalCacheHit(const GURL
& url
,
615 const std::string
& http_method
) {
616 if (!disk_cache_
.get() || mode_
== DISABLE
)
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")
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
);
651 HttpCache
* HttpCache::GetCache() {
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())
674 building_backend_
= true;
676 scoped_ptr
<WorkItem
> item(new WorkItem(WI_CREATE_BACKEND
, NULL
, callback
,
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
);
704 int HttpCache::GetBackendForTransaction(Transaction
* trans
) {
705 if (disk_cache_
.get())
708 if (!building_backend_
)
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()));
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.
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
);
759 void HttpCache::DoomActiveEntry(const std::string
& key
) {
760 ActiveEntriesMap::iterator it
= active_entries_
.find(key
);
761 if (it
== active_entries_
.end())
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
);
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()) {
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
);
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
);
819 void HttpCache::DoomMainEntryForUrl(const GURL
& url
) {
823 HttpRequestInfo temp_info
;
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
);
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
);
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
;
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();
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
);
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
);
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())
901 PendingOp
* operation
= new PendingOp();
902 pending_ops_
[key
] = operation
;
906 void HttpCache::DeletePendingOp(PendingOp
* pending_op
) {
908 if (pending_op
->disk_entry
)
909 key
= pending_op
->disk_entry
->GetKey();
912 PendingOpsMap::iterator it
= pending_ops_
.find(key
);
913 DCHECK(it
!= pending_ops_
.end());
914 pending_ops_
.erase(it
);
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
);
924 DCHECK(pending_op
->pending_queue
.empty());
929 int HttpCache::OpenEntry(const std::string
& key
, ActiveEntry
** entry
,
930 Transaction
* trans
) {
931 ActiveEntry
* active_entry
= FindActiveEntry(key
);
933 *entry
= active_entry
;
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
);
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
);
989 void HttpCache::DestroyEntry(ActiveEntry
* entry
) {
991 FinalizeDoomedEntry(entry
);
993 DeactivateEntry(entry
);
997 int HttpCache::AddTransactionToEntry(ActiveEntry
* entry
, Transaction
* trans
) {
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
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
;
1019 entry
->pending_queue
.push_back(trans
);
1020 return ERR_IO_PENDING
;
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
);
1036 void HttpCache::DoneWithEntry(ActiveEntry
* entry
, Transaction
* trans
,
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())
1043 if (entry
->writer
) {
1044 DCHECK(trans
== entry
->writer
);
1046 // Assume there was a failure.
1047 bool success
= false;
1049 DCHECK(entry
->disk_entry
);
1050 // This is a successful operation in the sense that we want to keep the
1052 success
= trans
->AddTruncatedFlag();
1053 // The previous operation may have deleted the entry.
1054 if (!trans
->entry())
1057 DoneWritingToEntry(entry
, success
);
1059 DoneReadingFromEntry(entry
, trans
);
1063 void HttpCache::DoneWritingToEntry(ActiveEntry
* entry
, bool success
) {
1064 DCHECK(entry
->readers
.empty());
1066 entry
->writer
= NULL
;
1069 ProcessPendingQueue(entry
);
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());
1131 if (i
!= active_entries_
.end())
1132 found
= RemovePendingTransactionFromEntry(i
->second
, trans
);
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
);
1146 PendingOpsMap::const_iterator j
= pending_ops_
.find(trans
->key());
1147 if (j
!= pending_ops_
.end())
1148 found
= RemovePendingTransactionFromPendingOp(j
->second
, trans
);
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())
1169 pending_queue
.erase(j
);
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();
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
)) {
1186 pending_queue
.erase(it
);
1193 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession
* 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
)
1209 entry
->will_process_pending_queue
= true;
1211 base::MessageLoop::current()->PostTask(
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
;
1224 async_validations_
.insert(AsyncValidationKeyValue(key
, async_validation
))
1227 DVLOG(1) << "Harmless race condition detected on URL "
1228 << original_request
.url
<< "; discarding redundant revalidation.";
1229 delete async_validation
;
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
);
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
;
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
);
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
);
1333 fail_requests
= true;
1336 if (fail_requests
) {
1337 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1341 if (item
->operation() == WI_CREATE_ENTRY
) {
1343 // A second Create request, but the first request succeeded.
1344 item
->NotifyTransaction(ERR_CACHE_CREATE_FAILURE
, NULL
);
1346 if (op
!= WI_CREATE_ENTRY
) {
1347 // Failed Open followed by a Create.
1348 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1349 fail_requests
= true;
1351 item
->NotifyTransaction(result
, entry
);
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;
1360 item
->NotifyTransaction(result
, entry
);
1367 void HttpCache::OnPendingOpComplete(const base::WeakPtr
<HttpCache
>& cache
,
1368 PendingOp
* pending_op
,
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"));
1376 cache
->OnIOComplete(rv
, pending_op
);
1378 // The callback was cancelled so we should delete the pending_op that
1379 // was used with this callback.
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.
1398 disk_cache_
= pending_op
->backend
.Pass();
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(
1415 base::Bind(&HttpCache::OnBackendCreated
, GetWeakPtr(),
1416 result
, pending_op
));
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
);