Probably broke Win7 Tests (dbg)(6). http://build.chromium.org/p/chromium.win/builders...
[chromium-blink-merge.git] / net / http / http_cache.cc
blobef2e3fdd231acc92ce65dd86060450602e3087f1
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/http/http_cache.h"
7 #include <algorithm>
9 #include "base/compiler_specific.h"
11 #if defined(OS_POSIX)
12 #include <unistd.h>
13 #endif
15 #include "base/bind.h"
16 #include "base/bind_helpers.h"
17 #include "base/callback.h"
18 #include "base/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"
46 namespace {
48 // Adaptor to delete a file on a worker thread.
49 void DeletePath(base::FilePath path) {
50 base::DeleteFile(path, false);
53 } // namespace
55 namespace net {
57 HttpCache::DefaultBackend::DefaultBackend(CacheType type,
58 BackendType backend_type,
59 const base::FilePath& path,
60 int max_bytes,
61 base::MessageLoopProxy* thread)
62 : type_(type),
63 backend_type_(backend_type),
64 path_(path),
65 max_bytes_(max_bytes),
66 thread_(thread) {
69 HttpCache::DefaultBackend::~DefaultBackend() {}
71 // static
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_,
82 backend_type_,
83 path_,
84 max_bytes_,
85 true,
86 thread_.get(),
87 net_log,
88 backend,
89 callback);
92 //-----------------------------------------------------------------------------
94 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
95 : disk_entry(entry),
96 writer(NULL),
97 will_process_pending_queue(false),
98 doomed(false) {
101 HttpCache::ActiveEntry::~ActiveEntry() {
102 if (disk_entry) {
103 disk_entry->Close();
104 disk_entry = NULL;
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) {}
114 ~PendingOp() {}
116 disk_cache::Entry* disk_entry;
117 scoped_ptr<disk_cache::Backend> backend;
118 WorkItem* writer;
119 CompletionCallback callback; // BackendCallback.
120 WorkItemList pending_queue;
123 //-----------------------------------------------------------------------------
125 // The type of operation represented by a work item.
126 enum WorkItemOperation {
127 WI_CREATE_BACKEND,
128 WI_OPEN_ENTRY,
129 WI_CREATE_ENTRY,
130 WI_DOOM_ENTRY
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 {
136 public:
137 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
138 : operation_(operation),
139 trans_(trans),
140 entry_(entry),
141 backend_(NULL) {}
142 WorkItem(WorkItemOperation operation, Transaction* trans,
143 const net::CompletionCallback& cb, disk_cache::Backend** backend)
144 : operation_(operation),
145 trans_(trans),
146 entry_(NULL),
147 callback_(cb),
148 backend_(backend) {}
149 ~WorkItem() {}
151 // Calls back the transaction with the result of the operation.
152 void NotifyTransaction(int result, ActiveEntry* entry) {
153 DCHECK(!entry || entry->disk_entry);
154 if (entry_)
155 *entry_ = entry;
156 if (trans_)
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) {
163 if (backend_)
164 *backend_ = backend;
165 if (!callback_.is_null()) {
166 callback_.Run(result);
167 return true;
169 return false;
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(); }
179 private:
180 WorkItemOperation operation_;
181 Transaction* trans_;
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
190 // to a given entry.
191 class HttpCache::MetadataWriter {
192 public:
193 explicit MetadataWriter(HttpCache::Transaction* trans)
194 : transaction_(trans),
195 verified_(false),
196 buf_len_(0) {
199 ~MetadataWriter() {}
201 // Implements the bulk of HttpCache::WriteMetadata.
202 void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf,
203 int buf_len);
205 private:
206 void VerifyResponse(int result);
207 void SelfDestroy();
208 void OnIOComplete(int result);
210 scoped_ptr<HttpCache::Transaction> transaction_;
211 bool verified_;
212 scoped_refptr<IOBuffer> buf_;
213 int buf_len_;
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);
223 DCHECK(buf);
224 DCHECK(buf->data());
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;
230 buf_ = buf;
231 buf_len_ = buf_len;
232 verified_ = false;
234 int rv = transaction_->Start(
235 &request_info_,
236 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)),
237 BoundNetLog());
238 if (rv != ERR_IO_PENDING)
239 VerifyResponse(rv);
242 void HttpCache::MetadataWriter::VerifyResponse(int result) {
243 verified_ = true;
244 if (result != OK)
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(
253 buf_.get(),
254 buf_len_,
255 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)));
256 if (result != ERR_IO_PENDING)
257 SelfDestroy();
260 void HttpCache::MetadataWriter::SelfDestroy() {
261 delete this;
264 void HttpCache::MetadataWriter::OnIOComplete(int result) {
265 if (!verified_)
266 return VerifyResponse(result);
267 SelfDestroy();
270 //-----------------------------------------------------------------------------
272 class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory {
273 public:
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_);
283 private:
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),
293 bypass_lock_for_test_(false),
294 mode_(NORMAL),
295 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))),
296 weak_factory_(this) {
297 SetupQuicServerInfoFactory(network_layer_->GetSession());
301 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
302 // |session| is shared.
303 HttpCache::HttpCache(HttpNetworkSession* session,
304 BackendFactory* backend_factory)
305 : net_log_(session->net_log()),
306 backend_factory_(backend_factory),
307 building_backend_(false),
308 bypass_lock_for_test_(false),
309 mode_(NORMAL),
310 network_layer_(new HttpNetworkLayer(session)),
311 weak_factory_(this) {
314 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
315 NetLog* net_log,
316 BackendFactory* backend_factory)
317 : net_log_(net_log),
318 backend_factory_(backend_factory),
319 building_backend_(false),
320 bypass_lock_for_test_(false),
321 mode_(NORMAL),
322 network_layer_(network_layer),
323 weak_factory_(this) {
324 SetupQuicServerInfoFactory(network_layer_->GetSession());
327 HttpCache::~HttpCache() {
328 // Transactions should see an invalid cache after this point; otherwise they
329 // could see an inconsistent object (half destroyed).
330 weak_factory_.InvalidateWeakPtrs();
332 // If we have any active entries remaining, then we need to deactivate them.
333 // We may have some pending calls to OnProcessPendingQueue, but since those
334 // won't run (due to our destruction), we can simply ignore the corresponding
335 // will_process_pending_queue flag.
336 while (!active_entries_.empty()) {
337 ActiveEntry* entry = active_entries_.begin()->second;
338 entry->will_process_pending_queue = false;
339 entry->pending_queue.clear();
340 entry->readers.clear();
341 entry->writer = NULL;
342 DeactivateEntry(entry);
345 STLDeleteElements(&doomed_entries_);
347 // Before deleting pending_ops_, we have to make sure that the disk cache is
348 // done with said operations, or it will attempt to use deleted data.
349 disk_cache_.reset();
351 PendingOpsMap::iterator pending_it = pending_ops_.begin();
352 for (; pending_it != pending_ops_.end(); ++pending_it) {
353 // We are not notifying the transactions about the cache going away, even
354 // though they are waiting for a callback that will never fire.
355 PendingOp* pending_op = pending_it->second;
356 delete pending_op->writer;
357 bool delete_pending_op = true;
358 if (building_backend_) {
359 // If we don't have a backend, when its construction finishes it will
360 // deliver the callbacks.
361 if (!pending_op->callback.is_null()) {
362 // If not null, the callback will delete the pending operation later.
363 delete_pending_op = false;
365 } else {
366 pending_op->callback.Reset();
369 STLDeleteElements(&pending_op->pending_queue);
370 if (delete_pending_op)
371 delete pending_op;
375 int HttpCache::GetBackend(disk_cache::Backend** backend,
376 const CompletionCallback& callback) {
377 DCHECK(!callback.is_null());
379 if (disk_cache_.get()) {
380 *backend = disk_cache_.get();
381 return OK;
384 return CreateBackend(backend, callback);
387 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
388 return disk_cache_.get();
391 // static
392 bool HttpCache::ParseResponseInfo(const char* data, int len,
393 HttpResponseInfo* response_info,
394 bool* response_truncated) {
395 Pickle pickle(data, len);
396 return response_info->InitFromPickle(pickle, response_truncated);
399 void HttpCache::WriteMetadata(const GURL& url,
400 RequestPriority priority,
401 base::Time expected_response_time,
402 IOBuffer* buf,
403 int buf_len) {
404 if (!buf_len)
405 return;
407 // Do lazy initialization of disk cache if needed.
408 if (!disk_cache_.get()) {
409 // We don't care about the result.
410 CreateBackend(NULL, net::CompletionCallback());
413 HttpCache::Transaction* trans =
414 new HttpCache::Transaction(priority, this);
415 MetadataWriter* writer = new MetadataWriter(trans);
417 // The writer will self destruct when done.
418 writer->Write(url, expected_response_time, buf, buf_len);
421 void HttpCache::CloseAllConnections() {
422 HttpNetworkSession* session = GetSession();
423 if (session)
424 session->CloseAllConnections();
427 void HttpCache::CloseIdleConnections() {
428 HttpNetworkSession* session = GetSession();
429 if (session)
430 session->CloseIdleConnections();
433 void HttpCache::OnExternalCacheHit(const GURL& url,
434 const std::string& http_method) {
435 if (!disk_cache_.get())
436 return;
438 HttpRequestInfo request_info;
439 request_info.url = url;
440 request_info.method = http_method;
441 std::string key = GenerateCacheKey(&request_info);
442 disk_cache_->OnExternalCacheHit(key);
445 void HttpCache::InitializeInfiniteCache(const base::FilePath& path) {
446 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
447 return;
448 base::WorkerPool::PostTask(FROM_HERE, base::Bind(&DeletePath, path), true);
451 int HttpCache::CreateTransaction(RequestPriority priority,
452 scoped_ptr<HttpTransaction>* trans) {
453 // Do lazy initialization of disk cache if needed.
454 if (!disk_cache_.get()) {
455 // We don't care about the result.
456 CreateBackend(NULL, net::CompletionCallback());
459 HttpCache::Transaction* transaction =
460 new HttpCache::Transaction(priority, this);
461 if (bypass_lock_for_test_)
462 transaction->BypassLockForTest();
464 trans->reset(transaction);
465 return OK;
468 HttpCache* HttpCache::GetCache() {
469 return this;
472 HttpNetworkSession* HttpCache::GetSession() {
473 return network_layer_->GetSession();
476 scoped_ptr<HttpTransactionFactory>
477 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
478 scoped_ptr<HttpTransactionFactory> new_network_layer) {
479 scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass());
480 network_layer_ = new_network_layer.Pass();
481 return old_network_layer.Pass();
484 //-----------------------------------------------------------------------------
486 int HttpCache::CreateBackend(disk_cache::Backend** backend,
487 const net::CompletionCallback& callback) {
488 if (!backend_factory_.get())
489 return ERR_FAILED;
491 building_backend_ = true;
493 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
494 backend));
496 // This is the only operation that we can do that is not related to any given
497 // entry, so we use an empty key for it.
498 PendingOp* pending_op = GetPendingOp(std::string());
499 if (pending_op->writer) {
500 if (!callback.is_null())
501 pending_op->pending_queue.push_back(item.release());
502 return ERR_IO_PENDING;
505 DCHECK(pending_op->pending_queue.empty());
507 pending_op->writer = item.release();
508 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
509 GetWeakPtr(), pending_op);
511 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
512 pending_op->callback);
513 if (rv != ERR_IO_PENDING) {
514 pending_op->writer->ClearCallback();
515 pending_op->callback.Run(rv);
518 return rv;
521 int HttpCache::GetBackendForTransaction(Transaction* trans) {
522 if (disk_cache_.get())
523 return OK;
525 if (!building_backend_)
526 return ERR_FAILED;
528 WorkItem* item = new WorkItem(
529 WI_CREATE_BACKEND, trans, net::CompletionCallback(), NULL);
530 PendingOp* pending_op = GetPendingOp(std::string());
531 DCHECK(pending_op->writer);
532 pending_op->pending_queue.push_back(item);
533 return ERR_IO_PENDING;
536 // Generate a key that can be used inside the cache.
537 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
538 // Strip out the reference, username, and password sections of the URL.
539 std::string url = HttpUtil::SpecForRequest(request->url);
541 DCHECK(mode_ != DISABLE);
542 if (mode_ == NORMAL) {
543 // No valid URL can begin with numerals, so we should not have to worry
544 // about collisions with normal URLs.
545 if (request->upload_data_stream &&
546 request->upload_data_stream->identifier()) {
547 url.insert(0, base::StringPrintf(
548 "%" PRId64 "/", request->upload_data_stream->identifier()));
550 return url;
553 // In playback and record mode, we cache everything.
555 // Lazily initialize.
556 if (playback_cache_map_ == NULL)
557 playback_cache_map_.reset(new PlaybackCacheMap());
559 // Each time we request an item from the cache, we tag it with a
560 // generation number. During playback, multiple fetches for the same
561 // item will use the same generation number and pull the proper
562 // instance of an URL from the cache.
563 int generation = 0;
564 DCHECK(playback_cache_map_ != NULL);
565 if (playback_cache_map_->find(url) != playback_cache_map_->end())
566 generation = (*playback_cache_map_)[url];
567 (*playback_cache_map_)[url] = generation + 1;
569 // The key into the cache is GENERATION # + METHOD + URL.
570 std::string result = base::IntToString(generation);
571 result.append(request->method);
572 result.append(url);
573 return result;
576 void HttpCache::DoomActiveEntry(const std::string& key) {
577 ActiveEntriesMap::iterator it = active_entries_.find(key);
578 if (it == active_entries_.end())
579 return;
581 // This is not a performance critical operation, this is handling an error
582 // condition so it is OK to look up the entry again.
583 int rv = DoomEntry(key, NULL);
584 DCHECK_EQ(OK, rv);
587 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
588 // Need to abandon the ActiveEntry, but any transaction attached to the entry
589 // should not be impacted. Dooming an entry only means that it will no
590 // longer be returned by FindActiveEntry (and it will also be destroyed once
591 // all consumers are finished with the entry).
592 ActiveEntriesMap::iterator it = active_entries_.find(key);
593 if (it == active_entries_.end()) {
594 DCHECK(trans);
595 return AsyncDoomEntry(key, trans);
598 ActiveEntry* entry = it->second;
599 active_entries_.erase(it);
601 // We keep track of doomed entries so that we can ensure that they are
602 // cleaned up properly when the cache is destroyed.
603 doomed_entries_.insert(entry);
605 entry->disk_entry->Doom();
606 entry->doomed = true;
608 DCHECK(entry->writer || !entry->readers.empty());
609 return OK;
612 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
613 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
614 PendingOp* pending_op = GetPendingOp(key);
615 if (pending_op->writer) {
616 pending_op->pending_queue.push_back(item);
617 return ERR_IO_PENDING;
620 DCHECK(pending_op->pending_queue.empty());
622 pending_op->writer = item;
623 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
624 GetWeakPtr(), pending_op);
626 int rv = disk_cache_->DoomEntry(key, pending_op->callback);
627 if (rv != ERR_IO_PENDING) {
628 item->ClearTransaction();
629 pending_op->callback.Run(rv);
632 return rv;
635 void HttpCache::DoomMainEntryForUrl(const GURL& url) {
636 if (!disk_cache_)
637 return;
639 HttpRequestInfo temp_info;
640 temp_info.url = url;
641 temp_info.method = "GET";
642 std::string key = GenerateCacheKey(&temp_info);
644 // Defer to DoomEntry if there is an active entry, otherwise call
645 // AsyncDoomEntry without triggering a callback.
646 if (active_entries_.count(key))
647 DoomEntry(key, NULL);
648 else
649 AsyncDoomEntry(key, NULL);
652 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
653 DCHECK(entry->doomed);
654 DCHECK(!entry->writer);
655 DCHECK(entry->readers.empty());
656 DCHECK(entry->pending_queue.empty());
658 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
659 DCHECK(it != doomed_entries_.end());
660 doomed_entries_.erase(it);
662 delete entry;
665 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
666 ActiveEntriesMap::const_iterator it = active_entries_.find(key);
667 return it != active_entries_.end() ? it->second : NULL;
670 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
671 disk_cache::Entry* disk_entry) {
672 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
673 ActiveEntry* entry = new ActiveEntry(disk_entry);
674 active_entries_[disk_entry->GetKey()] = entry;
675 return entry;
678 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
679 DCHECK(!entry->will_process_pending_queue);
680 DCHECK(!entry->doomed);
681 DCHECK(!entry->writer);
682 DCHECK(entry->disk_entry);
683 DCHECK(entry->readers.empty());
684 DCHECK(entry->pending_queue.empty());
686 std::string key = entry->disk_entry->GetKey();
687 if (key.empty())
688 return SlowDeactivateEntry(entry);
690 ActiveEntriesMap::iterator it = active_entries_.find(key);
691 DCHECK(it != active_entries_.end());
692 DCHECK(it->second == entry);
694 active_entries_.erase(it);
695 delete entry;
698 // We don't know this entry's key so we have to find it without it.
699 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
700 for (ActiveEntriesMap::iterator it = active_entries_.begin();
701 it != active_entries_.end(); ++it) {
702 if (it->second == entry) {
703 active_entries_.erase(it);
704 delete entry;
705 break;
710 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
711 DCHECK(!FindActiveEntry(key));
713 PendingOpsMap::const_iterator it = pending_ops_.find(key);
714 if (it != pending_ops_.end())
715 return it->second;
717 PendingOp* operation = new PendingOp();
718 pending_ops_[key] = operation;
719 return operation;
722 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
723 std::string key;
724 if (pending_op->disk_entry)
725 key = pending_op->disk_entry->GetKey();
727 if (!key.empty()) {
728 PendingOpsMap::iterator it = pending_ops_.find(key);
729 DCHECK(it != pending_ops_.end());
730 pending_ops_.erase(it);
731 } else {
732 for (PendingOpsMap::iterator it = pending_ops_.begin();
733 it != pending_ops_.end(); ++it) {
734 if (it->second == pending_op) {
735 pending_ops_.erase(it);
736 break;
740 DCHECK(pending_op->pending_queue.empty());
742 delete pending_op;
745 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
746 Transaction* trans) {
747 ActiveEntry* active_entry = FindActiveEntry(key);
748 if (active_entry) {
749 *entry = active_entry;
750 return OK;
753 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
754 PendingOp* pending_op = GetPendingOp(key);
755 if (pending_op->writer) {
756 pending_op->pending_queue.push_back(item);
757 return ERR_IO_PENDING;
760 DCHECK(pending_op->pending_queue.empty());
762 pending_op->writer = item;
763 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
764 GetWeakPtr(), pending_op);
766 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry),
767 pending_op->callback);
768 if (rv != ERR_IO_PENDING) {
769 item->ClearTransaction();
770 pending_op->callback.Run(rv);
773 return rv;
776 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
777 Transaction* trans) {
778 if (FindActiveEntry(key)) {
779 return ERR_CACHE_RACE;
782 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
783 PendingOp* pending_op = GetPendingOp(key);
784 if (pending_op->writer) {
785 pending_op->pending_queue.push_back(item);
786 return ERR_IO_PENDING;
789 DCHECK(pending_op->pending_queue.empty());
791 pending_op->writer = item;
792 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
793 GetWeakPtr(), pending_op);
795 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
796 pending_op->callback);
797 if (rv != ERR_IO_PENDING) {
798 item->ClearTransaction();
799 pending_op->callback.Run(rv);
802 return rv;
805 void HttpCache::DestroyEntry(ActiveEntry* entry) {
806 if (entry->doomed) {
807 FinalizeDoomedEntry(entry);
808 } else {
809 DeactivateEntry(entry);
813 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
814 DCHECK(entry);
815 DCHECK(entry->disk_entry);
817 // We implement a basic reader/writer lock for the disk cache entry. If
818 // there is already a writer, then everyone has to wait for the writer to
819 // finish before they can access the cache entry. There can be multiple
820 // readers.
822 // NOTE: If the transaction can only write, then the entry should not be in
823 // use (since any existing entry should have already been doomed).
825 if (entry->writer || entry->will_process_pending_queue) {
826 entry->pending_queue.push_back(trans);
827 return ERR_IO_PENDING;
830 if (trans->mode() & Transaction::WRITE) {
831 // transaction needs exclusive access to the entry
832 if (entry->readers.empty()) {
833 entry->writer = trans;
834 } else {
835 entry->pending_queue.push_back(trans);
836 return ERR_IO_PENDING;
838 } else {
839 // transaction needs read access to the entry
840 entry->readers.push_back(trans);
843 // We do this before calling EntryAvailable to force any further calls to
844 // AddTransactionToEntry to add their transaction to the pending queue, which
845 // ensures FIFO ordering.
846 if (!entry->writer && !entry->pending_queue.empty())
847 ProcessPendingQueue(entry);
849 return OK;
852 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
853 bool cancel) {
854 // If we already posted a task to move on to the next transaction and this was
855 // the writer, there is nothing to cancel.
856 if (entry->will_process_pending_queue && entry->readers.empty())
857 return;
859 if (entry->writer) {
860 DCHECK(trans == entry->writer);
862 // Assume there was a failure.
863 bool success = false;
864 if (cancel) {
865 DCHECK(entry->disk_entry);
866 // This is a successful operation in the sense that we want to keep the
867 // entry.
868 success = trans->AddTruncatedFlag();
869 // The previous operation may have deleted the entry.
870 if (!trans->entry())
871 return;
873 DoneWritingToEntry(entry, success);
874 } else {
875 DoneReadingFromEntry(entry, trans);
879 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
880 DCHECK(entry->readers.empty());
882 entry->writer = NULL;
884 if (success) {
885 ProcessPendingQueue(entry);
886 } else {
887 DCHECK(!entry->will_process_pending_queue);
889 // We failed to create this entry.
890 TransactionList pending_queue;
891 pending_queue.swap(entry->pending_queue);
893 entry->disk_entry->Doom();
894 DestroyEntry(entry);
896 // We need to do something about these pending entries, which now need to
897 // be added to a new entry.
898 while (!pending_queue.empty()) {
899 // ERR_CACHE_RACE causes the transaction to restart the whole process.
900 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
901 pending_queue.pop_front();
906 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
907 DCHECK(!entry->writer);
909 TransactionList::iterator it =
910 std::find(entry->readers.begin(), entry->readers.end(), trans);
911 DCHECK(it != entry->readers.end());
913 entry->readers.erase(it);
915 ProcessPendingQueue(entry);
918 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
919 DCHECK(entry->writer);
920 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
921 DCHECK(entry->readers.empty());
923 Transaction* trans = entry->writer;
925 entry->writer = NULL;
926 entry->readers.push_back(trans);
928 ProcessPendingQueue(entry);
931 LoadState HttpCache::GetLoadStateForPendingTransaction(
932 const Transaction* trans) {
933 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
934 if (i == active_entries_.end()) {
935 // If this is really a pending transaction, and it is not part of
936 // active_entries_, we should be creating the backend or the entry.
937 return LOAD_STATE_WAITING_FOR_CACHE;
940 Transaction* writer = i->second->writer;
941 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
944 void HttpCache::RemovePendingTransaction(Transaction* trans) {
945 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
946 bool found = false;
947 if (i != active_entries_.end())
948 found = RemovePendingTransactionFromEntry(i->second, trans);
950 if (found)
951 return;
953 if (building_backend_) {
954 PendingOpsMap::const_iterator j = pending_ops_.find(std::string());
955 if (j != pending_ops_.end())
956 found = RemovePendingTransactionFromPendingOp(j->second, trans);
958 if (found)
959 return;
962 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
963 if (j != pending_ops_.end())
964 found = RemovePendingTransactionFromPendingOp(j->second, trans);
966 if (found)
967 return;
969 ActiveEntriesSet::iterator k = doomed_entries_.begin();
970 for (; k != doomed_entries_.end() && !found; ++k)
971 found = RemovePendingTransactionFromEntry(*k, trans);
973 DCHECK(found) << "Pending transaction not found";
976 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
977 Transaction* trans) {
978 TransactionList& pending_queue = entry->pending_queue;
980 TransactionList::iterator j =
981 find(pending_queue.begin(), pending_queue.end(), trans);
982 if (j == pending_queue.end())
983 return false;
985 pending_queue.erase(j);
986 return true;
989 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
990 Transaction* trans) {
991 if (pending_op->writer->Matches(trans)) {
992 pending_op->writer->ClearTransaction();
993 pending_op->writer->ClearEntry();
994 return true;
996 WorkItemList& pending_queue = pending_op->pending_queue;
998 WorkItemList::iterator it = pending_queue.begin();
999 for (; it != pending_queue.end(); ++it) {
1000 if ((*it)->Matches(trans)) {
1001 delete *it;
1002 pending_queue.erase(it);
1003 return true;
1006 return false;
1009 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) {
1010 if (session && session->params().enable_quic_persist_server_info &&
1011 !session->quic_stream_factory()->has_quic_server_info_factory()) {
1012 DCHECK(!quic_server_info_factory_);
1013 quic_server_info_factory_.reset(new QuicServerInfoFactoryAdaptor(this));
1014 session->quic_stream_factory()->set_quic_server_info_factory(
1015 quic_server_info_factory_.get());
1019 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
1020 // Multiple readers may finish with an entry at once, so we want to batch up
1021 // calls to OnProcessPendingQueue. This flag also tells us that we should
1022 // not delete the entry before OnProcessPendingQueue runs.
1023 if (entry->will_process_pending_queue)
1024 return;
1025 entry->will_process_pending_queue = true;
1027 base::MessageLoop::current()->PostTask(
1028 FROM_HERE,
1029 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1032 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1033 entry->will_process_pending_queue = false;
1034 DCHECK(!entry->writer);
1036 // If no one is interested in this entry, then we can deactivate it.
1037 if (entry->pending_queue.empty()) {
1038 if (entry->readers.empty())
1039 DestroyEntry(entry);
1040 return;
1043 // Promote next transaction from the pending queue.
1044 Transaction* next = entry->pending_queue.front();
1045 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1046 return; // Have to wait.
1048 entry->pending_queue.erase(entry->pending_queue.begin());
1050 int rv = AddTransactionToEntry(entry, next);
1051 if (rv != ERR_IO_PENDING) {
1052 next->io_callback().Run(rv);
1056 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1057 WorkItemOperation op = pending_op->writer->operation();
1059 // Completing the creation of the backend is simpler than the other cases.
1060 if (op == WI_CREATE_BACKEND)
1061 return OnBackendCreated(result, pending_op);
1063 scoped_ptr<WorkItem> item(pending_op->writer);
1064 bool fail_requests = false;
1066 ActiveEntry* entry = NULL;
1067 std::string key;
1068 if (result == OK) {
1069 if (op == WI_DOOM_ENTRY) {
1070 // Anything after a Doom has to be restarted.
1071 fail_requests = true;
1072 } else if (item->IsValid()) {
1073 key = pending_op->disk_entry->GetKey();
1074 entry = ActivateEntry(pending_op->disk_entry);
1075 } else {
1076 // The writer transaction is gone.
1077 if (op == WI_CREATE_ENTRY)
1078 pending_op->disk_entry->Doom();
1079 pending_op->disk_entry->Close();
1080 pending_op->disk_entry = NULL;
1081 fail_requests = true;
1085 // We are about to notify a bunch of transactions, and they may decide to
1086 // re-issue a request (or send a different one). If we don't delete
1087 // pending_op, the new request will be appended to the end of the list, and
1088 // we'll see it again from this point before it has a chance to complete (and
1089 // we'll be messing out the request order). The down side is that if for some
1090 // reason notifying request A ends up cancelling request B (for the same key),
1091 // we won't find request B anywhere (because it would be in a local variable
1092 // here) and that's bad. If there is a chance for that to happen, we'll have
1093 // to move the callback used to be a CancelableCallback. By the way, for this
1094 // to happen the action (to cancel B) has to be synchronous to the
1095 // notification for request A.
1096 WorkItemList pending_items;
1097 pending_items.swap(pending_op->pending_queue);
1098 DeletePendingOp(pending_op);
1100 item->NotifyTransaction(result, entry);
1102 while (!pending_items.empty()) {
1103 item.reset(pending_items.front());
1104 pending_items.pop_front();
1106 if (item->operation() == WI_DOOM_ENTRY) {
1107 // A queued doom request is always a race.
1108 fail_requests = true;
1109 } else if (result == OK) {
1110 entry = FindActiveEntry(key);
1111 if (!entry)
1112 fail_requests = true;
1115 if (fail_requests) {
1116 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1117 continue;
1120 if (item->operation() == WI_CREATE_ENTRY) {
1121 if (result == OK) {
1122 // A second Create request, but the first request succeeded.
1123 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1124 } else {
1125 if (op != WI_CREATE_ENTRY) {
1126 // Failed Open followed by a Create.
1127 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1128 fail_requests = true;
1129 } else {
1130 item->NotifyTransaction(result, entry);
1133 } else {
1134 if (op == WI_CREATE_ENTRY && result != OK) {
1135 // Failed Create followed by an Open.
1136 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1137 fail_requests = true;
1138 } else {
1139 item->NotifyTransaction(result, entry);
1145 // static
1146 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
1147 PendingOp* pending_op,
1148 int rv) {
1149 if (cache.get()) {
1150 cache->OnIOComplete(rv, pending_op);
1151 } else {
1152 // The callback was cancelled so we should delete the pending_op that
1153 // was used with this callback.
1154 delete pending_op;
1158 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1159 scoped_ptr<WorkItem> item(pending_op->writer);
1160 WorkItemOperation op = item->operation();
1161 DCHECK_EQ(WI_CREATE_BACKEND, op);
1163 // We don't need the callback anymore.
1164 pending_op->callback.Reset();
1166 if (backend_factory_.get()) {
1167 // We may end up calling OnBackendCreated multiple times if we have pending
1168 // work items. The first call saves the backend and releases the factory,
1169 // and the last call clears building_backend_.
1170 backend_factory_.reset(); // Reclaim memory.
1171 if (result == OK)
1172 disk_cache_ = pending_op->backend.Pass();
1175 if (!pending_op->pending_queue.empty()) {
1176 WorkItem* pending_item = pending_op->pending_queue.front();
1177 pending_op->pending_queue.pop_front();
1178 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1180 // We want to process a single callback at a time, because the cache may
1181 // go away from the callback.
1182 pending_op->writer = pending_item;
1184 base::MessageLoop::current()->PostTask(
1185 FROM_HERE,
1186 base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(),
1187 result, pending_op));
1188 } else {
1189 building_backend_ = false;
1190 DeletePendingOp(pending_op);
1193 // The cache may be gone when we return from the callback.
1194 if (!item->DoCallback(result, disk_cache_.get()))
1195 item->NotifyTransaction(result, NULL);
1198 } // namespace net