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_cache_based_quic_server_info.h"
37 #include "net/http/http_cache_transaction.h"
38 #include "net/http/http_network_layer.h"
39 #include "net/http/http_network_session.h"
40 #include "net/http/http_request_info.h"
41 #include "net/http/http_response_headers.h"
42 #include "net/http/http_response_info.h"
43 #include "net/http/http_util.h"
44 #include "net/quic/crypto/quic_server_info.h"
48 // Adaptor to delete a file on a worker thread.
49 void DeletePath(base::FilePath path
) {
50 base::DeleteFile(path
, false);
57 HttpCache::DefaultBackend::DefaultBackend(CacheType type
,
58 BackendType backend_type
,
59 const base::FilePath
& path
,
61 base::MessageLoopProxy
* thread
)
63 backend_type_(backend_type
),
65 max_bytes_(max_bytes
),
69 HttpCache::DefaultBackend::~DefaultBackend() {}
72 HttpCache::BackendFactory
* HttpCache::DefaultBackend::InMemory(int max_bytes
) {
73 return new DefaultBackend(MEMORY_CACHE
, net::CACHE_BACKEND_DEFAULT
,
74 base::FilePath(), max_bytes
, NULL
);
77 int HttpCache::DefaultBackend::CreateBackend(
78 NetLog
* net_log
, scoped_ptr
<disk_cache::Backend
>* backend
,
79 const CompletionCallback
& callback
) {
80 DCHECK_GE(max_bytes_
, 0);
81 return disk_cache::CreateCacheBackend(type_
,
92 //-----------------------------------------------------------------------------
94 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry
* entry
)
97 will_process_pending_queue(false),
101 HttpCache::ActiveEntry::~ActiveEntry() {
108 //-----------------------------------------------------------------------------
110 // This structure keeps track of work items that are attempting to create or
111 // open cache entries or the backend itself.
112 struct HttpCache::PendingOp
{
113 PendingOp() : disk_entry(NULL
), writer(NULL
) {}
116 disk_cache::Entry
* disk_entry
;
117 scoped_ptr
<disk_cache::Backend
> backend
;
119 CompletionCallback callback
; // BackendCallback.
120 WorkItemList pending_queue
;
123 //-----------------------------------------------------------------------------
125 // The type of operation represented by a work item.
126 enum WorkItemOperation
{
133 // A work item encapsulates a single request to the backend with all the
134 // information needed to complete that request.
135 class HttpCache::WorkItem
{
137 WorkItem(WorkItemOperation operation
, Transaction
* trans
, ActiveEntry
** entry
)
138 : operation_(operation
),
142 WorkItem(WorkItemOperation operation
, Transaction
* trans
,
143 const net::CompletionCallback
& cb
, disk_cache::Backend
** backend
)
144 : operation_(operation
),
151 // Calls back the transaction with the result of the operation.
152 void NotifyTransaction(int result
, ActiveEntry
* entry
) {
153 DCHECK(!entry
|| entry
->disk_entry
);
157 trans_
->io_callback().Run(result
);
160 // Notifies the caller about the operation completion. Returns true if the
161 // callback was invoked.
162 bool DoCallback(int result
, disk_cache::Backend
* backend
) {
165 if (!callback_
.is_null()) {
166 callback_
.Run(result
);
172 WorkItemOperation
operation() { return operation_
; }
173 void ClearTransaction() { trans_
= NULL
; }
174 void ClearEntry() { entry_
= NULL
; }
175 void ClearCallback() { callback_
.Reset(); }
176 bool Matches(Transaction
* trans
) const { return trans
== trans_
; }
177 bool IsValid() const { return trans_
|| entry_
|| !callback_
.is_null(); }
180 WorkItemOperation operation_
;
182 ActiveEntry
** entry_
;
183 net::CompletionCallback callback_
; // User callback.
184 disk_cache::Backend
** backend_
;
187 //-----------------------------------------------------------------------------
189 // This class encapsulates a transaction whose only purpose is to write metadata
191 class HttpCache::MetadataWriter
{
193 explicit MetadataWriter(HttpCache::Transaction
* trans
)
194 : transaction_(trans
),
201 // Implements the bulk of HttpCache::WriteMetadata.
202 void Write(const GURL
& url
, base::Time expected_response_time
, IOBuffer
* buf
,
206 void VerifyResponse(int result
);
208 void OnIOComplete(int result
);
210 scoped_ptr
<HttpCache::Transaction
> transaction_
;
212 scoped_refptr
<IOBuffer
> buf_
;
214 base::Time expected_response_time_
;
215 HttpRequestInfo request_info_
;
216 DISALLOW_COPY_AND_ASSIGN(MetadataWriter
);
219 void HttpCache::MetadataWriter::Write(const GURL
& url
,
220 base::Time expected_response_time
,
221 IOBuffer
* buf
, int buf_len
) {
222 DCHECK_GT(buf_len
, 0);
225 request_info_
.url
= url
;
226 request_info_
.method
= "GET";
227 request_info_
.load_flags
= LOAD_ONLY_FROM_CACHE
;
229 expected_response_time_
= expected_response_time
;
234 int rv
= transaction_
->Start(
236 base::Bind(&MetadataWriter::OnIOComplete
, base::Unretained(this)),
238 if (rv
!= ERR_IO_PENDING
)
242 void HttpCache::MetadataWriter::VerifyResponse(int result
) {
245 return SelfDestroy();
247 const HttpResponseInfo
* response_info
= transaction_
->GetResponseInfo();
248 DCHECK(response_info
->was_cached
);
249 if (response_info
->response_time
!= expected_response_time_
)
250 return SelfDestroy();
252 result
= transaction_
->WriteMetadata(
255 base::Bind(&MetadataWriter::OnIOComplete
, base::Unretained(this)));
256 if (result
!= ERR_IO_PENDING
)
260 void HttpCache::MetadataWriter::SelfDestroy() {
264 void HttpCache::MetadataWriter::OnIOComplete(int result
) {
266 return VerifyResponse(result
);
270 //-----------------------------------------------------------------------------
272 class HttpCache::QuicServerInfoFactoryAdaptor
: public QuicServerInfoFactory
{
274 QuicServerInfoFactoryAdaptor(HttpCache
* http_cache
)
275 : http_cache_(http_cache
) {
278 virtual QuicServerInfo
* GetForServer(
279 const QuicServerId
& server_id
) OVERRIDE
{
280 return new DiskCacheBasedQuicServerInfo(server_id
, http_cache_
);
284 HttpCache
* const http_cache_
;
287 //-----------------------------------------------------------------------------
288 HttpCache::HttpCache(const net::HttpNetworkSession::Params
& params
,
289 BackendFactory
* backend_factory
)
290 : net_log_(params
.net_log
),
291 backend_factory_(backend_factory
),
292 building_backend_(false),
294 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params
))),
295 weak_factory_(this) {
296 SetupQuicServerInfoFactory(network_layer_
->GetSession());
300 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
301 // |session| is shared.
302 HttpCache::HttpCache(HttpNetworkSession
* session
,
303 BackendFactory
* backend_factory
)
304 : net_log_(session
->net_log()),
305 backend_factory_(backend_factory
),
306 building_backend_(false),
308 network_layer_(new HttpNetworkLayer(session
)),
309 weak_factory_(this) {
312 HttpCache::HttpCache(HttpTransactionFactory
* network_layer
,
314 BackendFactory
* backend_factory
)
316 backend_factory_(backend_factory
),
317 building_backend_(false),
319 network_layer_(network_layer
),
320 weak_factory_(this) {
321 SetupQuicServerInfoFactory(network_layer_
->GetSession());
324 HttpCache::~HttpCache() {
325 // Transactions should see an invalid cache after this point; otherwise they
326 // could see an inconsistent object (half destroyed).
327 weak_factory_
.InvalidateWeakPtrs();
329 // If we have any active entries remaining, then we need to deactivate them.
330 // We may have some pending calls to OnProcessPendingQueue, but since those
331 // won't run (due to our destruction), we can simply ignore the corresponding
332 // will_process_pending_queue flag.
333 while (!active_entries_
.empty()) {
334 ActiveEntry
* entry
= active_entries_
.begin()->second
;
335 entry
->will_process_pending_queue
= false;
336 entry
->pending_queue
.clear();
337 entry
->readers
.clear();
338 entry
->writer
= NULL
;
339 DeactivateEntry(entry
);
342 STLDeleteElements(&doomed_entries_
);
344 // Before deleting pending_ops_, we have to make sure that the disk cache is
345 // done with said operations, or it will attempt to use deleted data.
348 PendingOpsMap::iterator pending_it
= pending_ops_
.begin();
349 for (; pending_it
!= pending_ops_
.end(); ++pending_it
) {
350 // We are not notifying the transactions about the cache going away, even
351 // though they are waiting for a callback that will never fire.
352 PendingOp
* pending_op
= pending_it
->second
;
353 delete pending_op
->writer
;
354 bool delete_pending_op
= true;
355 if (building_backend_
) {
356 // If we don't have a backend, when its construction finishes it will
357 // deliver the callbacks.
358 if (!pending_op
->callback
.is_null()) {
359 // If not null, the callback will delete the pending operation later.
360 delete_pending_op
= false;
363 pending_op
->callback
.Reset();
366 STLDeleteElements(&pending_op
->pending_queue
);
367 if (delete_pending_op
)
372 int HttpCache::GetBackend(disk_cache::Backend
** backend
,
373 const CompletionCallback
& callback
) {
374 DCHECK(!callback
.is_null());
376 if (disk_cache_
.get()) {
377 *backend
= disk_cache_
.get();
381 return CreateBackend(backend
, callback
);
384 disk_cache::Backend
* HttpCache::GetCurrentBackend() const {
385 return disk_cache_
.get();
389 bool HttpCache::ParseResponseInfo(const char* data
, int len
,
390 HttpResponseInfo
* response_info
,
391 bool* response_truncated
) {
392 Pickle
pickle(data
, len
);
393 return response_info
->InitFromPickle(pickle
, response_truncated
);
396 void HttpCache::WriteMetadata(const GURL
& url
,
397 RequestPriority priority
,
398 base::Time expected_response_time
,
404 // Do lazy initialization of disk cache if needed.
405 if (!disk_cache_
.get()) {
406 // We don't care about the result.
407 CreateBackend(NULL
, net::CompletionCallback());
410 HttpCache::Transaction
* trans
=
411 new HttpCache::Transaction(priority
, this);
412 MetadataWriter
* writer
= new MetadataWriter(trans
);
414 // The writer will self destruct when done.
415 writer
->Write(url
, expected_response_time
, buf
, buf_len
);
418 void HttpCache::CloseAllConnections() {
419 net::HttpNetworkLayer
* network
=
420 static_cast<net::HttpNetworkLayer
*>(network_layer_
.get());
421 HttpNetworkSession
* session
= network
->GetSession();
423 session
->CloseAllConnections();
426 void HttpCache::CloseIdleConnections() {
427 net::HttpNetworkLayer
* network
=
428 static_cast<net::HttpNetworkLayer
*>(network_layer_
.get());
429 HttpNetworkSession
* session
= network
->GetSession();
431 session
->CloseIdleConnections();
434 void HttpCache::OnExternalCacheHit(const GURL
& url
,
435 const std::string
& http_method
) {
436 if (!disk_cache_
.get())
439 HttpRequestInfo request_info
;
440 request_info
.url
= url
;
441 request_info
.method
= http_method
;
442 std::string key
= GenerateCacheKey(&request_info
);
443 disk_cache_
->OnExternalCacheHit(key
);
446 void HttpCache::InitializeInfiniteCache(const base::FilePath
& path
) {
447 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
449 base::WorkerPool::PostTask(FROM_HERE
, base::Bind(&DeletePath
, path
), true);
452 int HttpCache::CreateTransaction(RequestPriority priority
,
453 scoped_ptr
<HttpTransaction
>* trans
) {
454 // Do lazy initialization of disk cache if needed.
455 if (!disk_cache_
.get()) {
456 // We don't care about the result.
457 CreateBackend(NULL
, net::CompletionCallback());
460 trans
->reset(new HttpCache::Transaction(priority
, this));
464 HttpCache
* HttpCache::GetCache() {
468 HttpNetworkSession
* HttpCache::GetSession() {
469 net::HttpNetworkLayer
* network
=
470 static_cast<net::HttpNetworkLayer
*>(network_layer_
.get());
471 return network
->GetSession();
474 scoped_ptr
<HttpTransactionFactory
>
475 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
476 scoped_ptr
<HttpTransactionFactory
> new_network_layer
) {
477 scoped_ptr
<HttpTransactionFactory
> old_network_layer(network_layer_
.Pass());
478 network_layer_
= new_network_layer
.Pass();
479 return old_network_layer
.Pass();
482 //-----------------------------------------------------------------------------
484 int HttpCache::CreateBackend(disk_cache::Backend
** backend
,
485 const net::CompletionCallback
& callback
) {
486 if (!backend_factory_
.get())
489 building_backend_
= true;
491 scoped_ptr
<WorkItem
> item(new WorkItem(WI_CREATE_BACKEND
, NULL
, callback
,
494 // This is the only operation that we can do that is not related to any given
495 // entry, so we use an empty key for it.
496 PendingOp
* pending_op
= GetPendingOp(std::string());
497 if (pending_op
->writer
) {
498 if (!callback
.is_null())
499 pending_op
->pending_queue
.push_back(item
.release());
500 return ERR_IO_PENDING
;
503 DCHECK(pending_op
->pending_queue
.empty());
505 pending_op
->writer
= item
.release();
506 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
507 GetWeakPtr(), pending_op
);
509 int rv
= backend_factory_
->CreateBackend(net_log_
, &pending_op
->backend
,
510 pending_op
->callback
);
511 if (rv
!= ERR_IO_PENDING
) {
512 pending_op
->writer
->ClearCallback();
513 pending_op
->callback
.Run(rv
);
519 int HttpCache::GetBackendForTransaction(Transaction
* trans
) {
520 if (disk_cache_
.get())
523 if (!building_backend_
)
526 WorkItem
* item
= new WorkItem(
527 WI_CREATE_BACKEND
, trans
, net::CompletionCallback(), NULL
);
528 PendingOp
* pending_op
= GetPendingOp(std::string());
529 DCHECK(pending_op
->writer
);
530 pending_op
->pending_queue
.push_back(item
);
531 return ERR_IO_PENDING
;
534 // Generate a key that can be used inside the cache.
535 std::string
HttpCache::GenerateCacheKey(const HttpRequestInfo
* request
) {
536 // Strip out the reference, username, and password sections of the URL.
537 std::string url
= HttpUtil::SpecForRequest(request
->url
);
539 DCHECK(mode_
!= DISABLE
);
540 if (mode_
== NORMAL
) {
541 // No valid URL can begin with numerals, so we should not have to worry
542 // about collisions with normal URLs.
543 if (request
->upload_data_stream
&&
544 request
->upload_data_stream
->identifier()) {
545 url
.insert(0, base::StringPrintf(
546 "%" PRId64
"/", request
->upload_data_stream
->identifier()));
551 // In playback and record mode, we cache everything.
553 // Lazily initialize.
554 if (playback_cache_map_
== NULL
)
555 playback_cache_map_
.reset(new PlaybackCacheMap());
557 // Each time we request an item from the cache, we tag it with a
558 // generation number. During playback, multiple fetches for the same
559 // item will use the same generation number and pull the proper
560 // instance of an URL from the cache.
562 DCHECK(playback_cache_map_
!= NULL
);
563 if (playback_cache_map_
->find(url
) != playback_cache_map_
->end())
564 generation
= (*playback_cache_map_
)[url
];
565 (*playback_cache_map_
)[url
] = generation
+ 1;
567 // The key into the cache is GENERATION # + METHOD + URL.
568 std::string result
= base::IntToString(generation
);
569 result
.append(request
->method
);
574 void HttpCache::DoomActiveEntry(const std::string
& key
) {
575 ActiveEntriesMap::iterator it
= active_entries_
.find(key
);
576 if (it
== active_entries_
.end())
579 // This is not a performance critical operation, this is handling an error
580 // condition so it is OK to look up the entry again.
581 int rv
= DoomEntry(key
, NULL
);
585 int HttpCache::DoomEntry(const std::string
& key
, Transaction
* trans
) {
586 // Need to abandon the ActiveEntry, but any transaction attached to the entry
587 // should not be impacted. Dooming an entry only means that it will no
588 // longer be returned by FindActiveEntry (and it will also be destroyed once
589 // all consumers are finished with the entry).
590 ActiveEntriesMap::iterator it
= active_entries_
.find(key
);
591 if (it
== active_entries_
.end()) {
593 return AsyncDoomEntry(key
, trans
);
596 ActiveEntry
* entry
= it
->second
;
597 active_entries_
.erase(it
);
599 // We keep track of doomed entries so that we can ensure that they are
600 // cleaned up properly when the cache is destroyed.
601 doomed_entries_
.insert(entry
);
603 entry
->disk_entry
->Doom();
604 entry
->doomed
= true;
606 DCHECK(entry
->writer
|| !entry
->readers
.empty());
610 int HttpCache::AsyncDoomEntry(const std::string
& key
, Transaction
* trans
) {
611 WorkItem
* item
= new WorkItem(WI_DOOM_ENTRY
, trans
, NULL
);
612 PendingOp
* pending_op
= GetPendingOp(key
);
613 if (pending_op
->writer
) {
614 pending_op
->pending_queue
.push_back(item
);
615 return ERR_IO_PENDING
;
618 DCHECK(pending_op
->pending_queue
.empty());
620 pending_op
->writer
= item
;
621 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
622 GetWeakPtr(), pending_op
);
624 int rv
= disk_cache_
->DoomEntry(key
, pending_op
->callback
);
625 if (rv
!= ERR_IO_PENDING
) {
626 item
->ClearTransaction();
627 pending_op
->callback
.Run(rv
);
633 void HttpCache::DoomMainEntryForUrl(const GURL
& url
) {
637 HttpRequestInfo temp_info
;
639 temp_info
.method
= "GET";
640 std::string key
= GenerateCacheKey(&temp_info
);
642 // Defer to DoomEntry if there is an active entry, otherwise call
643 // AsyncDoomEntry without triggering a callback.
644 if (active_entries_
.count(key
))
645 DoomEntry(key
, NULL
);
647 AsyncDoomEntry(key
, NULL
);
650 void HttpCache::FinalizeDoomedEntry(ActiveEntry
* entry
) {
651 DCHECK(entry
->doomed
);
652 DCHECK(!entry
->writer
);
653 DCHECK(entry
->readers
.empty());
654 DCHECK(entry
->pending_queue
.empty());
656 ActiveEntriesSet::iterator it
= doomed_entries_
.find(entry
);
657 DCHECK(it
!= doomed_entries_
.end());
658 doomed_entries_
.erase(it
);
663 HttpCache::ActiveEntry
* HttpCache::FindActiveEntry(const std::string
& key
) {
664 ActiveEntriesMap::const_iterator it
= active_entries_
.find(key
);
665 return it
!= active_entries_
.end() ? it
->second
: NULL
;
668 HttpCache::ActiveEntry
* HttpCache::ActivateEntry(
669 disk_cache::Entry
* disk_entry
) {
670 DCHECK(!FindActiveEntry(disk_entry
->GetKey()));
671 ActiveEntry
* entry
= new ActiveEntry(disk_entry
);
672 active_entries_
[disk_entry
->GetKey()] = entry
;
676 void HttpCache::DeactivateEntry(ActiveEntry
* entry
) {
677 DCHECK(!entry
->will_process_pending_queue
);
678 DCHECK(!entry
->doomed
);
679 DCHECK(!entry
->writer
);
680 DCHECK(entry
->disk_entry
);
681 DCHECK(entry
->readers
.empty());
682 DCHECK(entry
->pending_queue
.empty());
684 std::string key
= entry
->disk_entry
->GetKey();
686 return SlowDeactivateEntry(entry
);
688 ActiveEntriesMap::iterator it
= active_entries_
.find(key
);
689 DCHECK(it
!= active_entries_
.end());
690 DCHECK(it
->second
== entry
);
692 active_entries_
.erase(it
);
696 // We don't know this entry's key so we have to find it without it.
697 void HttpCache::SlowDeactivateEntry(ActiveEntry
* entry
) {
698 for (ActiveEntriesMap::iterator it
= active_entries_
.begin();
699 it
!= active_entries_
.end(); ++it
) {
700 if (it
->second
== entry
) {
701 active_entries_
.erase(it
);
708 HttpCache::PendingOp
* HttpCache::GetPendingOp(const std::string
& key
) {
709 DCHECK(!FindActiveEntry(key
));
711 PendingOpsMap::const_iterator it
= pending_ops_
.find(key
);
712 if (it
!= pending_ops_
.end())
715 PendingOp
* operation
= new PendingOp();
716 pending_ops_
[key
] = operation
;
720 void HttpCache::DeletePendingOp(PendingOp
* pending_op
) {
722 if (pending_op
->disk_entry
)
723 key
= pending_op
->disk_entry
->GetKey();
726 PendingOpsMap::iterator it
= pending_ops_
.find(key
);
727 DCHECK(it
!= pending_ops_
.end());
728 pending_ops_
.erase(it
);
730 for (PendingOpsMap::iterator it
= pending_ops_
.begin();
731 it
!= pending_ops_
.end(); ++it
) {
732 if (it
->second
== pending_op
) {
733 pending_ops_
.erase(it
);
738 DCHECK(pending_op
->pending_queue
.empty());
743 int HttpCache::OpenEntry(const std::string
& key
, ActiveEntry
** entry
,
744 Transaction
* trans
) {
745 ActiveEntry
* active_entry
= FindActiveEntry(key
);
747 *entry
= active_entry
;
751 WorkItem
* item
= new WorkItem(WI_OPEN_ENTRY
, trans
, entry
);
752 PendingOp
* pending_op
= GetPendingOp(key
);
753 if (pending_op
->writer
) {
754 pending_op
->pending_queue
.push_back(item
);
755 return ERR_IO_PENDING
;
758 DCHECK(pending_op
->pending_queue
.empty());
760 pending_op
->writer
= item
;
761 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
762 GetWeakPtr(), pending_op
);
764 int rv
= disk_cache_
->OpenEntry(key
, &(pending_op
->disk_entry
),
765 pending_op
->callback
);
766 if (rv
!= ERR_IO_PENDING
) {
767 item
->ClearTransaction();
768 pending_op
->callback
.Run(rv
);
774 int HttpCache::CreateEntry(const std::string
& key
, ActiveEntry
** entry
,
775 Transaction
* trans
) {
776 if (FindActiveEntry(key
)) {
777 return ERR_CACHE_RACE
;
780 WorkItem
* item
= new WorkItem(WI_CREATE_ENTRY
, trans
, entry
);
781 PendingOp
* pending_op
= GetPendingOp(key
);
782 if (pending_op
->writer
) {
783 pending_op
->pending_queue
.push_back(item
);
784 return ERR_IO_PENDING
;
787 DCHECK(pending_op
->pending_queue
.empty());
789 pending_op
->writer
= item
;
790 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
791 GetWeakPtr(), pending_op
);
793 int rv
= disk_cache_
->CreateEntry(key
, &(pending_op
->disk_entry
),
794 pending_op
->callback
);
795 if (rv
!= ERR_IO_PENDING
) {
796 item
->ClearTransaction();
797 pending_op
->callback
.Run(rv
);
803 void HttpCache::DestroyEntry(ActiveEntry
* entry
) {
805 FinalizeDoomedEntry(entry
);
807 DeactivateEntry(entry
);
811 int HttpCache::AddTransactionToEntry(ActiveEntry
* entry
, Transaction
* trans
) {
813 DCHECK(entry
->disk_entry
);
815 // We implement a basic reader/writer lock for the disk cache entry. If
816 // there is already a writer, then everyone has to wait for the writer to
817 // finish before they can access the cache entry. There can be multiple
820 // NOTE: If the transaction can only write, then the entry should not be in
821 // use (since any existing entry should have already been doomed).
823 if (entry
->writer
|| entry
->will_process_pending_queue
) {
824 entry
->pending_queue
.push_back(trans
);
825 return ERR_IO_PENDING
;
828 if (trans
->mode() & Transaction::WRITE
) {
829 // transaction needs exclusive access to the entry
830 if (entry
->readers
.empty()) {
831 entry
->writer
= trans
;
833 entry
->pending_queue
.push_back(trans
);
834 return ERR_IO_PENDING
;
837 // transaction needs read access to the entry
838 entry
->readers
.push_back(trans
);
841 // We do this before calling EntryAvailable to force any further calls to
842 // AddTransactionToEntry to add their transaction to the pending queue, which
843 // ensures FIFO ordering.
844 if (!entry
->writer
&& !entry
->pending_queue
.empty())
845 ProcessPendingQueue(entry
);
850 void HttpCache::DoneWithEntry(ActiveEntry
* entry
, Transaction
* trans
,
852 // If we already posted a task to move on to the next transaction and this was
853 // the writer, there is nothing to cancel.
854 if (entry
->will_process_pending_queue
&& entry
->readers
.empty())
858 DCHECK(trans
== entry
->writer
);
860 // Assume there was a failure.
861 bool success
= false;
863 DCHECK(entry
->disk_entry
);
864 // This is a successful operation in the sense that we want to keep the
866 success
= trans
->AddTruncatedFlag();
867 // The previous operation may have deleted the entry.
871 DoneWritingToEntry(entry
, success
);
873 DoneReadingFromEntry(entry
, trans
);
877 void HttpCache::DoneWritingToEntry(ActiveEntry
* entry
, bool success
) {
878 DCHECK(entry
->readers
.empty());
880 entry
->writer
= NULL
;
883 ProcessPendingQueue(entry
);
885 DCHECK(!entry
->will_process_pending_queue
);
887 // We failed to create this entry.
888 TransactionList pending_queue
;
889 pending_queue
.swap(entry
->pending_queue
);
891 entry
->disk_entry
->Doom();
894 // We need to do something about these pending entries, which now need to
895 // be added to a new entry.
896 while (!pending_queue
.empty()) {
897 // ERR_CACHE_RACE causes the transaction to restart the whole process.
898 pending_queue
.front()->io_callback().Run(ERR_CACHE_RACE
);
899 pending_queue
.pop_front();
904 void HttpCache::DoneReadingFromEntry(ActiveEntry
* entry
, Transaction
* trans
) {
905 DCHECK(!entry
->writer
);
907 TransactionList::iterator it
=
908 std::find(entry
->readers
.begin(), entry
->readers
.end(), trans
);
909 DCHECK(it
!= entry
->readers
.end());
911 entry
->readers
.erase(it
);
913 ProcessPendingQueue(entry
);
916 void HttpCache::ConvertWriterToReader(ActiveEntry
* entry
) {
917 DCHECK(entry
->writer
);
918 DCHECK(entry
->writer
->mode() == Transaction::READ_WRITE
);
919 DCHECK(entry
->readers
.empty());
921 Transaction
* trans
= entry
->writer
;
923 entry
->writer
= NULL
;
924 entry
->readers
.push_back(trans
);
926 ProcessPendingQueue(entry
);
929 LoadState
HttpCache::GetLoadStateForPendingTransaction(
930 const Transaction
* trans
) {
931 ActiveEntriesMap::const_iterator i
= active_entries_
.find(trans
->key());
932 if (i
== active_entries_
.end()) {
933 // If this is really a pending transaction, and it is not part of
934 // active_entries_, we should be creating the backend or the entry.
935 return LOAD_STATE_WAITING_FOR_CACHE
;
938 Transaction
* writer
= i
->second
->writer
;
939 return writer
? writer
->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE
;
942 void HttpCache::RemovePendingTransaction(Transaction
* trans
) {
943 ActiveEntriesMap::const_iterator i
= active_entries_
.find(trans
->key());
945 if (i
!= active_entries_
.end())
946 found
= RemovePendingTransactionFromEntry(i
->second
, trans
);
951 if (building_backend_
) {
952 PendingOpsMap::const_iterator j
= pending_ops_
.find(std::string());
953 if (j
!= pending_ops_
.end())
954 found
= RemovePendingTransactionFromPendingOp(j
->second
, trans
);
960 PendingOpsMap::const_iterator j
= pending_ops_
.find(trans
->key());
961 if (j
!= pending_ops_
.end())
962 found
= RemovePendingTransactionFromPendingOp(j
->second
, trans
);
967 ActiveEntriesSet::iterator k
= doomed_entries_
.begin();
968 for (; k
!= doomed_entries_
.end() && !found
; ++k
)
969 found
= RemovePendingTransactionFromEntry(*k
, trans
);
971 DCHECK(found
) << "Pending transaction not found";
974 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry
* entry
,
975 Transaction
* trans
) {
976 TransactionList
& pending_queue
= entry
->pending_queue
;
978 TransactionList::iterator j
=
979 find(pending_queue
.begin(), pending_queue
.end(), trans
);
980 if (j
== pending_queue
.end())
983 pending_queue
.erase(j
);
987 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp
* pending_op
,
988 Transaction
* trans
) {
989 if (pending_op
->writer
->Matches(trans
)) {
990 pending_op
->writer
->ClearTransaction();
991 pending_op
->writer
->ClearEntry();
994 WorkItemList
& pending_queue
= pending_op
->pending_queue
;
996 WorkItemList::iterator it
= pending_queue
.begin();
997 for (; it
!= pending_queue
.end(); ++it
) {
998 if ((*it
)->Matches(trans
)) {
1000 pending_queue
.erase(it
);
1007 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession
* session
) {
1008 if (session
&& session
->params().enable_quic_persist_server_info
&&
1009 !session
->quic_stream_factory()->has_quic_server_info_factory()) {
1010 DCHECK(!quic_server_info_factory_
);
1011 quic_server_info_factory_
.reset(new QuicServerInfoFactoryAdaptor(this));
1012 session
->quic_stream_factory()->set_quic_server_info_factory(
1013 quic_server_info_factory_
.get());
1017 void HttpCache::ProcessPendingQueue(ActiveEntry
* entry
) {
1018 // Multiple readers may finish with an entry at once, so we want to batch up
1019 // calls to OnProcessPendingQueue. This flag also tells us that we should
1020 // not delete the entry before OnProcessPendingQueue runs.
1021 if (entry
->will_process_pending_queue
)
1023 entry
->will_process_pending_queue
= true;
1025 base::MessageLoop::current()->PostTask(
1027 base::Bind(&HttpCache::OnProcessPendingQueue
, GetWeakPtr(), entry
));
1030 void HttpCache::OnProcessPendingQueue(ActiveEntry
* entry
) {
1031 entry
->will_process_pending_queue
= false;
1032 DCHECK(!entry
->writer
);
1034 // If no one is interested in this entry, then we can deactivate it.
1035 if (entry
->pending_queue
.empty()) {
1036 if (entry
->readers
.empty())
1037 DestroyEntry(entry
);
1041 // Promote next transaction from the pending queue.
1042 Transaction
* next
= entry
->pending_queue
.front();
1043 if ((next
->mode() & Transaction::WRITE
) && !entry
->readers
.empty())
1044 return; // Have to wait.
1046 entry
->pending_queue
.erase(entry
->pending_queue
.begin());
1048 int rv
= AddTransactionToEntry(entry
, next
);
1049 if (rv
!= ERR_IO_PENDING
) {
1050 next
->io_callback().Run(rv
);
1054 void HttpCache::OnIOComplete(int result
, PendingOp
* pending_op
) {
1055 WorkItemOperation op
= pending_op
->writer
->operation();
1057 // Completing the creation of the backend is simpler than the other cases.
1058 if (op
== WI_CREATE_BACKEND
)
1059 return OnBackendCreated(result
, pending_op
);
1061 scoped_ptr
<WorkItem
> item(pending_op
->writer
);
1062 bool fail_requests
= false;
1064 ActiveEntry
* entry
= NULL
;
1067 if (op
== WI_DOOM_ENTRY
) {
1068 // Anything after a Doom has to be restarted.
1069 fail_requests
= true;
1070 } else if (item
->IsValid()) {
1071 key
= pending_op
->disk_entry
->GetKey();
1072 entry
= ActivateEntry(pending_op
->disk_entry
);
1074 // The writer transaction is gone.
1075 if (op
== WI_CREATE_ENTRY
)
1076 pending_op
->disk_entry
->Doom();
1077 pending_op
->disk_entry
->Close();
1078 pending_op
->disk_entry
= NULL
;
1079 fail_requests
= true;
1083 // We are about to notify a bunch of transactions, and they may decide to
1084 // re-issue a request (or send a different one). If we don't delete
1085 // pending_op, the new request will be appended to the end of the list, and
1086 // we'll see it again from this point before it has a chance to complete (and
1087 // we'll be messing out the request order). The down side is that if for some
1088 // reason notifying request A ends up cancelling request B (for the same key),
1089 // we won't find request B anywhere (because it would be in a local variable
1090 // here) and that's bad. If there is a chance for that to happen, we'll have
1091 // to move the callback used to be a CancelableCallback. By the way, for this
1092 // to happen the action (to cancel B) has to be synchronous to the
1093 // notification for request A.
1094 WorkItemList pending_items
;
1095 pending_items
.swap(pending_op
->pending_queue
);
1096 DeletePendingOp(pending_op
);
1098 item
->NotifyTransaction(result
, entry
);
1100 while (!pending_items
.empty()) {
1101 item
.reset(pending_items
.front());
1102 pending_items
.pop_front();
1104 if (item
->operation() == WI_DOOM_ENTRY
) {
1105 // A queued doom request is always a race.
1106 fail_requests
= true;
1107 } else if (result
== OK
) {
1108 entry
= FindActiveEntry(key
);
1110 fail_requests
= true;
1113 if (fail_requests
) {
1114 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1118 if (item
->operation() == WI_CREATE_ENTRY
) {
1120 // A second Create request, but the first request succeeded.
1121 item
->NotifyTransaction(ERR_CACHE_CREATE_FAILURE
, NULL
);
1123 if (op
!= WI_CREATE_ENTRY
) {
1124 // Failed Open followed by a Create.
1125 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1126 fail_requests
= true;
1128 item
->NotifyTransaction(result
, entry
);
1132 if (op
== WI_CREATE_ENTRY
&& result
!= OK
) {
1133 // Failed Create followed by an Open.
1134 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1135 fail_requests
= true;
1137 item
->NotifyTransaction(result
, entry
);
1144 void HttpCache::OnPendingOpComplete(const base::WeakPtr
<HttpCache
>& cache
,
1145 PendingOp
* pending_op
,
1148 cache
->OnIOComplete(rv
, pending_op
);
1150 // The callback was cancelled so we should delete the pending_op that
1151 // was used with this callback.
1156 void HttpCache::OnBackendCreated(int result
, PendingOp
* pending_op
) {
1157 scoped_ptr
<WorkItem
> item(pending_op
->writer
);
1158 WorkItemOperation op
= item
->operation();
1159 DCHECK_EQ(WI_CREATE_BACKEND
, op
);
1161 // We don't need the callback anymore.
1162 pending_op
->callback
.Reset();
1164 if (backend_factory_
.get()) {
1165 // We may end up calling OnBackendCreated multiple times if we have pending
1166 // work items. The first call saves the backend and releases the factory,
1167 // and the last call clears building_backend_.
1168 backend_factory_
.reset(); // Reclaim memory.
1170 disk_cache_
= pending_op
->backend
.Pass();
1173 if (!pending_op
->pending_queue
.empty()) {
1174 WorkItem
* pending_item
= pending_op
->pending_queue
.front();
1175 pending_op
->pending_queue
.pop_front();
1176 DCHECK_EQ(WI_CREATE_BACKEND
, pending_item
->operation());
1178 // We want to process a single callback at a time, because the cache may
1179 // go away from the callback.
1180 pending_op
->writer
= pending_item
;
1182 base::MessageLoop::current()->PostTask(
1184 base::Bind(&HttpCache::OnBackendCreated
, GetWeakPtr(),
1185 result
, pending_op
));
1187 building_backend_
= false;
1188 DeletePendingOp(pending_op
);
1191 // The cache may be gone when we return from the callback.
1192 if (!item
->DoCallback(result
, disk_cache_
.get()))
1193 item
->NotifyTransaction(result
, NULL
);