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/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/pickle.h"
25 #include "base/stl_util.h"
26 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/threading/worker_pool.h"
30 #include "net/base/cache_type.h"
31 #include "net/base/io_buffer.h"
32 #include "net/base/load_flags.h"
33 #include "net/base/net_errors.h"
34 #include "net/base/upload_data_stream.h"
35 #include "net/disk_cache/disk_cache.h"
36 #include "net/http/disk_based_cert_cache.h"
37 #include "net/http/disk_cache_based_quic_server_info.h"
38 #include "net/http/http_cache_transaction.h"
39 #include "net/http/http_network_layer.h"
40 #include "net/http/http_network_session.h"
41 #include "net/http/http_request_info.h"
42 #include "net/http/http_response_headers.h"
43 #include "net/http/http_response_info.h"
44 #include "net/http/http_util.h"
45 #include "net/quic/crypto/quic_server_info.h"
50 return base::FieldTrialList::FindFullName("CertCacheTrial") ==
54 // Adaptor to delete a file on a worker thread.
55 void DeletePath(base::FilePath path
) {
56 base::DeleteFile(path
, false);
63 HttpCache::DefaultBackend::DefaultBackend(
65 BackendType backend_type
,
66 const base::FilePath
& path
,
68 const scoped_refptr
<base::SingleThreadTaskRunner
>& thread
)
70 backend_type_(backend_type
),
72 max_bytes_(max_bytes
),
76 HttpCache::DefaultBackend::~DefaultBackend() {}
79 HttpCache::BackendFactory
* HttpCache::DefaultBackend::InMemory(int max_bytes
) {
80 return new DefaultBackend(MEMORY_CACHE
, net::CACHE_BACKEND_DEFAULT
,
81 base::FilePath(), max_bytes
, NULL
);
84 int HttpCache::DefaultBackend::CreateBackend(
85 NetLog
* net_log
, scoped_ptr
<disk_cache::Backend
>* backend
,
86 const CompletionCallback
& callback
) {
87 DCHECK_GE(max_bytes_
, 0);
88 return disk_cache::CreateCacheBackend(type_
,
99 //-----------------------------------------------------------------------------
101 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry
* entry
)
104 will_process_pending_queue(false),
108 HttpCache::ActiveEntry::~ActiveEntry() {
115 //-----------------------------------------------------------------------------
117 // This structure keeps track of work items that are attempting to create or
118 // open cache entries or the backend itself.
119 struct HttpCache::PendingOp
{
120 PendingOp() : disk_entry(NULL
), writer(NULL
) {}
123 disk_cache::Entry
* disk_entry
;
124 scoped_ptr
<disk_cache::Backend
> backend
;
126 CompletionCallback callback
; // BackendCallback.
127 WorkItemList pending_queue
;
130 //-----------------------------------------------------------------------------
132 // The type of operation represented by a work item.
133 enum WorkItemOperation
{
140 // A work item encapsulates a single request to the backend with all the
141 // information needed to complete that request.
142 class HttpCache::WorkItem
{
144 WorkItem(WorkItemOperation operation
, Transaction
* trans
, ActiveEntry
** entry
)
145 : operation_(operation
),
149 WorkItem(WorkItemOperation operation
, Transaction
* trans
,
150 const net::CompletionCallback
& cb
, disk_cache::Backend
** backend
)
151 : operation_(operation
),
158 // Calls back the transaction with the result of the operation.
159 void NotifyTransaction(int result
, ActiveEntry
* entry
) {
160 DCHECK(!entry
|| entry
->disk_entry
);
164 trans_
->io_callback().Run(result
);
167 // Notifies the caller about the operation completion. Returns true if the
168 // callback was invoked.
169 bool DoCallback(int result
, disk_cache::Backend
* backend
) {
172 if (!callback_
.is_null()) {
173 callback_
.Run(result
);
179 WorkItemOperation
operation() { return operation_
; }
180 void ClearTransaction() { trans_
= NULL
; }
181 void ClearEntry() { entry_
= NULL
; }
182 void ClearCallback() { callback_
.Reset(); }
183 bool Matches(Transaction
* trans
) const { return trans
== trans_
; }
184 bool IsValid() const { return trans_
|| entry_
|| !callback_
.is_null(); }
187 WorkItemOperation operation_
;
189 ActiveEntry
** entry_
;
190 net::CompletionCallback callback_
; // User callback.
191 disk_cache::Backend
** backend_
;
194 //-----------------------------------------------------------------------------
196 // This class encapsulates a transaction whose only purpose is to write metadata
198 class HttpCache::MetadataWriter
{
200 explicit MetadataWriter(HttpCache::Transaction
* trans
)
201 : transaction_(trans
),
208 // Implements the bulk of HttpCache::WriteMetadata.
209 void Write(const GURL
& url
, base::Time expected_response_time
, IOBuffer
* buf
,
213 void VerifyResponse(int result
);
215 void OnIOComplete(int result
);
217 scoped_ptr
<HttpCache::Transaction
> transaction_
;
219 scoped_refptr
<IOBuffer
> buf_
;
221 base::Time expected_response_time_
;
222 HttpRequestInfo request_info_
;
223 DISALLOW_COPY_AND_ASSIGN(MetadataWriter
);
226 void HttpCache::MetadataWriter::Write(const GURL
& url
,
227 base::Time expected_response_time
,
228 IOBuffer
* buf
, int buf_len
) {
229 DCHECK_GT(buf_len
, 0);
232 request_info_
.url
= url
;
233 request_info_
.method
= "GET";
234 request_info_
.load_flags
= LOAD_ONLY_FROM_CACHE
;
236 expected_response_time_
= expected_response_time
;
241 int rv
= transaction_
->Start(
243 base::Bind(&MetadataWriter::OnIOComplete
, base::Unretained(this)),
245 if (rv
!= ERR_IO_PENDING
)
249 void HttpCache::MetadataWriter::VerifyResponse(int result
) {
252 return SelfDestroy();
254 const HttpResponseInfo
* response_info
= transaction_
->GetResponseInfo();
255 DCHECK(response_info
->was_cached
);
256 if (response_info
->response_time
!= expected_response_time_
)
257 return SelfDestroy();
259 result
= transaction_
->WriteMetadata(
262 base::Bind(&MetadataWriter::OnIOComplete
, base::Unretained(this)));
263 if (result
!= ERR_IO_PENDING
)
267 void HttpCache::MetadataWriter::SelfDestroy() {
271 void HttpCache::MetadataWriter::OnIOComplete(int result
) {
273 return VerifyResponse(result
);
277 //-----------------------------------------------------------------------------
279 class HttpCache::QuicServerInfoFactoryAdaptor
: public QuicServerInfoFactory
{
281 QuicServerInfoFactoryAdaptor(HttpCache
* http_cache
)
282 : http_cache_(http_cache
) {
285 virtual QuicServerInfo
* GetForServer(
286 const QuicServerId
& server_id
) OVERRIDE
{
287 return new DiskCacheBasedQuicServerInfo(server_id
, http_cache_
);
291 HttpCache
* const http_cache_
;
294 //-----------------------------------------------------------------------------
295 HttpCache::HttpCache(const net::HttpNetworkSession::Params
& params
,
296 BackendFactory
* backend_factory
)
297 : net_log_(params
.net_log
),
298 backend_factory_(backend_factory
),
299 building_backend_(false),
300 bypass_lock_for_test_(false),
302 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params
))),
303 weak_factory_(this) {
304 SetupQuicServerInfoFactory(network_layer_
->GetSession());
308 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
309 // |session| is shared.
310 HttpCache::HttpCache(HttpNetworkSession
* session
,
311 BackendFactory
* backend_factory
)
312 : net_log_(session
->net_log()),
313 backend_factory_(backend_factory
),
314 building_backend_(false),
315 bypass_lock_for_test_(false),
317 network_layer_(new HttpNetworkLayer(session
)),
318 weak_factory_(this) {
321 HttpCache::HttpCache(HttpTransactionFactory
* network_layer
,
323 BackendFactory
* backend_factory
)
325 backend_factory_(backend_factory
),
326 building_backend_(false),
327 bypass_lock_for_test_(false),
329 network_layer_(network_layer
),
330 weak_factory_(this) {
331 SetupQuicServerInfoFactory(network_layer_
->GetSession());
334 HttpCache::~HttpCache() {
335 // Transactions should see an invalid cache after this point; otherwise they
336 // could see an inconsistent object (half destroyed).
337 weak_factory_
.InvalidateWeakPtrs();
339 // If we have any active entries remaining, then we need to deactivate them.
340 // We may have some pending calls to OnProcessPendingQueue, but since those
341 // won't run (due to our destruction), we can simply ignore the corresponding
342 // will_process_pending_queue flag.
343 while (!active_entries_
.empty()) {
344 ActiveEntry
* entry
= active_entries_
.begin()->second
;
345 entry
->will_process_pending_queue
= false;
346 entry
->pending_queue
.clear();
347 entry
->readers
.clear();
348 entry
->writer
= NULL
;
349 DeactivateEntry(entry
);
352 STLDeleteElements(&doomed_entries_
);
354 // Before deleting pending_ops_, we have to make sure that the disk cache is
355 // done with said operations, or it will attempt to use deleted data.
359 PendingOpsMap::iterator pending_it
= pending_ops_
.begin();
360 for (; pending_it
!= pending_ops_
.end(); ++pending_it
) {
361 // We are not notifying the transactions about the cache going away, even
362 // though they are waiting for a callback that will never fire.
363 PendingOp
* pending_op
= pending_it
->second
;
364 delete pending_op
->writer
;
365 bool delete_pending_op
= true;
366 if (building_backend_
) {
367 // If we don't have a backend, when its construction finishes it will
368 // deliver the callbacks.
369 if (!pending_op
->callback
.is_null()) {
370 // If not null, the callback will delete the pending operation later.
371 delete_pending_op
= false;
374 pending_op
->callback
.Reset();
377 STLDeleteElements(&pending_op
->pending_queue
);
378 if (delete_pending_op
)
383 int HttpCache::GetBackend(disk_cache::Backend
** backend
,
384 const CompletionCallback
& callback
) {
385 DCHECK(!callback
.is_null());
387 if (disk_cache_
.get()) {
388 *backend
= disk_cache_
.get();
392 return CreateBackend(backend
, callback
);
395 disk_cache::Backend
* HttpCache::GetCurrentBackend() const {
396 return disk_cache_
.get();
400 bool HttpCache::ParseResponseInfo(const char* data
, int len
,
401 HttpResponseInfo
* response_info
,
402 bool* response_truncated
) {
403 Pickle
pickle(data
, len
);
404 return response_info
->InitFromPickle(pickle
, response_truncated
);
407 void HttpCache::WriteMetadata(const GURL
& url
,
408 RequestPriority priority
,
409 base::Time expected_response_time
,
415 // Do lazy initialization of disk cache if needed.
416 if (!disk_cache_
.get()) {
417 // We don't care about the result.
418 CreateBackend(NULL
, net::CompletionCallback());
421 HttpCache::Transaction
* trans
=
422 new HttpCache::Transaction(priority
, this);
423 MetadataWriter
* writer
= new MetadataWriter(trans
);
425 // The writer will self destruct when done.
426 writer
->Write(url
, expected_response_time
, buf
, buf_len
);
429 void HttpCache::CloseAllConnections() {
430 HttpNetworkSession
* session
= GetSession();
432 session
->CloseAllConnections();
435 void HttpCache::CloseIdleConnections() {
436 HttpNetworkSession
* session
= GetSession();
438 session
->CloseIdleConnections();
441 void HttpCache::OnExternalCacheHit(const GURL
& url
,
442 const std::string
& http_method
) {
443 if (!disk_cache_
.get())
446 HttpRequestInfo request_info
;
447 request_info
.url
= url
;
448 request_info
.method
= http_method
;
449 std::string key
= GenerateCacheKey(&request_info
);
450 disk_cache_
->OnExternalCacheHit(key
);
453 void HttpCache::InitializeInfiniteCache(const base::FilePath
& path
) {
454 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
456 base::WorkerPool::PostTask(FROM_HERE
, base::Bind(&DeletePath
, path
), true);
459 int HttpCache::CreateTransaction(RequestPriority priority
,
460 scoped_ptr
<HttpTransaction
>* trans
) {
461 // Do lazy initialization of disk cache if needed.
462 if (!disk_cache_
.get()) {
463 // We don't care about the result.
464 CreateBackend(NULL
, net::CompletionCallback());
467 HttpCache::Transaction
* transaction
=
468 new HttpCache::Transaction(priority
, this);
469 if (bypass_lock_for_test_
)
470 transaction
->BypassLockForTest();
472 trans
->reset(transaction
);
476 HttpCache
* HttpCache::GetCache() {
480 HttpNetworkSession
* HttpCache::GetSession() {
481 return network_layer_
->GetSession();
484 scoped_ptr
<HttpTransactionFactory
>
485 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
486 scoped_ptr
<HttpTransactionFactory
> new_network_layer
) {
487 scoped_ptr
<HttpTransactionFactory
> old_network_layer(network_layer_
.Pass());
488 network_layer_
= new_network_layer
.Pass();
489 return old_network_layer
.Pass();
492 //-----------------------------------------------------------------------------
494 int HttpCache::CreateBackend(disk_cache::Backend
** backend
,
495 const net::CompletionCallback
& callback
) {
496 if (!backend_factory_
.get())
499 building_backend_
= true;
501 scoped_ptr
<WorkItem
> item(new WorkItem(WI_CREATE_BACKEND
, NULL
, callback
,
504 // This is the only operation that we can do that is not related to any given
505 // entry, so we use an empty key for it.
506 PendingOp
* pending_op
= GetPendingOp(std::string());
507 if (pending_op
->writer
) {
508 if (!callback
.is_null())
509 pending_op
->pending_queue
.push_back(item
.release());
510 return ERR_IO_PENDING
;
513 DCHECK(pending_op
->pending_queue
.empty());
515 pending_op
->writer
= item
.release();
516 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
517 GetWeakPtr(), pending_op
);
519 int rv
= backend_factory_
->CreateBackend(net_log_
, &pending_op
->backend
,
520 pending_op
->callback
);
521 if (rv
!= ERR_IO_PENDING
) {
522 pending_op
->writer
->ClearCallback();
523 pending_op
->callback
.Run(rv
);
529 int HttpCache::GetBackendForTransaction(Transaction
* trans
) {
530 if (disk_cache_
.get())
533 if (!building_backend_
)
536 WorkItem
* item
= new WorkItem(
537 WI_CREATE_BACKEND
, trans
, net::CompletionCallback(), NULL
);
538 PendingOp
* pending_op
= GetPendingOp(std::string());
539 DCHECK(pending_op
->writer
);
540 pending_op
->pending_queue
.push_back(item
);
541 return ERR_IO_PENDING
;
544 // Generate a key that can be used inside the cache.
545 std::string
HttpCache::GenerateCacheKey(const HttpRequestInfo
* request
) {
546 // Strip out the reference, username, and password sections of the URL.
547 std::string url
= HttpUtil::SpecForRequest(request
->url
);
549 DCHECK(mode_
!= DISABLE
);
550 if (mode_
== NORMAL
) {
551 // No valid URL can begin with numerals, so we should not have to worry
552 // about collisions with normal URLs.
553 if (request
->upload_data_stream
&&
554 request
->upload_data_stream
->identifier()) {
555 url
.insert(0, base::StringPrintf(
556 "%" PRId64
"/", request
->upload_data_stream
->identifier()));
561 // In playback and record mode, we cache everything.
563 // Lazily initialize.
564 if (playback_cache_map_
== NULL
)
565 playback_cache_map_
.reset(new PlaybackCacheMap());
567 // Each time we request an item from the cache, we tag it with a
568 // generation number. During playback, multiple fetches for the same
569 // item will use the same generation number and pull the proper
570 // instance of an URL from the cache.
572 DCHECK(playback_cache_map_
!= NULL
);
573 if (playback_cache_map_
->find(url
) != playback_cache_map_
->end())
574 generation
= (*playback_cache_map_
)[url
];
575 (*playback_cache_map_
)[url
] = generation
+ 1;
577 // The key into the cache is GENERATION # + METHOD + URL.
578 std::string result
= base::IntToString(generation
);
579 result
.append(request
->method
);
584 void HttpCache::DoomActiveEntry(const std::string
& key
) {
585 ActiveEntriesMap::iterator it
= active_entries_
.find(key
);
586 if (it
== active_entries_
.end())
589 // This is not a performance critical operation, this is handling an error
590 // condition so it is OK to look up the entry again.
591 int rv
= DoomEntry(key
, NULL
);
595 int HttpCache::DoomEntry(const std::string
& key
, Transaction
* trans
) {
596 // Need to abandon the ActiveEntry, but any transaction attached to the entry
597 // should not be impacted. Dooming an entry only means that it will no
598 // longer be returned by FindActiveEntry (and it will also be destroyed once
599 // all consumers are finished with the entry).
600 ActiveEntriesMap::iterator it
= active_entries_
.find(key
);
601 if (it
== active_entries_
.end()) {
603 return AsyncDoomEntry(key
, trans
);
606 ActiveEntry
* entry
= it
->second
;
607 active_entries_
.erase(it
);
609 // We keep track of doomed entries so that we can ensure that they are
610 // cleaned up properly when the cache is destroyed.
611 doomed_entries_
.insert(entry
);
613 entry
->disk_entry
->Doom();
614 entry
->doomed
= true;
616 DCHECK(entry
->writer
|| !entry
->readers
.empty() ||
617 entry
->will_process_pending_queue
);
621 int HttpCache::AsyncDoomEntry(const std::string
& key
, Transaction
* trans
) {
622 WorkItem
* item
= new WorkItem(WI_DOOM_ENTRY
, trans
, NULL
);
623 PendingOp
* pending_op
= GetPendingOp(key
);
624 if (pending_op
->writer
) {
625 pending_op
->pending_queue
.push_back(item
);
626 return ERR_IO_PENDING
;
629 DCHECK(pending_op
->pending_queue
.empty());
631 pending_op
->writer
= item
;
632 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
633 GetWeakPtr(), pending_op
);
635 int rv
= disk_cache_
->DoomEntry(key
, pending_op
->callback
);
636 if (rv
!= ERR_IO_PENDING
) {
637 item
->ClearTransaction();
638 pending_op
->callback
.Run(rv
);
644 void HttpCache::DoomMainEntryForUrl(const GURL
& url
) {
648 HttpRequestInfo temp_info
;
650 temp_info
.method
= "GET";
651 std::string key
= GenerateCacheKey(&temp_info
);
653 // Defer to DoomEntry if there is an active entry, otherwise call
654 // AsyncDoomEntry without triggering a callback.
655 if (active_entries_
.count(key
))
656 DoomEntry(key
, NULL
);
658 AsyncDoomEntry(key
, NULL
);
661 void HttpCache::FinalizeDoomedEntry(ActiveEntry
* entry
) {
662 DCHECK(entry
->doomed
);
663 DCHECK(!entry
->writer
);
664 DCHECK(entry
->readers
.empty());
665 DCHECK(entry
->pending_queue
.empty());
667 ActiveEntriesSet::iterator it
= doomed_entries_
.find(entry
);
668 DCHECK(it
!= doomed_entries_
.end());
669 doomed_entries_
.erase(it
);
674 HttpCache::ActiveEntry
* HttpCache::FindActiveEntry(const std::string
& key
) {
675 ActiveEntriesMap::const_iterator it
= active_entries_
.find(key
);
676 return it
!= active_entries_
.end() ? it
->second
: NULL
;
679 HttpCache::ActiveEntry
* HttpCache::ActivateEntry(
680 disk_cache::Entry
* disk_entry
) {
681 DCHECK(!FindActiveEntry(disk_entry
->GetKey()));
682 ActiveEntry
* entry
= new ActiveEntry(disk_entry
);
683 active_entries_
[disk_entry
->GetKey()] = entry
;
687 void HttpCache::DeactivateEntry(ActiveEntry
* entry
) {
688 DCHECK(!entry
->will_process_pending_queue
);
689 DCHECK(!entry
->doomed
);
690 DCHECK(!entry
->writer
);
691 DCHECK(entry
->disk_entry
);
692 DCHECK(entry
->readers
.empty());
693 DCHECK(entry
->pending_queue
.empty());
695 std::string key
= entry
->disk_entry
->GetKey();
697 return SlowDeactivateEntry(entry
);
699 ActiveEntriesMap::iterator it
= active_entries_
.find(key
);
700 DCHECK(it
!= active_entries_
.end());
701 DCHECK(it
->second
== entry
);
703 active_entries_
.erase(it
);
707 // We don't know this entry's key so we have to find it without it.
708 void HttpCache::SlowDeactivateEntry(ActiveEntry
* entry
) {
709 for (ActiveEntriesMap::iterator it
= active_entries_
.begin();
710 it
!= active_entries_
.end(); ++it
) {
711 if (it
->second
== entry
) {
712 active_entries_
.erase(it
);
719 HttpCache::PendingOp
* HttpCache::GetPendingOp(const std::string
& key
) {
720 DCHECK(!FindActiveEntry(key
));
722 PendingOpsMap::const_iterator it
= pending_ops_
.find(key
);
723 if (it
!= pending_ops_
.end())
726 PendingOp
* operation
= new PendingOp();
727 pending_ops_
[key
] = operation
;
731 void HttpCache::DeletePendingOp(PendingOp
* pending_op
) {
733 if (pending_op
->disk_entry
)
734 key
= pending_op
->disk_entry
->GetKey();
737 PendingOpsMap::iterator it
= pending_ops_
.find(key
);
738 DCHECK(it
!= pending_ops_
.end());
739 pending_ops_
.erase(it
);
741 for (PendingOpsMap::iterator it
= pending_ops_
.begin();
742 it
!= pending_ops_
.end(); ++it
) {
743 if (it
->second
== pending_op
) {
744 pending_ops_
.erase(it
);
749 DCHECK(pending_op
->pending_queue
.empty());
754 int HttpCache::OpenEntry(const std::string
& key
, ActiveEntry
** entry
,
755 Transaction
* trans
) {
756 ActiveEntry
* active_entry
= FindActiveEntry(key
);
758 *entry
= active_entry
;
762 WorkItem
* item
= new WorkItem(WI_OPEN_ENTRY
, trans
, entry
);
763 PendingOp
* pending_op
= GetPendingOp(key
);
764 if (pending_op
->writer
) {
765 pending_op
->pending_queue
.push_back(item
);
766 return ERR_IO_PENDING
;
769 DCHECK(pending_op
->pending_queue
.empty());
771 pending_op
->writer
= item
;
772 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
773 GetWeakPtr(), pending_op
);
775 int rv
= disk_cache_
->OpenEntry(key
, &(pending_op
->disk_entry
),
776 pending_op
->callback
);
777 if (rv
!= ERR_IO_PENDING
) {
778 item
->ClearTransaction();
779 pending_op
->callback
.Run(rv
);
785 int HttpCache::CreateEntry(const std::string
& key
, ActiveEntry
** entry
,
786 Transaction
* trans
) {
787 if (FindActiveEntry(key
)) {
788 return ERR_CACHE_RACE
;
791 WorkItem
* item
= new WorkItem(WI_CREATE_ENTRY
, trans
, entry
);
792 PendingOp
* pending_op
= GetPendingOp(key
);
793 if (pending_op
->writer
) {
794 pending_op
->pending_queue
.push_back(item
);
795 return ERR_IO_PENDING
;
798 DCHECK(pending_op
->pending_queue
.empty());
800 pending_op
->writer
= item
;
801 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
802 GetWeakPtr(), pending_op
);
804 int rv
= disk_cache_
->CreateEntry(key
, &(pending_op
->disk_entry
),
805 pending_op
->callback
);
806 if (rv
!= ERR_IO_PENDING
) {
807 item
->ClearTransaction();
808 pending_op
->callback
.Run(rv
);
814 void HttpCache::DestroyEntry(ActiveEntry
* entry
) {
816 FinalizeDoomedEntry(entry
);
818 DeactivateEntry(entry
);
822 int HttpCache::AddTransactionToEntry(ActiveEntry
* entry
, Transaction
* trans
) {
824 DCHECK(entry
->disk_entry
);
826 // We implement a basic reader/writer lock for the disk cache entry. If
827 // there is already a writer, then everyone has to wait for the writer to
828 // finish before they can access the cache entry. There can be multiple
831 // NOTE: If the transaction can only write, then the entry should not be in
832 // use (since any existing entry should have already been doomed).
834 if (entry
->writer
|| entry
->will_process_pending_queue
) {
835 entry
->pending_queue
.push_back(trans
);
836 return ERR_IO_PENDING
;
839 if (trans
->mode() & Transaction::WRITE
) {
840 // transaction needs exclusive access to the entry
841 if (entry
->readers
.empty()) {
842 entry
->writer
= trans
;
844 entry
->pending_queue
.push_back(trans
);
845 return ERR_IO_PENDING
;
848 // transaction needs read access to the entry
849 entry
->readers
.push_back(trans
);
852 // We do this before calling EntryAvailable to force any further calls to
853 // AddTransactionToEntry to add their transaction to the pending queue, which
854 // ensures FIFO ordering.
855 if (!entry
->writer
&& !entry
->pending_queue
.empty())
856 ProcessPendingQueue(entry
);
861 void HttpCache::DoneWithEntry(ActiveEntry
* entry
, Transaction
* trans
,
863 // If we already posted a task to move on to the next transaction and this was
864 // the writer, there is nothing to cancel.
865 if (entry
->will_process_pending_queue
&& entry
->readers
.empty())
869 DCHECK(trans
== entry
->writer
);
871 // Assume there was a failure.
872 bool success
= false;
874 DCHECK(entry
->disk_entry
);
875 // This is a successful operation in the sense that we want to keep the
877 success
= trans
->AddTruncatedFlag();
878 // The previous operation may have deleted the entry.
882 DoneWritingToEntry(entry
, success
);
884 DoneReadingFromEntry(entry
, trans
);
888 void HttpCache::DoneWritingToEntry(ActiveEntry
* entry
, bool success
) {
889 DCHECK(entry
->readers
.empty());
891 entry
->writer
= NULL
;
894 ProcessPendingQueue(entry
);
896 DCHECK(!entry
->will_process_pending_queue
);
898 // We failed to create this entry.
899 TransactionList pending_queue
;
900 pending_queue
.swap(entry
->pending_queue
);
902 entry
->disk_entry
->Doom();
905 // We need to do something about these pending entries, which now need to
906 // be added to a new entry.
907 while (!pending_queue
.empty()) {
908 // ERR_CACHE_RACE causes the transaction to restart the whole process.
909 pending_queue
.front()->io_callback().Run(ERR_CACHE_RACE
);
910 pending_queue
.pop_front();
915 void HttpCache::DoneReadingFromEntry(ActiveEntry
* entry
, Transaction
* trans
) {
916 DCHECK(!entry
->writer
);
918 TransactionList::iterator it
=
919 std::find(entry
->readers
.begin(), entry
->readers
.end(), trans
);
920 DCHECK(it
!= entry
->readers
.end());
922 entry
->readers
.erase(it
);
924 ProcessPendingQueue(entry
);
927 void HttpCache::ConvertWriterToReader(ActiveEntry
* entry
) {
928 DCHECK(entry
->writer
);
929 DCHECK(entry
->writer
->mode() == Transaction::READ_WRITE
);
930 DCHECK(entry
->readers
.empty());
932 Transaction
* trans
= entry
->writer
;
934 entry
->writer
= NULL
;
935 entry
->readers
.push_back(trans
);
937 ProcessPendingQueue(entry
);
940 LoadState
HttpCache::GetLoadStateForPendingTransaction(
941 const Transaction
* trans
) {
942 ActiveEntriesMap::const_iterator i
= active_entries_
.find(trans
->key());
943 if (i
== active_entries_
.end()) {
944 // If this is really a pending transaction, and it is not part of
945 // active_entries_, we should be creating the backend or the entry.
946 return LOAD_STATE_WAITING_FOR_CACHE
;
949 Transaction
* writer
= i
->second
->writer
;
950 return writer
? writer
->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE
;
953 void HttpCache::RemovePendingTransaction(Transaction
* trans
) {
954 ActiveEntriesMap::const_iterator i
= active_entries_
.find(trans
->key());
956 if (i
!= active_entries_
.end())
957 found
= RemovePendingTransactionFromEntry(i
->second
, trans
);
962 if (building_backend_
) {
963 PendingOpsMap::const_iterator j
= pending_ops_
.find(std::string());
964 if (j
!= pending_ops_
.end())
965 found
= RemovePendingTransactionFromPendingOp(j
->second
, trans
);
971 PendingOpsMap::const_iterator j
= pending_ops_
.find(trans
->key());
972 if (j
!= pending_ops_
.end())
973 found
= RemovePendingTransactionFromPendingOp(j
->second
, trans
);
978 ActiveEntriesSet::iterator k
= doomed_entries_
.begin();
979 for (; k
!= doomed_entries_
.end() && !found
; ++k
)
980 found
= RemovePendingTransactionFromEntry(*k
, trans
);
982 DCHECK(found
) << "Pending transaction not found";
985 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry
* entry
,
986 Transaction
* trans
) {
987 TransactionList
& pending_queue
= entry
->pending_queue
;
989 TransactionList::iterator j
=
990 find(pending_queue
.begin(), pending_queue
.end(), trans
);
991 if (j
== pending_queue
.end())
994 pending_queue
.erase(j
);
998 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp
* pending_op
,
999 Transaction
* trans
) {
1000 if (pending_op
->writer
->Matches(trans
)) {
1001 pending_op
->writer
->ClearTransaction();
1002 pending_op
->writer
->ClearEntry();
1005 WorkItemList
& pending_queue
= pending_op
->pending_queue
;
1007 WorkItemList::iterator it
= pending_queue
.begin();
1008 for (; it
!= pending_queue
.end(); ++it
) {
1009 if ((*it
)->Matches(trans
)) {
1011 pending_queue
.erase(it
);
1018 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession
* session
) {
1020 !session
->quic_stream_factory()->has_quic_server_info_factory()) {
1021 DCHECK(!quic_server_info_factory_
);
1022 quic_server_info_factory_
.reset(new QuicServerInfoFactoryAdaptor(this));
1023 session
->quic_stream_factory()->set_quic_server_info_factory(
1024 quic_server_info_factory_
.get());
1028 void HttpCache::ProcessPendingQueue(ActiveEntry
* entry
) {
1029 // Multiple readers may finish with an entry at once, so we want to batch up
1030 // calls to OnProcessPendingQueue. This flag also tells us that we should
1031 // not delete the entry before OnProcessPendingQueue runs.
1032 if (entry
->will_process_pending_queue
)
1034 entry
->will_process_pending_queue
= true;
1036 base::MessageLoop::current()->PostTask(
1038 base::Bind(&HttpCache::OnProcessPendingQueue
, GetWeakPtr(), entry
));
1041 void HttpCache::OnProcessPendingQueue(ActiveEntry
* entry
) {
1042 entry
->will_process_pending_queue
= false;
1043 DCHECK(!entry
->writer
);
1045 // If no one is interested in this entry, then we can deactivate it.
1046 if (entry
->pending_queue
.empty()) {
1047 if (entry
->readers
.empty())
1048 DestroyEntry(entry
);
1052 // Promote next transaction from the pending queue.
1053 Transaction
* next
= entry
->pending_queue
.front();
1054 if ((next
->mode() & Transaction::WRITE
) && !entry
->readers
.empty())
1055 return; // Have to wait.
1057 entry
->pending_queue
.erase(entry
->pending_queue
.begin());
1059 int rv
= AddTransactionToEntry(entry
, next
);
1060 if (rv
!= ERR_IO_PENDING
) {
1061 next
->io_callback().Run(rv
);
1065 void HttpCache::OnIOComplete(int result
, PendingOp
* pending_op
) {
1066 WorkItemOperation op
= pending_op
->writer
->operation();
1068 // Completing the creation of the backend is simpler than the other cases.
1069 if (op
== WI_CREATE_BACKEND
)
1070 return OnBackendCreated(result
, pending_op
);
1072 scoped_ptr
<WorkItem
> item(pending_op
->writer
);
1073 bool fail_requests
= false;
1075 ActiveEntry
* entry
= NULL
;
1078 if (op
== WI_DOOM_ENTRY
) {
1079 // Anything after a Doom has to be restarted.
1080 fail_requests
= true;
1081 } else if (item
->IsValid()) {
1082 key
= pending_op
->disk_entry
->GetKey();
1083 entry
= ActivateEntry(pending_op
->disk_entry
);
1085 // The writer transaction is gone.
1086 if (op
== WI_CREATE_ENTRY
)
1087 pending_op
->disk_entry
->Doom();
1088 pending_op
->disk_entry
->Close();
1089 pending_op
->disk_entry
= NULL
;
1090 fail_requests
= true;
1094 // We are about to notify a bunch of transactions, and they may decide to
1095 // re-issue a request (or send a different one). If we don't delete
1096 // pending_op, the new request will be appended to the end of the list, and
1097 // we'll see it again from this point before it has a chance to complete (and
1098 // we'll be messing out the request order). The down side is that if for some
1099 // reason notifying request A ends up cancelling request B (for the same key),
1100 // we won't find request B anywhere (because it would be in a local variable
1101 // here) and that's bad. If there is a chance for that to happen, we'll have
1102 // to move the callback used to be a CancelableCallback. By the way, for this
1103 // to happen the action (to cancel B) has to be synchronous to the
1104 // notification for request A.
1105 WorkItemList pending_items
;
1106 pending_items
.swap(pending_op
->pending_queue
);
1107 DeletePendingOp(pending_op
);
1109 item
->NotifyTransaction(result
, entry
);
1111 while (!pending_items
.empty()) {
1112 item
.reset(pending_items
.front());
1113 pending_items
.pop_front();
1115 if (item
->operation() == WI_DOOM_ENTRY
) {
1116 // A queued doom request is always a race.
1117 fail_requests
= true;
1118 } else if (result
== OK
) {
1119 entry
= FindActiveEntry(key
);
1121 fail_requests
= true;
1124 if (fail_requests
) {
1125 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1129 if (item
->operation() == WI_CREATE_ENTRY
) {
1131 // A second Create request, but the first request succeeded.
1132 item
->NotifyTransaction(ERR_CACHE_CREATE_FAILURE
, NULL
);
1134 if (op
!= WI_CREATE_ENTRY
) {
1135 // Failed Open followed by a Create.
1136 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1137 fail_requests
= true;
1139 item
->NotifyTransaction(result
, entry
);
1143 if (op
== WI_CREATE_ENTRY
&& result
!= OK
) {
1144 // Failed Create followed by an Open.
1145 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1146 fail_requests
= true;
1148 item
->NotifyTransaction(result
, entry
);
1155 void HttpCache::OnPendingOpComplete(const base::WeakPtr
<HttpCache
>& cache
,
1156 PendingOp
* pending_op
,
1159 cache
->OnIOComplete(rv
, pending_op
);
1161 // The callback was cancelled so we should delete the pending_op that
1162 // was used with this callback.
1167 void HttpCache::OnBackendCreated(int result
, PendingOp
* pending_op
) {
1168 scoped_ptr
<WorkItem
> item(pending_op
->writer
);
1169 WorkItemOperation op
= item
->operation();
1170 DCHECK_EQ(WI_CREATE_BACKEND
, op
);
1172 // We don't need the callback anymore.
1173 pending_op
->callback
.Reset();
1175 if (backend_factory_
.get()) {
1176 // We may end up calling OnBackendCreated multiple times if we have pending
1177 // work items. The first call saves the backend and releases the factory,
1178 // and the last call clears building_backend_.
1179 backend_factory_
.reset(); // Reclaim memory.
1181 disk_cache_
= pending_op
->backend
.Pass();
1183 cert_cache_
.reset(new DiskBasedCertCache(disk_cache_
.get()));
1187 if (!pending_op
->pending_queue
.empty()) {
1188 WorkItem
* pending_item
= pending_op
->pending_queue
.front();
1189 pending_op
->pending_queue
.pop_front();
1190 DCHECK_EQ(WI_CREATE_BACKEND
, pending_item
->operation());
1192 // We want to process a single callback at a time, because the cache may
1193 // go away from the callback.
1194 pending_op
->writer
= pending_item
;
1196 base::MessageLoop::current()->PostTask(
1198 base::Bind(&HttpCache::OnBackendCreated
, GetWeakPtr(),
1199 result
, pending_op
));
1201 building_backend_
= false;
1202 DeletePendingOp(pending_op
);
1205 // The cache may be gone when we return from the callback.
1206 if (!item
->DoCallback(result
, disk_cache_
.get()))
1207 item
->NotifyTransaction(result
, NULL
);