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.
4
5 #include "net/http/http_cache.h"
6
7 #include <algorithm>
8
9 #include "base/compiler_specific.h"
10
11 #if defined(OS_POSIX)
12 #include <unistd.h>
13 #endif
14
15 #include "base/bind.h"
16 #include "base/bind_helpers.h"
17 #include "base/callback.h"
18 #include "base/files/file_util.h"
19 #include "base/format_macros.h"
20 #include "base/location.h"
21 #include "base/memory/ref_counted.h"
22 #include "base/message_loop/message_loop.h"
23 #include "base/metrics/field_trial.h"
24 #include "base/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"
46
47 namespace {
48
UseCertCache()49 bool UseCertCache() {
50 return base::FieldTrialList::FindFullName("CertCacheTrial") ==
51 "ExperimentGroup";
52 }
53
54 // Adaptor to delete a file on a worker thread.
DeletePath(base::FilePath path)55 void DeletePath(base::FilePath path) {
56 base::DeleteFile(path, false);
57 }
58
59 } // namespace
60
61 namespace net {
62
DefaultBackend(CacheType type,BackendType backend_type,const base::FilePath & path,int max_bytes,const scoped_refptr<base::SingleThreadTaskRunner> & thread)63 HttpCache::DefaultBackend::DefaultBackend(
64 CacheType type,
65 BackendType backend_type,
66 const base::FilePath& path,
67 int max_bytes,
68 const scoped_refptr<base::SingleThreadTaskRunner>& thread)
69 : type_(type),
70 backend_type_(backend_type),
71 path_(path),
72 max_bytes_(max_bytes),
73 thread_(thread) {
74 }
75
~DefaultBackend()76 HttpCache::DefaultBackend::~DefaultBackend() {}
77
78 // static
InMemory(int max_bytes)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);
82 }
83
CreateBackend(NetLog * net_log,scoped_ptr<disk_cache::Backend> * backend,const CompletionCallback & callback)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_,
89 backend_type_,
90 path_,
91 max_bytes_,
92 true,
93 thread_,
94 net_log,
95 backend,
96 callback);
97 }
98
99 //-----------------------------------------------------------------------------
100
ActiveEntry(disk_cache::Entry * entry)101 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
102 : disk_entry(entry),
103 writer(NULL),
104 will_process_pending_queue(false),
105 doomed(false) {
106 }
107
~ActiveEntry()108 HttpCache::ActiveEntry::~ActiveEntry() {
109 if (disk_entry) {
110 disk_entry->Close();
111 disk_entry = NULL;
112 }
113 }
114
115 //-----------------------------------------------------------------------------
116
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 {
PendingOpnet::HttpCache::PendingOp120 PendingOp() : disk_entry(NULL), writer(NULL) {}
~PendingOpnet::HttpCache::PendingOp121 ~PendingOp() {}
122
123 disk_cache::Entry* disk_entry;
124 scoped_ptr<disk_cache::Backend> backend;
125 WorkItem* writer;
126 CompletionCallback callback; // BackendCallback.
127 WorkItemList pending_queue;
128 };
129
130 //-----------------------------------------------------------------------------
131
132 // The type of operation represented by a work item.
133 enum WorkItemOperation {
134 WI_CREATE_BACKEND,
135 WI_OPEN_ENTRY,
136 WI_CREATE_ENTRY,
137 WI_DOOM_ENTRY
138 };
139
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 {
143 public:
WorkItem(WorkItemOperation operation,Transaction * trans,ActiveEntry ** entry)144 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
145 : operation_(operation),
146 trans_(trans),
147 entry_(entry),
148 backend_(NULL) {}
WorkItem(WorkItemOperation operation,Transaction * trans,const net::CompletionCallback & cb,disk_cache::Backend ** backend)149 WorkItem(WorkItemOperation operation, Transaction* trans,
150 const net::CompletionCallback& cb, disk_cache::Backend** backend)
151 : operation_(operation),
152 trans_(trans),
153 entry_(NULL),
154 callback_(cb),
155 backend_(backend) {}
~WorkItem()156 ~WorkItem() {}
157
158 // Calls back the transaction with the result of the operation.
NotifyTransaction(int result,ActiveEntry * entry)159 void NotifyTransaction(int result, ActiveEntry* entry) {
160 DCHECK(!entry || entry->disk_entry);
161 if (entry_)
162 *entry_ = entry;
163 if (trans_)
164 trans_->io_callback().Run(result);
165 }
166
167 // Notifies the caller about the operation completion. Returns true if the
168 // callback was invoked.
DoCallback(int result,disk_cache::Backend * backend)169 bool DoCallback(int result, disk_cache::Backend* backend) {
170 if (backend_)
171 *backend_ = backend;
172 if (!callback_.is_null()) {
173 callback_.Run(result);
174 return true;
175 }
176 return false;
177 }
178
operation()179 WorkItemOperation operation() { return operation_; }
ClearTransaction()180 void ClearTransaction() { trans_ = NULL; }
ClearEntry()181 void ClearEntry() { entry_ = NULL; }
ClearCallback()182 void ClearCallback() { callback_.Reset(); }
Matches(Transaction * trans) const183 bool Matches(Transaction* trans) const { return trans == trans_; }
IsValid() const184 bool IsValid() const { return trans_ || entry_ || !callback_.is_null(); }
185
186 private:
187 WorkItemOperation operation_;
188 Transaction* trans_;
189 ActiveEntry** entry_;
190 net::CompletionCallback callback_; // User callback.
191 disk_cache::Backend** backend_;
192 };
193
194 //-----------------------------------------------------------------------------
195
196 // This class encapsulates a transaction whose only purpose is to write metadata
197 // to a given entry.
198 class HttpCache::MetadataWriter {
199 public:
MetadataWriter(HttpCache::Transaction * trans)200 explicit MetadataWriter(HttpCache::Transaction* trans)
201 : transaction_(trans),
202 verified_(false),
203 buf_len_(0) {
204 }
205
~MetadataWriter()206 ~MetadataWriter() {}
207
208 // Implements the bulk of HttpCache::WriteMetadata.
209 void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf,
210 int buf_len);
211
212 private:
213 void VerifyResponse(int result);
214 void SelfDestroy();
215 void OnIOComplete(int result);
216
217 scoped_ptr<HttpCache::Transaction> transaction_;
218 bool verified_;
219 scoped_refptr<IOBuffer> buf_;
220 int buf_len_;
221 base::Time expected_response_time_;
222 HttpRequestInfo request_info_;
223 DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
224 };
225
Write(const GURL & url,base::Time expected_response_time,IOBuffer * buf,int buf_len)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);
230 DCHECK(buf);
231 DCHECK(buf->data());
232 request_info_.url = url;
233 request_info_.method = "GET";
234 request_info_.load_flags = LOAD_ONLY_FROM_CACHE;
235
236 expected_response_time_ = expected_response_time;
237 buf_ = buf;
238 buf_len_ = buf_len;
239 verified_ = false;
240
241 int rv = transaction_->Start(
242 &request_info_,
243 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)),
244 BoundNetLog());
245 if (rv != ERR_IO_PENDING)
246 VerifyResponse(rv);
247 }
248
VerifyResponse(int result)249 void HttpCache::MetadataWriter::VerifyResponse(int result) {
250 verified_ = true;
251 if (result != OK)
252 return SelfDestroy();
253
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();
258
259 result = transaction_->WriteMetadata(
260 buf_.get(),
261 buf_len_,
262 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)));
263 if (result != ERR_IO_PENDING)
264 SelfDestroy();
265 }
266
SelfDestroy()267 void HttpCache::MetadataWriter::SelfDestroy() {
268 delete this;
269 }
270
OnIOComplete(int result)271 void HttpCache::MetadataWriter::OnIOComplete(int result) {
272 if (!verified_)
273 return VerifyResponse(result);
274 SelfDestroy();
275 }
276
277 //-----------------------------------------------------------------------------
278
279 class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory {
280 public:
QuicServerInfoFactoryAdaptor(HttpCache * http_cache)281 QuicServerInfoFactoryAdaptor(HttpCache* http_cache)
282 : http_cache_(http_cache) {
283 }
284
GetForServer(const QuicServerId & server_id)285 virtual QuicServerInfo* GetForServer(
286 const QuicServerId& server_id) OVERRIDE {
287 return new DiskCacheBasedQuicServerInfo(server_id, http_cache_);
288 }
289
290 private:
291 HttpCache* const http_cache_;
292 };
293
294 //-----------------------------------------------------------------------------
HttpCache(const net::HttpNetworkSession::Params & params,BackendFactory * backend_factory)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),
301 mode_(NORMAL),
302 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))),
303 weak_factory_(this) {
304 SetupQuicServerInfoFactory(network_layer_->GetSession());
305 }
306
307
308 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
309 // |session| is shared.
HttpCache(HttpNetworkSession * session,BackendFactory * backend_factory)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),
316 mode_(NORMAL),
317 network_layer_(new HttpNetworkLayer(session)),
318 weak_factory_(this) {
319 }
320
HttpCache(HttpTransactionFactory * network_layer,NetLog * net_log,BackendFactory * backend_factory)321 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
322 NetLog* net_log,
323 BackendFactory* backend_factory)
324 : net_log_(net_log),
325 backend_factory_(backend_factory),
326 building_backend_(false),
327 bypass_lock_for_test_(false),
328 mode_(NORMAL),
329 network_layer_(network_layer),
330 weak_factory_(this) {
331 SetupQuicServerInfoFactory(network_layer_->GetSession());
332 }
333
~HttpCache()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();
338
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);
350 }
351
352 STLDeleteElements(&doomed_entries_);
353
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.
356 cert_cache_.reset();
357 disk_cache_.reset();
358
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;
372 }
373 } else {
374 pending_op->callback.Reset();
375 }
376
377 STLDeleteElements(&pending_op->pending_queue);
378 if (delete_pending_op)
379 delete pending_op;
380 }
381 }
382
GetBackend(disk_cache::Backend ** backend,const CompletionCallback & callback)383 int HttpCache::GetBackend(disk_cache::Backend** backend,
384 const CompletionCallback& callback) {
385 DCHECK(!callback.is_null());
386
387 if (disk_cache_.get()) {
388 *backend = disk_cache_.get();
389 return OK;
390 }
391
392 return CreateBackend(backend, callback);
393 }
394
GetCurrentBackend() const395 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
396 return disk_cache_.get();
397 }
398
399 // static
ParseResponseInfo(const char * data,int len,HttpResponseInfo * response_info,bool * response_truncated)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);
405 }
406
WriteMetadata(const GURL & url,RequestPriority priority,base::Time expected_response_time,IOBuffer * buf,int buf_len)407 void HttpCache::WriteMetadata(const GURL& url,
408 RequestPriority priority,
409 base::Time expected_response_time,
410 IOBuffer* buf,
411 int buf_len) {
412 if (!buf_len)
413 return;
414
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());
419 }
420
421 HttpCache::Transaction* trans =
422 new HttpCache::Transaction(priority, this);
423 MetadataWriter* writer = new MetadataWriter(trans);
424
425 // The writer will self destruct when done.
426 writer->Write(url, expected_response_time, buf, buf_len);
427 }
428
CloseAllConnections()429 void HttpCache::CloseAllConnections() {
430 HttpNetworkSession* session = GetSession();
431 if (session)
432 session->CloseAllConnections();
433 }
434
CloseIdleConnections()435 void HttpCache::CloseIdleConnections() {
436 HttpNetworkSession* session = GetSession();
437 if (session)
438 session->CloseIdleConnections();
439 }
440
OnExternalCacheHit(const GURL & url,const std::string & http_method)441 void HttpCache::OnExternalCacheHit(const GURL& url,
442 const std::string& http_method) {
443 if (!disk_cache_.get())
444 return;
445
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);
451 }
452
InitializeInfiniteCache(const base::FilePath & path)453 void HttpCache::InitializeInfiniteCache(const base::FilePath& path) {
454 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
455 return;
456 base::WorkerPool::PostTask(FROM_HERE, base::Bind(&DeletePath, path), true);
457 }
458
CreateTransaction(RequestPriority priority,scoped_ptr<HttpTransaction> * trans)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());
465 }
466
467 HttpCache::Transaction* transaction =
468 new HttpCache::Transaction(priority, this);
469 if (bypass_lock_for_test_)
470 transaction->BypassLockForTest();
471
472 trans->reset(transaction);
473 return OK;
474 }
475
GetCache()476 HttpCache* HttpCache::GetCache() {
477 return this;
478 }
479
GetSession()480 HttpNetworkSession* HttpCache::GetSession() {
481 return network_layer_->GetSession();
482 }
483
484 scoped_ptr<HttpTransactionFactory>
SetHttpNetworkTransactionFactoryForTesting(scoped_ptr<HttpTransactionFactory> new_network_layer)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();
490 }
491
492 //-----------------------------------------------------------------------------
493
CreateBackend(disk_cache::Backend ** backend,const net::CompletionCallback & callback)494 int HttpCache::CreateBackend(disk_cache::Backend** backend,
495 const net::CompletionCallback& callback) {
496 if (!backend_factory_.get())
497 return ERR_FAILED;
498
499 building_backend_ = true;
500
501 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
502 backend));
503
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;
511 }
512
513 DCHECK(pending_op->pending_queue.empty());
514
515 pending_op->writer = item.release();
516 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
517 GetWeakPtr(), pending_op);
518
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);
524 }
525
526 return rv;
527 }
528
GetBackendForTransaction(Transaction * trans)529 int HttpCache::GetBackendForTransaction(Transaction* trans) {
530 if (disk_cache_.get())
531 return OK;
532
533 if (!building_backend_)
534 return ERR_FAILED;
535
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;
542 }
543
544 // Generate a key that can be used inside the cache.
GenerateCacheKey(const HttpRequestInfo * request)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);
548
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()));
557 }
558 return url;
559 }
560
561 // In playback and record mode, we cache everything.
562
563 // Lazily initialize.
564 if (playback_cache_map_ == NULL)
565 playback_cache_map_.reset(new PlaybackCacheMap());
566
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.
571 int generation = 0;
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;
576
577 // The key into the cache is GENERATION # + METHOD + URL.
578 std::string result = base::IntToString(generation);
579 result.append(request->method);
580 result.append(url);
581 return result;
582 }
583
DoomActiveEntry(const std::string & key)584 void HttpCache::DoomActiveEntry(const std::string& key) {
585 ActiveEntriesMap::iterator it = active_entries_.find(key);
586 if (it == active_entries_.end())
587 return;
588
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);
592 DCHECK_EQ(OK, rv);
593 }
594
DoomEntry(const std::string & key,Transaction * trans)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()) {
602 DCHECK(trans);
603 return AsyncDoomEntry(key, trans);
604 }
605
606 ActiveEntry* entry = it->second;
607 active_entries_.erase(it);
608
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);
612
613 entry->disk_entry->Doom();
614 entry->doomed = true;
615
616 DCHECK(entry->writer || !entry->readers.empty() ||
617 entry->will_process_pending_queue);
618 return OK;
619 }
620
AsyncDoomEntry(const std::string & key,Transaction * trans)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;
627 }
628
629 DCHECK(pending_op->pending_queue.empty());
630
631 pending_op->writer = item;
632 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
633 GetWeakPtr(), pending_op);
634
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);
639 }
640
641 return rv;
642 }
643
DoomMainEntryForUrl(const GURL & url)644 void HttpCache::DoomMainEntryForUrl(const GURL& url) {
645 if (!disk_cache_)
646 return;
647
648 HttpRequestInfo temp_info;
649 temp_info.url = url;
650 temp_info.method = "GET";
651 std::string key = GenerateCacheKey(&temp_info);
652
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);
657 else
658 AsyncDoomEntry(key, NULL);
659 }
660
FinalizeDoomedEntry(ActiveEntry * entry)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());
666
667 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
668 DCHECK(it != doomed_entries_.end());
669 doomed_entries_.erase(it);
670
671 delete entry;
672 }
673
FindActiveEntry(const std::string & key)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;
677 }
678
ActivateEntry(disk_cache::Entry * disk_entry)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;
684 return entry;
685 }
686
DeactivateEntry(ActiveEntry * 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());
694
695 std::string key = entry->disk_entry->GetKey();
696 if (key.empty())
697 return SlowDeactivateEntry(entry);
698
699 ActiveEntriesMap::iterator it = active_entries_.find(key);
700 DCHECK(it != active_entries_.end());
701 DCHECK(it->second == entry);
702
703 active_entries_.erase(it);
704 delete entry;
705 }
706
707 // We don't know this entry's key so we have to find it without it.
SlowDeactivateEntry(ActiveEntry * entry)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);
713 delete entry;
714 break;
715 }
716 }
717 }
718
GetPendingOp(const std::string & key)719 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
720 DCHECK(!FindActiveEntry(key));
721
722 PendingOpsMap::const_iterator it = pending_ops_.find(key);
723 if (it != pending_ops_.end())
724 return it->second;
725
726 PendingOp* operation = new PendingOp();
727 pending_ops_[key] = operation;
728 return operation;
729 }
730
DeletePendingOp(PendingOp * pending_op)731 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
732 std::string key;
733 if (pending_op->disk_entry)
734 key = pending_op->disk_entry->GetKey();
735
736 if (!key.empty()) {
737 PendingOpsMap::iterator it = pending_ops_.find(key);
738 DCHECK(it != pending_ops_.end());
739 pending_ops_.erase(it);
740 } else {
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);
745 break;
746 }
747 }
748 }
749 DCHECK(pending_op->pending_queue.empty());
750
751 delete pending_op;
752 }
753
OpenEntry(const std::string & key,ActiveEntry ** entry,Transaction * trans)754 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
755 Transaction* trans) {
756 ActiveEntry* active_entry = FindActiveEntry(key);
757 if (active_entry) {
758 *entry = active_entry;
759 return OK;
760 }
761
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;
767 }
768
769 DCHECK(pending_op->pending_queue.empty());
770
771 pending_op->writer = item;
772 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
773 GetWeakPtr(), pending_op);
774
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);
780 }
781
782 return rv;
783 }
784
CreateEntry(const std::string & key,ActiveEntry ** entry,Transaction * trans)785 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
786 Transaction* trans) {
787 if (FindActiveEntry(key)) {
788 return ERR_CACHE_RACE;
789 }
790
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;
796 }
797
798 DCHECK(pending_op->pending_queue.empty());
799
800 pending_op->writer = item;
801 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
802 GetWeakPtr(), pending_op);
803
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);
809 }
810
811 return rv;
812 }
813
DestroyEntry(ActiveEntry * entry)814 void HttpCache::DestroyEntry(ActiveEntry* entry) {
815 if (entry->doomed) {
816 FinalizeDoomedEntry(entry);
817 } else {
818 DeactivateEntry(entry);
819 }
820 }
821
AddTransactionToEntry(ActiveEntry * entry,Transaction * trans)822 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
823 DCHECK(entry);
824 DCHECK(entry->disk_entry);
825
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
829 // readers.
830 //
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).
833
834 if (entry->writer || entry->will_process_pending_queue) {
835 entry->pending_queue.push_back(trans);
836 return ERR_IO_PENDING;
837 }
838
839 if (trans->mode() & Transaction::WRITE) {
840 // transaction needs exclusive access to the entry
841 if (entry->readers.empty()) {
842 entry->writer = trans;
843 } else {
844 entry->pending_queue.push_back(trans);
845 return ERR_IO_PENDING;
846 }
847 } else {
848 // transaction needs read access to the entry
849 entry->readers.push_back(trans);
850 }
851
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);
857
858 return OK;
859 }
860
DoneWithEntry(ActiveEntry * entry,Transaction * trans,bool cancel)861 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
862 bool cancel) {
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())
866 return;
867
868 if (entry->writer) {
869 DCHECK(trans == entry->writer);
870
871 // Assume there was a failure.
872 bool success = false;
873 if (cancel) {
874 DCHECK(entry->disk_entry);
875 // This is a successful operation in the sense that we want to keep the
876 // entry.
877 success = trans->AddTruncatedFlag();
878 // The previous operation may have deleted the entry.
879 if (!trans->entry())
880 return;
881 }
882 DoneWritingToEntry(entry, success);
883 } else {
884 DoneReadingFromEntry(entry, trans);
885 }
886 }
887
DoneWritingToEntry(ActiveEntry * entry,bool success)888 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
889 DCHECK(entry->readers.empty());
890
891 entry->writer = NULL;
892
893 if (success) {
894 ProcessPendingQueue(entry);
895 } else {
896 DCHECK(!entry->will_process_pending_queue);
897
898 // We failed to create this entry.
899 TransactionList pending_queue;
900 pending_queue.swap(entry->pending_queue);
901
902 entry->disk_entry->Doom();
903 DestroyEntry(entry);
904
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();
911 }
912 }
913 }
914
DoneReadingFromEntry(ActiveEntry * entry,Transaction * trans)915 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
916 DCHECK(!entry->writer);
917
918 TransactionList::iterator it =
919 std::find(entry->readers.begin(), entry->readers.end(), trans);
920 DCHECK(it != entry->readers.end());
921
922 entry->readers.erase(it);
923
924 ProcessPendingQueue(entry);
925 }
926
ConvertWriterToReader(ActiveEntry * entry)927 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
928 DCHECK(entry->writer);
929 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
930 DCHECK(entry->readers.empty());
931
932 Transaction* trans = entry->writer;
933
934 entry->writer = NULL;
935 entry->readers.push_back(trans);
936
937 ProcessPendingQueue(entry);
938 }
939
GetLoadStateForPendingTransaction(const Transaction * trans)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;
947 }
948
949 Transaction* writer = i->second->writer;
950 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
951 }
952
RemovePendingTransaction(Transaction * trans)953 void HttpCache::RemovePendingTransaction(Transaction* trans) {
954 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
955 bool found = false;
956 if (i != active_entries_.end())
957 found = RemovePendingTransactionFromEntry(i->second, trans);
958
959 if (found)
960 return;
961
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);
966
967 if (found)
968 return;
969 }
970
971 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
972 if (j != pending_ops_.end())
973 found = RemovePendingTransactionFromPendingOp(j->second, trans);
974
975 if (found)
976 return;
977
978 ActiveEntriesSet::iterator k = doomed_entries_.begin();
979 for (; k != doomed_entries_.end() && !found; ++k)
980 found = RemovePendingTransactionFromEntry(*k, trans);
981
982 DCHECK(found) << "Pending transaction not found";
983 }
984
RemovePendingTransactionFromEntry(ActiveEntry * entry,Transaction * trans)985 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
986 Transaction* trans) {
987 TransactionList& pending_queue = entry->pending_queue;
988
989 TransactionList::iterator j =
990 find(pending_queue.begin(), pending_queue.end(), trans);
991 if (j == pending_queue.end())
992 return false;
993
994 pending_queue.erase(j);
995 return true;
996 }
997
RemovePendingTransactionFromPendingOp(PendingOp * pending_op,Transaction * trans)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();
1003 return true;
1004 }
1005 WorkItemList& pending_queue = pending_op->pending_queue;
1006
1007 WorkItemList::iterator it = pending_queue.begin();
1008 for (; it != pending_queue.end(); ++it) {
1009 if ((*it)->Matches(trans)) {
1010 delete *it;
1011 pending_queue.erase(it);
1012 return true;
1013 }
1014 }
1015 return false;
1016 }
1017
SetupQuicServerInfoFactory(HttpNetworkSession * session)1018 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) {
1019 if (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());
1025 }
1026 }
1027
ProcessPendingQueue(ActiveEntry * entry)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)
1033 return;
1034 entry->will_process_pending_queue = true;
1035
1036 base::MessageLoop::current()->PostTask(
1037 FROM_HERE,
1038 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1039 }
1040
OnProcessPendingQueue(ActiveEntry * entry)1041 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1042 entry->will_process_pending_queue = false;
1043 DCHECK(!entry->writer);
1044
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);
1049 return;
1050 }
1051
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.
1056
1057 entry->pending_queue.erase(entry->pending_queue.begin());
1058
1059 int rv = AddTransactionToEntry(entry, next);
1060 if (rv != ERR_IO_PENDING) {
1061 next->io_callback().Run(rv);
1062 }
1063 }
1064
OnIOComplete(int result,PendingOp * pending_op)1065 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1066 WorkItemOperation op = pending_op->writer->operation();
1067
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);
1071
1072 scoped_ptr<WorkItem> item(pending_op->writer);
1073 bool fail_requests = false;
1074
1075 ActiveEntry* entry = NULL;
1076 std::string key;
1077 if (result == OK) {
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);
1084 } else {
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;
1091 }
1092 }
1093
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);
1108
1109 item->NotifyTransaction(result, entry);
1110
1111 while (!pending_items.empty()) {
1112 item.reset(pending_items.front());
1113 pending_items.pop_front();
1114
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);
1120 if (!entry)
1121 fail_requests = true;
1122 }
1123
1124 if (fail_requests) {
1125 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1126 continue;
1127 }
1128
1129 if (item->operation() == WI_CREATE_ENTRY) {
1130 if (result == OK) {
1131 // A second Create request, but the first request succeeded.
1132 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1133 } else {
1134 if (op != WI_CREATE_ENTRY) {
1135 // Failed Open followed by a Create.
1136 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1137 fail_requests = true;
1138 } else {
1139 item->NotifyTransaction(result, entry);
1140 }
1141 }
1142 } else {
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;
1147 } else {
1148 item->NotifyTransaction(result, entry);
1149 }
1150 }
1151 }
1152 }
1153
1154 // static
OnPendingOpComplete(const base::WeakPtr<HttpCache> & cache,PendingOp * pending_op,int rv)1155 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
1156 PendingOp* pending_op,
1157 int rv) {
1158 if (cache.get()) {
1159 cache->OnIOComplete(rv, pending_op);
1160 } else {
1161 // The callback was cancelled so we should delete the pending_op that
1162 // was used with this callback.
1163 delete pending_op;
1164 }
1165 }
1166
OnBackendCreated(int result,PendingOp * pending_op)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);
1171
1172 // We don't need the callback anymore.
1173 pending_op->callback.Reset();
1174
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.
1180 if (result == OK) {
1181 disk_cache_ = pending_op->backend.Pass();
1182 if (UseCertCache())
1183 cert_cache_.reset(new DiskBasedCertCache(disk_cache_.get()));
1184 }
1185 }
1186
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());
1191
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;
1195
1196 base::MessageLoop::current()->PostTask(
1197 FROM_HERE,
1198 base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(),
1199 result, pending_op));
1200 } else {
1201 building_backend_ = false;
1202 DeletePendingOp(pending_op);
1203 }
1204
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);
1208 }
1209
1210 } // namespace net
1211