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/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"
45
46 namespace {
47
48 // Adaptor to delete a file on a worker thread.
DeletePath(base::FilePath path)49 void DeletePath(base::FilePath path) {
50 base::DeleteFile(path, false);
51 }
52
53 } // namespace
54
55 namespace net {
56
DefaultBackend(CacheType type,BackendType backend_type,const base::FilePath & path,int max_bytes,base::MessageLoopProxy * thread)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) {
67 }
68
~DefaultBackend()69 HttpCache::DefaultBackend::~DefaultBackend() {}
70
71 // static
InMemory(int max_bytes)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);
75 }
76
CreateBackend(NetLog * net_log,scoped_ptr<disk_cache::Backend> * backend,const CompletionCallback & callback)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);
90 }
91
92 //-----------------------------------------------------------------------------
93
ActiveEntry(disk_cache::Entry * entry)94 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
95 : disk_entry(entry),
96 writer(NULL),
97 will_process_pending_queue(false),
98 doomed(false) {
99 }
100
~ActiveEntry()101 HttpCache::ActiveEntry::~ActiveEntry() {
102 if (disk_entry) {
103 disk_entry->Close();
104 disk_entry = NULL;
105 }
106 }
107
108 //-----------------------------------------------------------------------------
109
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 {
PendingOpnet::HttpCache::PendingOp113 PendingOp() : disk_entry(NULL), writer(NULL) {}
~PendingOpnet::HttpCache::PendingOp114 ~PendingOp() {}
115
116 disk_cache::Entry* disk_entry;
117 scoped_ptr<disk_cache::Backend> backend;
118 WorkItem* writer;
119 CompletionCallback callback; // BackendCallback.
120 WorkItemList pending_queue;
121 };
122
123 //-----------------------------------------------------------------------------
124
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
131 };
132
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:
WorkItem(WorkItemOperation operation,Transaction * trans,ActiveEntry ** entry)137 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
138 : operation_(operation),
139 trans_(trans),
140 entry_(entry),
141 backend_(NULL) {}
WorkItem(WorkItemOperation operation,Transaction * trans,const net::CompletionCallback & cb,disk_cache::Backend ** backend)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) {}
~WorkItem()149 ~WorkItem() {}
150
151 // Calls back the transaction with the result of the operation.
NotifyTransaction(int result,ActiveEntry * entry)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);
158 }
159
160 // Notifies the caller about the operation completion. Returns true if the
161 // callback was invoked.
DoCallback(int result,disk_cache::Backend * backend)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;
168 }
169 return false;
170 }
171
operation()172 WorkItemOperation operation() { return operation_; }
ClearTransaction()173 void ClearTransaction() { trans_ = NULL; }
ClearEntry()174 void ClearEntry() { entry_ = NULL; }
ClearCallback()175 void ClearCallback() { callback_.Reset(); }
Matches(Transaction * trans) const176 bool Matches(Transaction* trans) const { return trans == trans_; }
IsValid() const177 bool IsValid() const { return trans_ || entry_ || !callback_.is_null(); }
178
179 private:
180 WorkItemOperation operation_;
181 Transaction* trans_;
182 ActiveEntry** entry_;
183 net::CompletionCallback callback_; // User callback.
184 disk_cache::Backend** backend_;
185 };
186
187 //-----------------------------------------------------------------------------
188
189 // This class encapsulates a transaction whose only purpose is to write metadata
190 // to a given entry.
191 class HttpCache::MetadataWriter {
192 public:
MetadataWriter(HttpCache::Transaction * trans)193 explicit MetadataWriter(HttpCache::Transaction* trans)
194 : transaction_(trans),
195 verified_(false),
196 buf_len_(0) {
197 }
198
~MetadataWriter()199 ~MetadataWriter() {}
200
201 // Implements the bulk of HttpCache::WriteMetadata.
202 void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf,
203 int buf_len);
204
205 private:
206 void VerifyResponse(int result);
207 void SelfDestroy();
208 void OnIOComplete(int result);
209
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);
217 };
218
Write(const GURL & url,base::Time expected_response_time,IOBuffer * buf,int buf_len)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;
228
229 expected_response_time_ = expected_response_time;
230 buf_ = buf;
231 buf_len_ = buf_len;
232 verified_ = false;
233
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);
240 }
241
VerifyResponse(int result)242 void HttpCache::MetadataWriter::VerifyResponse(int result) {
243 verified_ = true;
244 if (result != OK)
245 return SelfDestroy();
246
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();
251
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();
258 }
259
SelfDestroy()260 void HttpCache::MetadataWriter::SelfDestroy() {
261 delete this;
262 }
263
OnIOComplete(int result)264 void HttpCache::MetadataWriter::OnIOComplete(int result) {
265 if (!verified_)
266 return VerifyResponse(result);
267 SelfDestroy();
268 }
269
270 //-----------------------------------------------------------------------------
271
272 class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory {
273 public:
QuicServerInfoFactoryAdaptor(HttpCache * http_cache)274 QuicServerInfoFactoryAdaptor(HttpCache* http_cache)
275 : http_cache_(http_cache) {
276 }
277
GetForServer(const QuicServerId & server_id)278 virtual QuicServerInfo* GetForServer(
279 const QuicServerId& server_id) OVERRIDE {
280 return new DiskCacheBasedQuicServerInfo(server_id, http_cache_);
281 }
282
283 private:
284 HttpCache* const http_cache_;
285 };
286
287 //-----------------------------------------------------------------------------
HttpCache(const net::HttpNetworkSession::Params & params,BackendFactory * backend_factory)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 mode_(NORMAL),
294 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))),
295 weak_factory_(this) {
296 SetupQuicServerInfoFactory(network_layer_->GetSession());
297 }
298
299
300 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
301 // |session| is shared.
HttpCache(HttpNetworkSession * session,BackendFactory * backend_factory)302 HttpCache::HttpCache(HttpNetworkSession* session,
303 BackendFactory* backend_factory)
304 : net_log_(session->net_log()),
305 backend_factory_(backend_factory),
306 building_backend_(false),
307 mode_(NORMAL),
308 network_layer_(new HttpNetworkLayer(session)),
309 weak_factory_(this) {
310 }
311
HttpCache(HttpTransactionFactory * network_layer,NetLog * net_log,BackendFactory * backend_factory)312 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
313 NetLog* net_log,
314 BackendFactory* backend_factory)
315 : net_log_(net_log),
316 backend_factory_(backend_factory),
317 building_backend_(false),
318 mode_(NORMAL),
319 network_layer_(network_layer),
320 weak_factory_(this) {
321 SetupQuicServerInfoFactory(network_layer_->GetSession());
322 }
323
~HttpCache()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();
328
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);
340 }
341
342 STLDeleteElements(&doomed_entries_);
343
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.
346 disk_cache_.reset();
347
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;
361 }
362 } else {
363 pending_op->callback.Reset();
364 }
365
366 STLDeleteElements(&pending_op->pending_queue);
367 if (delete_pending_op)
368 delete pending_op;
369 }
370 }
371
GetBackend(disk_cache::Backend ** backend,const CompletionCallback & callback)372 int HttpCache::GetBackend(disk_cache::Backend** backend,
373 const CompletionCallback& callback) {
374 DCHECK(!callback.is_null());
375
376 if (disk_cache_.get()) {
377 *backend = disk_cache_.get();
378 return OK;
379 }
380
381 return CreateBackend(backend, callback);
382 }
383
GetCurrentBackend() const384 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
385 return disk_cache_.get();
386 }
387
388 // static
ParseResponseInfo(const char * data,int len,HttpResponseInfo * response_info,bool * response_truncated)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);
394 }
395
WriteMetadata(const GURL & url,RequestPriority priority,base::Time expected_response_time,IOBuffer * buf,int buf_len)396 void HttpCache::WriteMetadata(const GURL& url,
397 RequestPriority priority,
398 base::Time expected_response_time,
399 IOBuffer* buf,
400 int buf_len) {
401 if (!buf_len)
402 return;
403
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());
408 }
409
410 HttpCache::Transaction* trans =
411 new HttpCache::Transaction(priority, this);
412 MetadataWriter* writer = new MetadataWriter(trans);
413
414 // The writer will self destruct when done.
415 writer->Write(url, expected_response_time, buf, buf_len);
416 }
417
CloseAllConnections()418 void HttpCache::CloseAllConnections() {
419 HttpNetworkSession* session = GetSession();
420 if (session)
421 session->CloseAllConnections();
422 }
423
CloseIdleConnections()424 void HttpCache::CloseIdleConnections() {
425 HttpNetworkSession* session = GetSession();
426 if (session)
427 session->CloseIdleConnections();
428 }
429
OnExternalCacheHit(const GURL & url,const std::string & http_method)430 void HttpCache::OnExternalCacheHit(const GURL& url,
431 const std::string& http_method) {
432 if (!disk_cache_.get())
433 return;
434
435 HttpRequestInfo request_info;
436 request_info.url = url;
437 request_info.method = http_method;
438 std::string key = GenerateCacheKey(&request_info);
439 disk_cache_->OnExternalCacheHit(key);
440 }
441
InitializeInfiniteCache(const base::FilePath & path)442 void HttpCache::InitializeInfiniteCache(const base::FilePath& path) {
443 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
444 return;
445 base::WorkerPool::PostTask(FROM_HERE, base::Bind(&DeletePath, path), true);
446 }
447
CreateTransaction(RequestPriority priority,scoped_ptr<HttpTransaction> * trans)448 int HttpCache::CreateTransaction(RequestPriority priority,
449 scoped_ptr<HttpTransaction>* trans) {
450 // Do lazy initialization of disk cache if needed.
451 if (!disk_cache_.get()) {
452 // We don't care about the result.
453 CreateBackend(NULL, net::CompletionCallback());
454 }
455
456 trans->reset(new HttpCache::Transaction(priority, this));
457 return OK;
458 }
459
GetCache()460 HttpCache* HttpCache::GetCache() {
461 return this;
462 }
463
GetSession()464 HttpNetworkSession* HttpCache::GetSession() {
465 return network_layer_->GetSession();
466 }
467
468 scoped_ptr<HttpTransactionFactory>
SetHttpNetworkTransactionFactoryForTesting(scoped_ptr<HttpTransactionFactory> new_network_layer)469 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
470 scoped_ptr<HttpTransactionFactory> new_network_layer) {
471 scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass());
472 network_layer_ = new_network_layer.Pass();
473 return old_network_layer.Pass();
474 }
475
476 //-----------------------------------------------------------------------------
477
CreateBackend(disk_cache::Backend ** backend,const net::CompletionCallback & callback)478 int HttpCache::CreateBackend(disk_cache::Backend** backend,
479 const net::CompletionCallback& callback) {
480 if (!backend_factory_.get())
481 return ERR_FAILED;
482
483 building_backend_ = true;
484
485 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
486 backend));
487
488 // This is the only operation that we can do that is not related to any given
489 // entry, so we use an empty key for it.
490 PendingOp* pending_op = GetPendingOp(std::string());
491 if (pending_op->writer) {
492 if (!callback.is_null())
493 pending_op->pending_queue.push_back(item.release());
494 return ERR_IO_PENDING;
495 }
496
497 DCHECK(pending_op->pending_queue.empty());
498
499 pending_op->writer = item.release();
500 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
501 GetWeakPtr(), pending_op);
502
503 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
504 pending_op->callback);
505 if (rv != ERR_IO_PENDING) {
506 pending_op->writer->ClearCallback();
507 pending_op->callback.Run(rv);
508 }
509
510 return rv;
511 }
512
GetBackendForTransaction(Transaction * trans)513 int HttpCache::GetBackendForTransaction(Transaction* trans) {
514 if (disk_cache_.get())
515 return OK;
516
517 if (!building_backend_)
518 return ERR_FAILED;
519
520 WorkItem* item = new WorkItem(
521 WI_CREATE_BACKEND, trans, net::CompletionCallback(), NULL);
522 PendingOp* pending_op = GetPendingOp(std::string());
523 DCHECK(pending_op->writer);
524 pending_op->pending_queue.push_back(item);
525 return ERR_IO_PENDING;
526 }
527
528 // Generate a key that can be used inside the cache.
GenerateCacheKey(const HttpRequestInfo * request)529 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
530 // Strip out the reference, username, and password sections of the URL.
531 std::string url = HttpUtil::SpecForRequest(request->url);
532
533 DCHECK(mode_ != DISABLE);
534 if (mode_ == NORMAL) {
535 // No valid URL can begin with numerals, so we should not have to worry
536 // about collisions with normal URLs.
537 if (request->upload_data_stream &&
538 request->upload_data_stream->identifier()) {
539 url.insert(0, base::StringPrintf(
540 "%" PRId64 "/", request->upload_data_stream->identifier()));
541 }
542 return url;
543 }
544
545 // In playback and record mode, we cache everything.
546
547 // Lazily initialize.
548 if (playback_cache_map_ == NULL)
549 playback_cache_map_.reset(new PlaybackCacheMap());
550
551 // Each time we request an item from the cache, we tag it with a
552 // generation number. During playback, multiple fetches for the same
553 // item will use the same generation number and pull the proper
554 // instance of an URL from the cache.
555 int generation = 0;
556 DCHECK(playback_cache_map_ != NULL);
557 if (playback_cache_map_->find(url) != playback_cache_map_->end())
558 generation = (*playback_cache_map_)[url];
559 (*playback_cache_map_)[url] = generation + 1;
560
561 // The key into the cache is GENERATION # + METHOD + URL.
562 std::string result = base::IntToString(generation);
563 result.append(request->method);
564 result.append(url);
565 return result;
566 }
567
DoomActiveEntry(const std::string & key)568 void HttpCache::DoomActiveEntry(const std::string& key) {
569 ActiveEntriesMap::iterator it = active_entries_.find(key);
570 if (it == active_entries_.end())
571 return;
572
573 // This is not a performance critical operation, this is handling an error
574 // condition so it is OK to look up the entry again.
575 int rv = DoomEntry(key, NULL);
576 DCHECK_EQ(OK, rv);
577 }
578
DoomEntry(const std::string & key,Transaction * trans)579 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
580 // Need to abandon the ActiveEntry, but any transaction attached to the entry
581 // should not be impacted. Dooming an entry only means that it will no
582 // longer be returned by FindActiveEntry (and it will also be destroyed once
583 // all consumers are finished with the entry).
584 ActiveEntriesMap::iterator it = active_entries_.find(key);
585 if (it == active_entries_.end()) {
586 DCHECK(trans);
587 return AsyncDoomEntry(key, trans);
588 }
589
590 ActiveEntry* entry = it->second;
591 active_entries_.erase(it);
592
593 // We keep track of doomed entries so that we can ensure that they are
594 // cleaned up properly when the cache is destroyed.
595 doomed_entries_.insert(entry);
596
597 entry->disk_entry->Doom();
598 entry->doomed = true;
599
600 DCHECK(entry->writer || !entry->readers.empty());
601 return OK;
602 }
603
AsyncDoomEntry(const std::string & key,Transaction * trans)604 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
605 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
606 PendingOp* pending_op = GetPendingOp(key);
607 if (pending_op->writer) {
608 pending_op->pending_queue.push_back(item);
609 return ERR_IO_PENDING;
610 }
611
612 DCHECK(pending_op->pending_queue.empty());
613
614 pending_op->writer = item;
615 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
616 GetWeakPtr(), pending_op);
617
618 int rv = disk_cache_->DoomEntry(key, pending_op->callback);
619 if (rv != ERR_IO_PENDING) {
620 item->ClearTransaction();
621 pending_op->callback.Run(rv);
622 }
623
624 return rv;
625 }
626
DoomMainEntryForUrl(const GURL & url)627 void HttpCache::DoomMainEntryForUrl(const GURL& url) {
628 if (!disk_cache_)
629 return;
630
631 HttpRequestInfo temp_info;
632 temp_info.url = url;
633 temp_info.method = "GET";
634 std::string key = GenerateCacheKey(&temp_info);
635
636 // Defer to DoomEntry if there is an active entry, otherwise call
637 // AsyncDoomEntry without triggering a callback.
638 if (active_entries_.count(key))
639 DoomEntry(key, NULL);
640 else
641 AsyncDoomEntry(key, NULL);
642 }
643
FinalizeDoomedEntry(ActiveEntry * entry)644 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
645 DCHECK(entry->doomed);
646 DCHECK(!entry->writer);
647 DCHECK(entry->readers.empty());
648 DCHECK(entry->pending_queue.empty());
649
650 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
651 DCHECK(it != doomed_entries_.end());
652 doomed_entries_.erase(it);
653
654 delete entry;
655 }
656
FindActiveEntry(const std::string & key)657 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
658 ActiveEntriesMap::const_iterator it = active_entries_.find(key);
659 return it != active_entries_.end() ? it->second : NULL;
660 }
661
ActivateEntry(disk_cache::Entry * disk_entry)662 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
663 disk_cache::Entry* disk_entry) {
664 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
665 ActiveEntry* entry = new ActiveEntry(disk_entry);
666 active_entries_[disk_entry->GetKey()] = entry;
667 return entry;
668 }
669
DeactivateEntry(ActiveEntry * entry)670 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
671 DCHECK(!entry->will_process_pending_queue);
672 DCHECK(!entry->doomed);
673 DCHECK(!entry->writer);
674 DCHECK(entry->disk_entry);
675 DCHECK(entry->readers.empty());
676 DCHECK(entry->pending_queue.empty());
677
678 std::string key = entry->disk_entry->GetKey();
679 if (key.empty())
680 return SlowDeactivateEntry(entry);
681
682 ActiveEntriesMap::iterator it = active_entries_.find(key);
683 DCHECK(it != active_entries_.end());
684 DCHECK(it->second == entry);
685
686 active_entries_.erase(it);
687 delete entry;
688 }
689
690 // We don't know this entry's key so we have to find it without it.
SlowDeactivateEntry(ActiveEntry * entry)691 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
692 for (ActiveEntriesMap::iterator it = active_entries_.begin();
693 it != active_entries_.end(); ++it) {
694 if (it->second == entry) {
695 active_entries_.erase(it);
696 delete entry;
697 break;
698 }
699 }
700 }
701
GetPendingOp(const std::string & key)702 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
703 DCHECK(!FindActiveEntry(key));
704
705 PendingOpsMap::const_iterator it = pending_ops_.find(key);
706 if (it != pending_ops_.end())
707 return it->second;
708
709 PendingOp* operation = new PendingOp();
710 pending_ops_[key] = operation;
711 return operation;
712 }
713
DeletePendingOp(PendingOp * pending_op)714 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
715 std::string key;
716 if (pending_op->disk_entry)
717 key = pending_op->disk_entry->GetKey();
718
719 if (!key.empty()) {
720 PendingOpsMap::iterator it = pending_ops_.find(key);
721 DCHECK(it != pending_ops_.end());
722 pending_ops_.erase(it);
723 } else {
724 for (PendingOpsMap::iterator it = pending_ops_.begin();
725 it != pending_ops_.end(); ++it) {
726 if (it->second == pending_op) {
727 pending_ops_.erase(it);
728 break;
729 }
730 }
731 }
732 DCHECK(pending_op->pending_queue.empty());
733
734 delete pending_op;
735 }
736
OpenEntry(const std::string & key,ActiveEntry ** entry,Transaction * trans)737 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
738 Transaction* trans) {
739 ActiveEntry* active_entry = FindActiveEntry(key);
740 if (active_entry) {
741 *entry = active_entry;
742 return OK;
743 }
744
745 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
746 PendingOp* pending_op = GetPendingOp(key);
747 if (pending_op->writer) {
748 pending_op->pending_queue.push_back(item);
749 return ERR_IO_PENDING;
750 }
751
752 DCHECK(pending_op->pending_queue.empty());
753
754 pending_op->writer = item;
755 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
756 GetWeakPtr(), pending_op);
757
758 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry),
759 pending_op->callback);
760 if (rv != ERR_IO_PENDING) {
761 item->ClearTransaction();
762 pending_op->callback.Run(rv);
763 }
764
765 return rv;
766 }
767
CreateEntry(const std::string & key,ActiveEntry ** entry,Transaction * trans)768 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
769 Transaction* trans) {
770 if (FindActiveEntry(key)) {
771 return ERR_CACHE_RACE;
772 }
773
774 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
775 PendingOp* pending_op = GetPendingOp(key);
776 if (pending_op->writer) {
777 pending_op->pending_queue.push_back(item);
778 return ERR_IO_PENDING;
779 }
780
781 DCHECK(pending_op->pending_queue.empty());
782
783 pending_op->writer = item;
784 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
785 GetWeakPtr(), pending_op);
786
787 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
788 pending_op->callback);
789 if (rv != ERR_IO_PENDING) {
790 item->ClearTransaction();
791 pending_op->callback.Run(rv);
792 }
793
794 return rv;
795 }
796
DestroyEntry(ActiveEntry * entry)797 void HttpCache::DestroyEntry(ActiveEntry* entry) {
798 if (entry->doomed) {
799 FinalizeDoomedEntry(entry);
800 } else {
801 DeactivateEntry(entry);
802 }
803 }
804
AddTransactionToEntry(ActiveEntry * entry,Transaction * trans)805 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
806 DCHECK(entry);
807 DCHECK(entry->disk_entry);
808
809 // We implement a basic reader/writer lock for the disk cache entry. If
810 // there is already a writer, then everyone has to wait for the writer to
811 // finish before they can access the cache entry. There can be multiple
812 // readers.
813 //
814 // NOTE: If the transaction can only write, then the entry should not be in
815 // use (since any existing entry should have already been doomed).
816
817 if (entry->writer || entry->will_process_pending_queue) {
818 entry->pending_queue.push_back(trans);
819 return ERR_IO_PENDING;
820 }
821
822 if (trans->mode() & Transaction::WRITE) {
823 // transaction needs exclusive access to the entry
824 if (entry->readers.empty()) {
825 entry->writer = trans;
826 } else {
827 entry->pending_queue.push_back(trans);
828 return ERR_IO_PENDING;
829 }
830 } else {
831 // transaction needs read access to the entry
832 entry->readers.push_back(trans);
833 }
834
835 // We do this before calling EntryAvailable to force any further calls to
836 // AddTransactionToEntry to add their transaction to the pending queue, which
837 // ensures FIFO ordering.
838 if (!entry->writer && !entry->pending_queue.empty())
839 ProcessPendingQueue(entry);
840
841 return OK;
842 }
843
DoneWithEntry(ActiveEntry * entry,Transaction * trans,bool cancel)844 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
845 bool cancel) {
846 // If we already posted a task to move on to the next transaction and this was
847 // the writer, there is nothing to cancel.
848 if (entry->will_process_pending_queue && entry->readers.empty())
849 return;
850
851 if (entry->writer) {
852 DCHECK(trans == entry->writer);
853
854 // Assume there was a failure.
855 bool success = false;
856 if (cancel) {
857 DCHECK(entry->disk_entry);
858 // This is a successful operation in the sense that we want to keep the
859 // entry.
860 success = trans->AddTruncatedFlag();
861 // The previous operation may have deleted the entry.
862 if (!trans->entry())
863 return;
864 }
865 DoneWritingToEntry(entry, success);
866 } else {
867 DoneReadingFromEntry(entry, trans);
868 }
869 }
870
DoneWritingToEntry(ActiveEntry * entry,bool success)871 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
872 DCHECK(entry->readers.empty());
873
874 entry->writer = NULL;
875
876 if (success) {
877 ProcessPendingQueue(entry);
878 } else {
879 DCHECK(!entry->will_process_pending_queue);
880
881 // We failed to create this entry.
882 TransactionList pending_queue;
883 pending_queue.swap(entry->pending_queue);
884
885 entry->disk_entry->Doom();
886 DestroyEntry(entry);
887
888 // We need to do something about these pending entries, which now need to
889 // be added to a new entry.
890 while (!pending_queue.empty()) {
891 // ERR_CACHE_RACE causes the transaction to restart the whole process.
892 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
893 pending_queue.pop_front();
894 }
895 }
896 }
897
DoneReadingFromEntry(ActiveEntry * entry,Transaction * trans)898 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
899 DCHECK(!entry->writer);
900
901 TransactionList::iterator it =
902 std::find(entry->readers.begin(), entry->readers.end(), trans);
903 DCHECK(it != entry->readers.end());
904
905 entry->readers.erase(it);
906
907 ProcessPendingQueue(entry);
908 }
909
ConvertWriterToReader(ActiveEntry * entry)910 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
911 DCHECK(entry->writer);
912 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
913 DCHECK(entry->readers.empty());
914
915 Transaction* trans = entry->writer;
916
917 entry->writer = NULL;
918 entry->readers.push_back(trans);
919
920 ProcessPendingQueue(entry);
921 }
922
GetLoadStateForPendingTransaction(const Transaction * trans)923 LoadState HttpCache::GetLoadStateForPendingTransaction(
924 const Transaction* trans) {
925 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
926 if (i == active_entries_.end()) {
927 // If this is really a pending transaction, and it is not part of
928 // active_entries_, we should be creating the backend or the entry.
929 return LOAD_STATE_WAITING_FOR_CACHE;
930 }
931
932 Transaction* writer = i->second->writer;
933 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
934 }
935
RemovePendingTransaction(Transaction * trans)936 void HttpCache::RemovePendingTransaction(Transaction* trans) {
937 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
938 bool found = false;
939 if (i != active_entries_.end())
940 found = RemovePendingTransactionFromEntry(i->second, trans);
941
942 if (found)
943 return;
944
945 if (building_backend_) {
946 PendingOpsMap::const_iterator j = pending_ops_.find(std::string());
947 if (j != pending_ops_.end())
948 found = RemovePendingTransactionFromPendingOp(j->second, trans);
949
950 if (found)
951 return;
952 }
953
954 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
955 if (j != pending_ops_.end())
956 found = RemovePendingTransactionFromPendingOp(j->second, trans);
957
958 if (found)
959 return;
960
961 ActiveEntriesSet::iterator k = doomed_entries_.begin();
962 for (; k != doomed_entries_.end() && !found; ++k)
963 found = RemovePendingTransactionFromEntry(*k, trans);
964
965 DCHECK(found) << "Pending transaction not found";
966 }
967
RemovePendingTransactionFromEntry(ActiveEntry * entry,Transaction * trans)968 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
969 Transaction* trans) {
970 TransactionList& pending_queue = entry->pending_queue;
971
972 TransactionList::iterator j =
973 find(pending_queue.begin(), pending_queue.end(), trans);
974 if (j == pending_queue.end())
975 return false;
976
977 pending_queue.erase(j);
978 return true;
979 }
980
RemovePendingTransactionFromPendingOp(PendingOp * pending_op,Transaction * trans)981 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
982 Transaction* trans) {
983 if (pending_op->writer->Matches(trans)) {
984 pending_op->writer->ClearTransaction();
985 pending_op->writer->ClearEntry();
986 return true;
987 }
988 WorkItemList& pending_queue = pending_op->pending_queue;
989
990 WorkItemList::iterator it = pending_queue.begin();
991 for (; it != pending_queue.end(); ++it) {
992 if ((*it)->Matches(trans)) {
993 delete *it;
994 pending_queue.erase(it);
995 return true;
996 }
997 }
998 return false;
999 }
1000
SetupQuicServerInfoFactory(HttpNetworkSession * session)1001 void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) {
1002 if (session && session->params().enable_quic_persist_server_info &&
1003 !session->quic_stream_factory()->has_quic_server_info_factory()) {
1004 DCHECK(!quic_server_info_factory_);
1005 quic_server_info_factory_.reset(new QuicServerInfoFactoryAdaptor(this));
1006 session->quic_stream_factory()->set_quic_server_info_factory(
1007 quic_server_info_factory_.get());
1008 }
1009 }
1010
ProcessPendingQueue(ActiveEntry * entry)1011 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
1012 // Multiple readers may finish with an entry at once, so we want to batch up
1013 // calls to OnProcessPendingQueue. This flag also tells us that we should
1014 // not delete the entry before OnProcessPendingQueue runs.
1015 if (entry->will_process_pending_queue)
1016 return;
1017 entry->will_process_pending_queue = true;
1018
1019 base::MessageLoop::current()->PostTask(
1020 FROM_HERE,
1021 base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
1022 }
1023
OnProcessPendingQueue(ActiveEntry * entry)1024 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1025 entry->will_process_pending_queue = false;
1026 DCHECK(!entry->writer);
1027
1028 // If no one is interested in this entry, then we can deactivate it.
1029 if (entry->pending_queue.empty()) {
1030 if (entry->readers.empty())
1031 DestroyEntry(entry);
1032 return;
1033 }
1034
1035 // Promote next transaction from the pending queue.
1036 Transaction* next = entry->pending_queue.front();
1037 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1038 return; // Have to wait.
1039
1040 entry->pending_queue.erase(entry->pending_queue.begin());
1041
1042 int rv = AddTransactionToEntry(entry, next);
1043 if (rv != ERR_IO_PENDING) {
1044 next->io_callback().Run(rv);
1045 }
1046 }
1047
OnIOComplete(int result,PendingOp * pending_op)1048 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1049 WorkItemOperation op = pending_op->writer->operation();
1050
1051 // Completing the creation of the backend is simpler than the other cases.
1052 if (op == WI_CREATE_BACKEND)
1053 return OnBackendCreated(result, pending_op);
1054
1055 scoped_ptr<WorkItem> item(pending_op->writer);
1056 bool fail_requests = false;
1057
1058 ActiveEntry* entry = NULL;
1059 std::string key;
1060 if (result == OK) {
1061 if (op == WI_DOOM_ENTRY) {
1062 // Anything after a Doom has to be restarted.
1063 fail_requests = true;
1064 } else if (item->IsValid()) {
1065 key = pending_op->disk_entry->GetKey();
1066 entry = ActivateEntry(pending_op->disk_entry);
1067 } else {
1068 // The writer transaction is gone.
1069 if (op == WI_CREATE_ENTRY)
1070 pending_op->disk_entry->Doom();
1071 pending_op->disk_entry->Close();
1072 pending_op->disk_entry = NULL;
1073 fail_requests = true;
1074 }
1075 }
1076
1077 // We are about to notify a bunch of transactions, and they may decide to
1078 // re-issue a request (or send a different one). If we don't delete
1079 // pending_op, the new request will be appended to the end of the list, and
1080 // we'll see it again from this point before it has a chance to complete (and
1081 // we'll be messing out the request order). The down side is that if for some
1082 // reason notifying request A ends up cancelling request B (for the same key),
1083 // we won't find request B anywhere (because it would be in a local variable
1084 // here) and that's bad. If there is a chance for that to happen, we'll have
1085 // to move the callback used to be a CancelableCallback. By the way, for this
1086 // to happen the action (to cancel B) has to be synchronous to the
1087 // notification for request A.
1088 WorkItemList pending_items;
1089 pending_items.swap(pending_op->pending_queue);
1090 DeletePendingOp(pending_op);
1091
1092 item->NotifyTransaction(result, entry);
1093
1094 while (!pending_items.empty()) {
1095 item.reset(pending_items.front());
1096 pending_items.pop_front();
1097
1098 if (item->operation() == WI_DOOM_ENTRY) {
1099 // A queued doom request is always a race.
1100 fail_requests = true;
1101 } else if (result == OK) {
1102 entry = FindActiveEntry(key);
1103 if (!entry)
1104 fail_requests = true;
1105 }
1106
1107 if (fail_requests) {
1108 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1109 continue;
1110 }
1111
1112 if (item->operation() == WI_CREATE_ENTRY) {
1113 if (result == OK) {
1114 // A second Create request, but the first request succeeded.
1115 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1116 } else {
1117 if (op != WI_CREATE_ENTRY) {
1118 // Failed Open followed by a Create.
1119 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1120 fail_requests = true;
1121 } else {
1122 item->NotifyTransaction(result, entry);
1123 }
1124 }
1125 } else {
1126 if (op == WI_CREATE_ENTRY && result != OK) {
1127 // Failed Create followed by an Open.
1128 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1129 fail_requests = true;
1130 } else {
1131 item->NotifyTransaction(result, entry);
1132 }
1133 }
1134 }
1135 }
1136
1137 // static
OnPendingOpComplete(const base::WeakPtr<HttpCache> & cache,PendingOp * pending_op,int rv)1138 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
1139 PendingOp* pending_op,
1140 int rv) {
1141 if (cache.get()) {
1142 cache->OnIOComplete(rv, pending_op);
1143 } else {
1144 // The callback was cancelled so we should delete the pending_op that
1145 // was used with this callback.
1146 delete pending_op;
1147 }
1148 }
1149
OnBackendCreated(int result,PendingOp * pending_op)1150 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1151 scoped_ptr<WorkItem> item(pending_op->writer);
1152 WorkItemOperation op = item->operation();
1153 DCHECK_EQ(WI_CREATE_BACKEND, op);
1154
1155 // We don't need the callback anymore.
1156 pending_op->callback.Reset();
1157
1158 if (backend_factory_.get()) {
1159 // We may end up calling OnBackendCreated multiple times if we have pending
1160 // work items. The first call saves the backend and releases the factory,
1161 // and the last call clears building_backend_.
1162 backend_factory_.reset(); // Reclaim memory.
1163 if (result == OK)
1164 disk_cache_ = pending_op->backend.Pass();
1165 }
1166
1167 if (!pending_op->pending_queue.empty()) {
1168 WorkItem* pending_item = pending_op->pending_queue.front();
1169 pending_op->pending_queue.pop_front();
1170 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1171
1172 // We want to process a single callback at a time, because the cache may
1173 // go away from the callback.
1174 pending_op->writer = pending_item;
1175
1176 base::MessageLoop::current()->PostTask(
1177 FROM_HERE,
1178 base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(),
1179 result, pending_op));
1180 } else {
1181 building_backend_ = false;
1182 DeletePendingOp(pending_op);
1183 }
1184
1185 // The cache may be gone when we return from the callback.
1186 if (!item->DoCallback(result, disk_cache_.get()))
1187 item->NotifyTransaction(result, NULL);
1188 }
1189
1190 } // namespace net
1191