1 // Copyright (c) 2011 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/callback.h"
16 #include "base/format_macros.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/message_loop.h"
19 #include "base/pickle.h"
20 #include "base/stl_util-inl.h"
21 #include "base/string_number_conversions.h"
22 #include "base/string_util.h"
23 #include "base/stringprintf.h"
24 #include "net/base/io_buffer.h"
25 #include "net/base/load_flags.h"
26 #include "net/base/net_errors.h"
27 #include "net/disk_cache/disk_cache.h"
28 #include "net/http/disk_cache_based_ssl_host_info.h"
29 #include "net/http/http_cache_transaction.h"
30 #include "net/http/http_network_layer.h"
31 #include "net/http/http_network_session.h"
32 #include "net/http/http_request_info.h"
33 #include "net/http/http_response_headers.h"
34 #include "net/http/http_response_info.h"
35 #include "net/http/http_util.h"
36 #include "net/socket/ssl_host_info.h"
37
38 namespace net {
39
40 namespace {
41
CreateNetworkSession(HostResolver * host_resolver,CertVerifier * cert_verifier,DnsRRResolver * dnsrr_resolver,DnsCertProvenanceChecker * dns_cert_checker,ProxyService * proxy_service,SSLHostInfoFactory * ssl_host_info_factory,SSLConfigService * ssl_config_service,HttpAuthHandlerFactory * http_auth_handler_factory,NetworkDelegate * network_delegate,NetLog * net_log)42 HttpNetworkSession* CreateNetworkSession(
43 HostResolver* host_resolver,
44 CertVerifier* cert_verifier,
45 DnsRRResolver* dnsrr_resolver,
46 DnsCertProvenanceChecker* dns_cert_checker,
47 ProxyService* proxy_service,
48 SSLHostInfoFactory* ssl_host_info_factory,
49 SSLConfigService* ssl_config_service,
50 HttpAuthHandlerFactory* http_auth_handler_factory,
51 NetworkDelegate* network_delegate,
52 NetLog* net_log) {
53 HttpNetworkSession::Params params;
54 params.host_resolver = host_resolver;
55 params.cert_verifier = cert_verifier;
56 params.dnsrr_resolver = dnsrr_resolver;
57 params.dns_cert_checker = dns_cert_checker;
58 params.proxy_service = proxy_service;
59 params.ssl_host_info_factory = ssl_host_info_factory;
60 params.ssl_config_service = ssl_config_service;
61 params.http_auth_handler_factory = http_auth_handler_factory;
62 params.network_delegate = network_delegate;
63 params.net_log = net_log;
64 return new HttpNetworkSession(params);
65 }
66
67 } // namespace
68
DefaultBackend(CacheType type,const FilePath & path,int max_bytes,base::MessageLoopProxy * thread)69 HttpCache::DefaultBackend::DefaultBackend(CacheType type,
70 const FilePath& path,
71 int max_bytes,
72 base::MessageLoopProxy* thread)
73 : type_(type),
74 path_(path),
75 max_bytes_(max_bytes),
76 thread_(thread) {
77 }
78
~DefaultBackend()79 HttpCache::DefaultBackend::~DefaultBackend() {}
80
81 // static
InMemory(int max_bytes)82 HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) {
83 return new DefaultBackend(MEMORY_CACHE, FilePath(), max_bytes, NULL);
84 }
85
CreateBackend(NetLog * net_log,disk_cache::Backend ** backend,CompletionCallback * callback)86 int HttpCache::DefaultBackend::CreateBackend(NetLog* net_log,
87 disk_cache::Backend** backend,
88 CompletionCallback* callback) {
89 DCHECK_GE(max_bytes_, 0);
90 return disk_cache::CreateCacheBackend(type_, path_, max_bytes_, true,
91 thread_, net_log, backend, callback);
92 }
93
94 //-----------------------------------------------------------------------------
95
ActiveEntry(disk_cache::Entry * entry)96 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
97 : disk_entry(entry),
98 writer(NULL),
99 will_process_pending_queue(false),
100 doomed(false) {
101 }
102
~ActiveEntry()103 HttpCache::ActiveEntry::~ActiveEntry() {
104 if (disk_entry) {
105 disk_entry->Close();
106 disk_entry = NULL;
107 }
108 }
109
110 //-----------------------------------------------------------------------------
111
112 // This structure keeps track of work items that are attempting to create or
113 // open cache entries or the backend itself.
114 struct HttpCache::PendingOp {
PendingOpnet::HttpCache::PendingOp115 PendingOp() : disk_entry(NULL), backend(NULL), writer(NULL), callback(NULL) {}
~PendingOpnet::HttpCache::PendingOp116 ~PendingOp() {}
117
118 disk_cache::Entry* disk_entry;
119 disk_cache::Backend* backend;
120 WorkItem* writer;
121 CompletionCallback* callback; // BackendCallback.
122 WorkItemList pending_queue;
123 };
124
125 //-----------------------------------------------------------------------------
126
127 // The type of operation represented by a work item.
128 enum WorkItemOperation {
129 WI_CREATE_BACKEND,
130 WI_OPEN_ENTRY,
131 WI_CREATE_ENTRY,
132 WI_DOOM_ENTRY
133 };
134
135 // A work item encapsulates a single request to the backend with all the
136 // information needed to complete that request.
137 class HttpCache::WorkItem {
138 public:
WorkItem(WorkItemOperation operation,Transaction * trans,ActiveEntry ** entry)139 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
140 : operation_(operation), trans_(trans), entry_(entry), callback_(NULL),
141 backend_(NULL) {}
WorkItem(WorkItemOperation operation,Transaction * trans,CompletionCallback * cb,disk_cache::Backend ** backend)142 WorkItem(WorkItemOperation operation, Transaction* trans,
143 CompletionCallback* cb, disk_cache::Backend** backend)
144 : operation_(operation), trans_(trans), entry_(NULL), callback_(cb),
145 backend_(backend) {}
~WorkItem()146 ~WorkItem() {}
147
148 // Calls back the transaction with the result of the operation.
NotifyTransaction(int result,ActiveEntry * entry)149 void NotifyTransaction(int result, ActiveEntry* entry) {
150 DCHECK(!entry || entry->disk_entry);
151 if (entry_)
152 *entry_ = entry;
153 if (trans_)
154 trans_->io_callback()->Run(result);
155 }
156
157 // Notifies the caller about the operation completion. Returns true if the
158 // callback was invoked.
DoCallback(int result,disk_cache::Backend * backend)159 bool DoCallback(int result, disk_cache::Backend* backend) {
160 if (backend_)
161 *backend_ = backend;
162 if (callback_) {
163 callback_->Run(result);
164 return true;
165 }
166 return false;
167 }
168
operation()169 WorkItemOperation operation() { return operation_; }
ClearTransaction()170 void ClearTransaction() { trans_ = NULL; }
ClearEntry()171 void ClearEntry() { entry_ = NULL; }
ClearCallback()172 void ClearCallback() { callback_ = NULL; }
Matches(Transaction * trans) const173 bool Matches(Transaction* trans) const { return trans == trans_; }
IsValid() const174 bool IsValid() const { return trans_ || entry_ || callback_; }
175
176 private:
177 WorkItemOperation operation_;
178 Transaction* trans_;
179 ActiveEntry** entry_;
180 CompletionCallback* callback_; // User callback.
181 disk_cache::Backend** backend_;
182 };
183
184 //-----------------------------------------------------------------------------
185
186 // This class is a specialized type of CompletionCallback that allows us to
187 // pass multiple arguments to the completion routine.
188 class HttpCache::BackendCallback : public CallbackRunner<Tuple1<int> > {
189 public:
BackendCallback(HttpCache * cache,PendingOp * pending_op)190 BackendCallback(HttpCache* cache, PendingOp* pending_op)
191 : cache_(cache), pending_op_(pending_op) {}
~BackendCallback()192 ~BackendCallback() {}
193
RunWithParams(const Tuple1<int> & params)194 virtual void RunWithParams(const Tuple1<int>& params) {
195 if (cache_) {
196 cache_->OnIOComplete(params.a, pending_op_);
197 } else {
198 // The callback was cancelled so we should delete the pending_op that
199 // was used with this callback.
200 delete pending_op_;
201 }
202 delete this;
203 }
204
Cancel()205 void Cancel() {
206 cache_ = NULL;
207 }
208
209 private:
210 HttpCache* cache_;
211 PendingOp* pending_op_;
212 DISALLOW_COPY_AND_ASSIGN(BackendCallback);
213 };
214
215 //-----------------------------------------------------------------------------
216
217 // This class encapsulates a transaction whose only purpose is to write metadata
218 // to a given entry.
219 class HttpCache::MetadataWriter {
220 public:
MetadataWriter(HttpCache::Transaction * trans)221 explicit MetadataWriter(HttpCache::Transaction* trans)
222 : transaction_(trans),
223 ALLOW_THIS_IN_INITIALIZER_LIST(
224 callback_(this, &MetadataWriter::OnIOComplete)) {}
~MetadataWriter()225 ~MetadataWriter() {}
226
227 // Implementes the bulk of HttpCache::WriteMetadata.
228 void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf,
229 int buf_len);
230
231 private:
232 void VerifyResponse(int result);
233 void SelfDestroy();
234 void OnIOComplete(int result);
235
236 scoped_ptr<HttpCache::Transaction> transaction_;
237 bool verified_;
238 scoped_refptr<IOBuffer> buf_;
239 int buf_len_;
240 base::Time expected_response_time_;
241 CompletionCallbackImpl<MetadataWriter> callback_;
242 HttpRequestInfo request_info_;
243 DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
244 };
245
Write(const GURL & url,base::Time expected_response_time,IOBuffer * buf,int buf_len)246 void HttpCache::MetadataWriter::Write(const GURL& url,
247 base::Time expected_response_time,
248 IOBuffer* buf, int buf_len) {
249 DCHECK_GT(buf_len, 0);
250 DCHECK(buf);
251 DCHECK(buf->data());
252 request_info_.url = url;
253 request_info_.method = "GET";
254 request_info_.load_flags = LOAD_ONLY_FROM_CACHE;
255
256 expected_response_time_ = expected_response_time;
257 buf_ = buf;
258 buf_len_ = buf_len;
259 verified_ = false;
260
261 int rv = transaction_->Start(&request_info_, &callback_, BoundNetLog());
262 if (rv != ERR_IO_PENDING)
263 VerifyResponse(rv);
264 }
265
VerifyResponse(int result)266 void HttpCache::MetadataWriter::VerifyResponse(int result) {
267 verified_ = true;
268 if (result != OK)
269 return SelfDestroy();
270
271 const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
272 DCHECK(response_info->was_cached);
273 if (response_info->response_time != expected_response_time_)
274 return SelfDestroy();
275
276 result = transaction_->WriteMetadata(buf_, buf_len_, &callback_);
277 if (result != ERR_IO_PENDING)
278 SelfDestroy();
279 }
280
SelfDestroy()281 void HttpCache::MetadataWriter::SelfDestroy() {
282 delete this;
283 }
284
OnIOComplete(int result)285 void HttpCache::MetadataWriter::OnIOComplete(int result) {
286 if (!verified_)
287 return VerifyResponse(result);
288 SelfDestroy();
289 }
290
291 //-----------------------------------------------------------------------------
292
293 class HttpCache::SSLHostInfoFactoryAdaptor : public SSLHostInfoFactory {
294 public:
SSLHostInfoFactoryAdaptor(CertVerifier * cert_verifier,HttpCache * http_cache)295 SSLHostInfoFactoryAdaptor(CertVerifier* cert_verifier, HttpCache* http_cache)
296 : cert_verifier_(cert_verifier),
297 http_cache_(http_cache) {
298 }
299
GetForHost(const std::string & hostname,const SSLConfig & ssl_config)300 virtual SSLHostInfo* GetForHost(const std::string& hostname,
301 const SSLConfig& ssl_config) {
302 return new DiskCacheBasedSSLHostInfo(
303 hostname, ssl_config, cert_verifier_, http_cache_);
304 }
305
306 private:
307 CertVerifier* const cert_verifier_;
308 HttpCache* const http_cache_;
309 };
310
311 //-----------------------------------------------------------------------------
HttpCache(HostResolver * host_resolver,CertVerifier * cert_verifier,DnsRRResolver * dnsrr_resolver,DnsCertProvenanceChecker * dns_cert_checker_,ProxyService * proxy_service,SSLConfigService * ssl_config_service,HttpAuthHandlerFactory * http_auth_handler_factory,NetworkDelegate * network_delegate,NetLog * net_log,BackendFactory * backend_factory)312 HttpCache::HttpCache(HostResolver* host_resolver,
313 CertVerifier* cert_verifier,
314 DnsRRResolver* dnsrr_resolver,
315 DnsCertProvenanceChecker* dns_cert_checker_,
316 ProxyService* proxy_service,
317 SSLConfigService* ssl_config_service,
318 HttpAuthHandlerFactory* http_auth_handler_factory,
319 NetworkDelegate* network_delegate,
320 NetLog* net_log,
321 BackendFactory* backend_factory)
322 : net_log_(net_log),
323 backend_factory_(backend_factory),
324 building_backend_(false),
325 mode_(NORMAL),
326 ssl_host_info_factory_(new SSLHostInfoFactoryAdaptor(
327 cert_verifier,
328 ALLOW_THIS_IN_INITIALIZER_LIST(this))),
329 network_layer_(
330 new HttpNetworkLayer(
331 CreateNetworkSession(
332 host_resolver,
333 cert_verifier,
334 dnsrr_resolver,
335 dns_cert_checker_,
336 proxy_service,
337 ssl_host_info_factory_.get(),
338 ssl_config_service,
339 http_auth_handler_factory,
340 network_delegate,
341 net_log))),
342 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) {
343 }
344
345
HttpCache(HttpNetworkSession * session,BackendFactory * backend_factory)346 HttpCache::HttpCache(HttpNetworkSession* session,
347 BackendFactory* backend_factory)
348 : net_log_(session->net_log()),
349 backend_factory_(backend_factory),
350 building_backend_(false),
351 mode_(NORMAL),
352 ssl_host_info_factory_(new SSLHostInfoFactoryAdaptor(
353 session->cert_verifier(),
354 ALLOW_THIS_IN_INITIALIZER_LIST(this))),
355 network_layer_(new HttpNetworkLayer(session)),
356 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) {
357 }
358
HttpCache(HttpTransactionFactory * network_layer,NetLog * net_log,BackendFactory * backend_factory)359 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
360 NetLog* net_log,
361 BackendFactory* backend_factory)
362 : net_log_(net_log),
363 backend_factory_(backend_factory),
364 building_backend_(false),
365 mode_(NORMAL),
366 network_layer_(network_layer),
367 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) {
368 }
369
~HttpCache()370 HttpCache::~HttpCache() {
371 // If we have any active entries remaining, then we need to deactivate them.
372 // We may have some pending calls to OnProcessPendingQueue, but since those
373 // won't run (due to our destruction), we can simply ignore the corresponding
374 // will_process_pending_queue flag.
375 while (!active_entries_.empty()) {
376 ActiveEntry* entry = active_entries_.begin()->second;
377 entry->will_process_pending_queue = false;
378 entry->pending_queue.clear();
379 entry->readers.clear();
380 entry->writer = NULL;
381 DeactivateEntry(entry);
382 }
383
384 STLDeleteElements(&doomed_entries_);
385
386 PendingOpsMap::iterator pending_it = pending_ops_.begin();
387 for (; pending_it != pending_ops_.end(); ++pending_it) {
388 // We are not notifying the transactions about the cache going away, even
389 // though they are waiting for a callback that will never fire.
390 PendingOp* pending_op = pending_it->second;
391 delete pending_op->writer;
392 bool delete_pending_op = true;
393 if (building_backend_) {
394 // If we don't have a backend, when its construction finishes it will
395 // deliver the callbacks.
396 BackendCallback* callback =
397 static_cast<BackendCallback*>(pending_op->callback);
398 if (callback) {
399 // The callback will delete the pending operation.
400 callback->Cancel();
401 delete_pending_op = false;
402 }
403 } else {
404 delete pending_op->callback;
405 }
406
407 STLDeleteElements(&pending_op->pending_queue);
408 if (delete_pending_op)
409 delete pending_op;
410 }
411 }
412
GetBackend(disk_cache::Backend ** backend,CompletionCallback * callback)413 int HttpCache::GetBackend(disk_cache::Backend** backend,
414 CompletionCallback* callback) {
415 DCHECK(callback != NULL);
416
417 if (disk_cache_.get()) {
418 *backend = disk_cache_.get();
419 return OK;
420 }
421
422 return CreateBackend(backend, callback);
423 }
424
GetCurrentBackend()425 disk_cache::Backend* HttpCache::GetCurrentBackend() {
426 return disk_cache_.get();
427 }
428
429 // static
ParseResponseInfo(const char * data,int len,HttpResponseInfo * response_info,bool * response_truncated)430 bool HttpCache::ParseResponseInfo(const char* data, int len,
431 HttpResponseInfo* response_info,
432 bool* response_truncated) {
433 Pickle pickle(data, len);
434 return response_info->InitFromPickle(pickle, response_truncated);
435 }
436
WriteMetadata(const GURL & url,base::Time expected_response_time,IOBuffer * buf,int buf_len)437 void HttpCache::WriteMetadata(const GURL& url,
438 base::Time expected_response_time, IOBuffer* buf,
439 int buf_len) {
440 if (!buf_len)
441 return;
442
443 // Do lazy initialization of disk cache if needed.
444 if (!disk_cache_.get())
445 CreateBackend(NULL, NULL); // We don't care about the result.
446
447 HttpCache::Transaction* trans = new HttpCache::Transaction(this);
448 MetadataWriter* writer = new MetadataWriter(trans);
449
450 // The writer will self destruct when done.
451 writer->Write(url, expected_response_time, buf, buf_len);
452 }
453
CloseAllConnections()454 void HttpCache::CloseAllConnections() {
455 net::HttpNetworkLayer* network =
456 static_cast<net::HttpNetworkLayer*>(network_layer_.get());
457 HttpNetworkSession* session = network->GetSession();
458 if (session)
459 session->CloseAllConnections();
460 }
461
CloseIdleConnections()462 void HttpCache::CloseIdleConnections() {
463 net::HttpNetworkLayer* network =
464 static_cast<net::HttpNetworkLayer*>(network_layer_.get());
465 HttpNetworkSession* session = network->GetSession();
466 if (session)
467 session->CloseIdleConnections();
468 }
469
CreateTransaction(scoped_ptr<HttpTransaction> * trans)470 int HttpCache::CreateTransaction(scoped_ptr<HttpTransaction>* trans) {
471 // Do lazy initialization of disk cache if needed.
472 if (!disk_cache_.get())
473 CreateBackend(NULL, NULL); // We don't care about the result.
474
475 trans->reset(new HttpCache::Transaction(this));
476 return OK;
477 }
478
GetCache()479 HttpCache* HttpCache::GetCache() {
480 return this;
481 }
482
GetSession()483 HttpNetworkSession* HttpCache::GetSession() {
484 net::HttpNetworkLayer* network =
485 static_cast<net::HttpNetworkLayer*>(network_layer_.get());
486 return network->GetSession();
487 }
488
Suspend(bool suspend)489 void HttpCache::Suspend(bool suspend) {
490 network_layer_->Suspend(suspend);
491 }
492
493 //-----------------------------------------------------------------------------
494
CreateBackend(disk_cache::Backend ** backend,CompletionCallback * callback)495 int HttpCache::CreateBackend(disk_cache::Backend** backend,
496 CompletionCallback* callback) {
497 if (!backend_factory_.get())
498 return ERR_FAILED;
499
500 building_backend_ = true;
501
502 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
503 backend));
504
505 // This is the only operation that we can do that is not related to any given
506 // entry, so we use an empty key for it.
507 PendingOp* pending_op = GetPendingOp("");
508 if (pending_op->writer) {
509 if (callback)
510 pending_op->pending_queue.push_back(item.release());
511 return ERR_IO_PENDING;
512 }
513
514 DCHECK(pending_op->pending_queue.empty());
515
516 pending_op->writer = item.release();
517 BackendCallback* my_callback = new BackendCallback(this, pending_op);
518 pending_op->callback = my_callback;
519
520 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
521 my_callback);
522 if (rv != ERR_IO_PENDING) {
523 pending_op->writer->ClearCallback();
524 my_callback->Run(rv);
525 }
526
527 return rv;
528 }
529
GetBackendForTransaction(Transaction * trans)530 int HttpCache::GetBackendForTransaction(Transaction* trans) {
531 if (disk_cache_.get())
532 return OK;
533
534 if (!building_backend_)
535 return ERR_FAILED;
536
537 WorkItem* item = new WorkItem(WI_CREATE_BACKEND, trans, NULL, NULL);
538 PendingOp* pending_op = GetPendingOp("");
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 && request->upload_data->identifier()) {
554 url.insert(0, base::StringPrintf("%" PRId64 "/",
555 request->upload_data->identifier()));
556 }
557 return url;
558 }
559
560 // In playback and record mode, we cache everything.
561
562 // Lazily initialize.
563 if (playback_cache_map_ == NULL)
564 playback_cache_map_.reset(new PlaybackCacheMap());
565
566 // Each time we request an item from the cache, we tag it with a
567 // generation number. During playback, multiple fetches for the same
568 // item will use the same generation number and pull the proper
569 // instance of an URL from the cache.
570 int generation = 0;
571 DCHECK(playback_cache_map_ != NULL);
572 if (playback_cache_map_->find(url) != playback_cache_map_->end())
573 generation = (*playback_cache_map_)[url];
574 (*playback_cache_map_)[url] = generation + 1;
575
576 // The key into the cache is GENERATION # + METHOD + URL.
577 std::string result = base::IntToString(generation);
578 result.append(request->method);
579 result.append(url);
580 return result;
581 }
582
DoomEntry(const std::string & key,Transaction * trans)583 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
584 // Need to abandon the ActiveEntry, but any transaction attached to the entry
585 // should not be impacted. Dooming an entry only means that it will no
586 // longer be returned by FindActiveEntry (and it will also be destroyed once
587 // all consumers are finished with the entry).
588 ActiveEntriesMap::iterator it = active_entries_.find(key);
589 if (it == active_entries_.end()) {
590 return AsyncDoomEntry(key, trans);
591 }
592
593 ActiveEntry* entry = it->second;
594 active_entries_.erase(it);
595
596 // We keep track of doomed entries so that we can ensure that they are
597 // cleaned up properly when the cache is destroyed.
598 doomed_entries_.insert(entry);
599
600 entry->disk_entry->Doom();
601 entry->doomed = true;
602
603 DCHECK(entry->writer || !entry->readers.empty());
604 return OK;
605 }
606
AsyncDoomEntry(const std::string & key,Transaction * trans)607 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
608 DCHECK(trans);
609 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
610 PendingOp* pending_op = GetPendingOp(key);
611 if (pending_op->writer) {
612 pending_op->pending_queue.push_back(item);
613 return ERR_IO_PENDING;
614 }
615
616 DCHECK(pending_op->pending_queue.empty());
617
618 pending_op->writer = item;
619 BackendCallback* my_callback = new BackendCallback(this, pending_op);
620 pending_op->callback = my_callback;
621
622 int rv = disk_cache_->DoomEntry(key, my_callback);
623 if (rv != ERR_IO_PENDING) {
624 item->ClearTransaction();
625 my_callback->Run(rv);
626 }
627
628 return rv;
629 }
630
FinalizeDoomedEntry(ActiveEntry * entry)631 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
632 DCHECK(entry->doomed);
633 DCHECK(!entry->writer);
634 DCHECK(entry->readers.empty());
635 DCHECK(entry->pending_queue.empty());
636
637 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
638 DCHECK(it != doomed_entries_.end());
639 doomed_entries_.erase(it);
640
641 delete entry;
642 }
643
FindActiveEntry(const std::string & key)644 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
645 ActiveEntriesMap::const_iterator it = active_entries_.find(key);
646 return it != active_entries_.end() ? it->second : NULL;
647 }
648
ActivateEntry(disk_cache::Entry * disk_entry)649 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
650 disk_cache::Entry* disk_entry) {
651 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
652 ActiveEntry* entry = new ActiveEntry(disk_entry);
653 active_entries_[disk_entry->GetKey()] = entry;
654 return entry;
655 }
656
DeactivateEntry(ActiveEntry * entry)657 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
658 DCHECK(!entry->will_process_pending_queue);
659 DCHECK(!entry->doomed);
660 DCHECK(!entry->writer);
661 DCHECK(entry->disk_entry);
662 DCHECK(entry->readers.empty());
663 DCHECK(entry->pending_queue.empty());
664
665 std::string key = entry->disk_entry->GetKey();
666 if (key.empty())
667 return SlowDeactivateEntry(entry);
668
669 ActiveEntriesMap::iterator it = active_entries_.find(key);
670 DCHECK(it != active_entries_.end());
671 DCHECK(it->second == entry);
672
673 active_entries_.erase(it);
674 delete entry;
675 }
676
677 // We don't know this entry's key so we have to find it without it.
SlowDeactivateEntry(ActiveEntry * entry)678 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
679 for (ActiveEntriesMap::iterator it = active_entries_.begin();
680 it != active_entries_.end(); ++it) {
681 if (it->second == entry) {
682 active_entries_.erase(it);
683 delete entry;
684 break;
685 }
686 }
687 }
688
GetPendingOp(const std::string & key)689 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
690 DCHECK(!FindActiveEntry(key));
691
692 PendingOpsMap::const_iterator it = pending_ops_.find(key);
693 if (it != pending_ops_.end())
694 return it->second;
695
696 PendingOp* operation = new PendingOp();
697 pending_ops_[key] = operation;
698 return operation;
699 }
700
DeletePendingOp(PendingOp * pending_op)701 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
702 std::string key;
703 if (pending_op->disk_entry)
704 key = pending_op->disk_entry->GetKey();
705
706 if (!key.empty()) {
707 PendingOpsMap::iterator it = pending_ops_.find(key);
708 DCHECK(it != pending_ops_.end());
709 pending_ops_.erase(it);
710 } else {
711 for (PendingOpsMap::iterator it = pending_ops_.begin();
712 it != pending_ops_.end(); ++it) {
713 if (it->second == pending_op) {
714 pending_ops_.erase(it);
715 break;
716 }
717 }
718 }
719 DCHECK(pending_op->pending_queue.empty());
720
721 delete pending_op;
722 }
723
OpenEntry(const std::string & key,ActiveEntry ** entry,Transaction * trans)724 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
725 Transaction* trans) {
726 ActiveEntry* active_entry = FindActiveEntry(key);
727 if (active_entry) {
728 *entry = active_entry;
729 return OK;
730 }
731
732 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
733 PendingOp* pending_op = GetPendingOp(key);
734 if (pending_op->writer) {
735 pending_op->pending_queue.push_back(item);
736 return ERR_IO_PENDING;
737 }
738
739 DCHECK(pending_op->pending_queue.empty());
740
741 pending_op->writer = item;
742 BackendCallback* my_callback = new BackendCallback(this, pending_op);
743 pending_op->callback = my_callback;
744
745 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry), my_callback);
746 if (rv != ERR_IO_PENDING) {
747 item->ClearTransaction();
748 my_callback->Run(rv);
749 }
750
751 return rv;
752 }
753
CreateEntry(const std::string & key,ActiveEntry ** entry,Transaction * trans)754 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
755 Transaction* trans) {
756 DCHECK(!FindActiveEntry(key));
757
758 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
759 PendingOp* pending_op = GetPendingOp(key);
760 if (pending_op->writer) {
761 pending_op->pending_queue.push_back(item);
762 return ERR_IO_PENDING;
763 }
764
765 DCHECK(pending_op->pending_queue.empty());
766
767 pending_op->writer = item;
768 BackendCallback* my_callback = new BackendCallback(this, pending_op);
769 pending_op->callback = my_callback;
770
771 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
772 my_callback);
773 if (rv != ERR_IO_PENDING) {
774 item->ClearTransaction();
775 my_callback->Run(rv);
776 }
777
778 return rv;
779 }
780
DestroyEntry(ActiveEntry * entry)781 void HttpCache::DestroyEntry(ActiveEntry* entry) {
782 if (entry->doomed) {
783 FinalizeDoomedEntry(entry);
784 } else {
785 DeactivateEntry(entry);
786 }
787 }
788
AddTransactionToEntry(ActiveEntry * entry,Transaction * trans)789 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
790 DCHECK(entry);
791 DCHECK(entry->disk_entry);
792
793 // We implement a basic reader/writer lock for the disk cache entry. If
794 // there is already a writer, then everyone has to wait for the writer to
795 // finish before they can access the cache entry. There can be multiple
796 // readers.
797 //
798 // NOTE: If the transaction can only write, then the entry should not be in
799 // use (since any existing entry should have already been doomed).
800
801 if (entry->writer || entry->will_process_pending_queue) {
802 entry->pending_queue.push_back(trans);
803 return ERR_IO_PENDING;
804 }
805
806 if (trans->mode() & Transaction::WRITE) {
807 // transaction needs exclusive access to the entry
808 if (entry->readers.empty()) {
809 entry->writer = trans;
810 } else {
811 entry->pending_queue.push_back(trans);
812 return ERR_IO_PENDING;
813 }
814 } else {
815 // transaction needs read access to the entry
816 entry->readers.push_back(trans);
817 }
818
819 // We do this before calling EntryAvailable to force any further calls to
820 // AddTransactionToEntry to add their transaction to the pending queue, which
821 // ensures FIFO ordering.
822 if (!entry->writer && !entry->pending_queue.empty())
823 ProcessPendingQueue(entry);
824
825 return OK;
826 }
827
DoneWithEntry(ActiveEntry * entry,Transaction * trans,bool cancel)828 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
829 bool cancel) {
830 // If we already posted a task to move on to the next transaction and this was
831 // the writer, there is nothing to cancel.
832 if (entry->will_process_pending_queue && entry->readers.empty())
833 return;
834
835 if (entry->writer) {
836 DCHECK(trans == entry->writer);
837
838 // Assume there was a failure.
839 bool success = false;
840 if (cancel) {
841 DCHECK(entry->disk_entry);
842 // This is a successful operation in the sense that we want to keep the
843 // entry.
844 success = trans->AddTruncatedFlag();
845 }
846 DoneWritingToEntry(entry, success);
847 } else {
848 DoneReadingFromEntry(entry, trans);
849 }
850 }
851
DoneWritingToEntry(ActiveEntry * entry,bool success)852 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
853 DCHECK(entry->readers.empty());
854
855 entry->writer = NULL;
856
857 if (success) {
858 ProcessPendingQueue(entry);
859 } else {
860 DCHECK(!entry->will_process_pending_queue);
861
862 // We failed to create this entry.
863 TransactionList pending_queue;
864 pending_queue.swap(entry->pending_queue);
865
866 entry->disk_entry->Doom();
867 DestroyEntry(entry);
868
869 // We need to do something about these pending entries, which now need to
870 // be added to a new entry.
871 while (!pending_queue.empty()) {
872 // ERR_CACHE_RACE causes the transaction to restart the whole process.
873 pending_queue.front()->io_callback()->Run(ERR_CACHE_RACE);
874 pending_queue.pop_front();
875 }
876 }
877 }
878
DoneReadingFromEntry(ActiveEntry * entry,Transaction * trans)879 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
880 DCHECK(!entry->writer);
881
882 TransactionList::iterator it =
883 std::find(entry->readers.begin(), entry->readers.end(), trans);
884 DCHECK(it != entry->readers.end());
885
886 entry->readers.erase(it);
887
888 ProcessPendingQueue(entry);
889 }
890
ConvertWriterToReader(ActiveEntry * entry)891 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
892 DCHECK(entry->writer);
893 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
894 DCHECK(entry->readers.empty());
895
896 Transaction* trans = entry->writer;
897
898 entry->writer = NULL;
899 entry->readers.push_back(trans);
900
901 ProcessPendingQueue(entry);
902 }
903
GetLoadStateForPendingTransaction(const Transaction * trans)904 LoadState HttpCache::GetLoadStateForPendingTransaction(
905 const Transaction* trans) {
906 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
907 if (i == active_entries_.end()) {
908 // If this is really a pending transaction, and it is not part of
909 // active_entries_, we should be creating the backend or the entry.
910 return LOAD_STATE_WAITING_FOR_CACHE;
911 }
912
913 Transaction* writer = i->second->writer;
914 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
915 }
916
RemovePendingTransaction(Transaction * trans)917 void HttpCache::RemovePendingTransaction(Transaction* trans) {
918 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
919 bool found = false;
920 if (i != active_entries_.end())
921 found = RemovePendingTransactionFromEntry(i->second, trans);
922
923 if (found)
924 return;
925
926 if (building_backend_) {
927 PendingOpsMap::const_iterator j = pending_ops_.find("");
928 if (j != pending_ops_.end())
929 found = RemovePendingTransactionFromPendingOp(j->second, trans);
930
931 if (found)
932 return;
933 }
934
935 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
936 if (j != pending_ops_.end())
937 found = RemovePendingTransactionFromPendingOp(j->second, trans);
938
939 if (found)
940 return;
941
942 ActiveEntriesSet::iterator k = doomed_entries_.begin();
943 for (; k != doomed_entries_.end() && !found; ++k)
944 found = RemovePendingTransactionFromEntry(*k, trans);
945
946 DCHECK(found) << "Pending transaction not found";
947 }
948
RemovePendingTransactionFromEntry(ActiveEntry * entry,Transaction * trans)949 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
950 Transaction* trans) {
951 TransactionList& pending_queue = entry->pending_queue;
952
953 TransactionList::iterator j =
954 find(pending_queue.begin(), pending_queue.end(), trans);
955 if (j == pending_queue.end())
956 return false;
957
958 pending_queue.erase(j);
959 return true;
960 }
961
RemovePendingTransactionFromPendingOp(PendingOp * pending_op,Transaction * trans)962 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
963 Transaction* trans) {
964 if (pending_op->writer->Matches(trans)) {
965 pending_op->writer->ClearTransaction();
966 pending_op->writer->ClearEntry();
967 return true;
968 }
969 WorkItemList& pending_queue = pending_op->pending_queue;
970
971 WorkItemList::iterator it = pending_queue.begin();
972 for (; it != pending_queue.end(); ++it) {
973 if ((*it)->Matches(trans)) {
974 delete *it;
975 pending_queue.erase(it);
976 return true;
977 }
978 }
979 return false;
980 }
981
ProcessPendingQueue(ActiveEntry * entry)982 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
983 // Multiple readers may finish with an entry at once, so we want to batch up
984 // calls to OnProcessPendingQueue. This flag also tells us that we should
985 // not delete the entry before OnProcessPendingQueue runs.
986 if (entry->will_process_pending_queue)
987 return;
988 entry->will_process_pending_queue = true;
989
990 MessageLoop::current()->PostTask(
991 FROM_HERE,
992 task_factory_.NewRunnableMethod(&HttpCache::OnProcessPendingQueue,
993 entry));
994 }
995
OnProcessPendingQueue(ActiveEntry * entry)996 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
997 entry->will_process_pending_queue = false;
998 DCHECK(!entry->writer);
999
1000 // If no one is interested in this entry, then we can de-activate it.
1001 if (entry->pending_queue.empty()) {
1002 if (entry->readers.empty())
1003 DestroyEntry(entry);
1004 return;
1005 }
1006
1007 // Promote next transaction from the pending queue.
1008 Transaction* next = entry->pending_queue.front();
1009 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1010 return; // Have to wait.
1011
1012 entry->pending_queue.erase(entry->pending_queue.begin());
1013
1014 int rv = AddTransactionToEntry(entry, next);
1015 if (rv != ERR_IO_PENDING) {
1016 next->io_callback()->Run(rv);
1017 }
1018 }
1019
OnIOComplete(int result,PendingOp * pending_op)1020 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1021 WorkItemOperation op = pending_op->writer->operation();
1022
1023 // Completing the creation of the backend is simpler than the other cases.
1024 if (op == WI_CREATE_BACKEND)
1025 return OnBackendCreated(result, pending_op);
1026
1027 scoped_ptr<WorkItem> item(pending_op->writer);
1028 bool fail_requests = false;
1029
1030 ActiveEntry* entry = NULL;
1031 std::string key;
1032 if (result == OK) {
1033 if (op == WI_DOOM_ENTRY) {
1034 // Anything after a Doom has to be restarted.
1035 fail_requests = true;
1036 } else if (item->IsValid()) {
1037 key = pending_op->disk_entry->GetKey();
1038 entry = ActivateEntry(pending_op->disk_entry);
1039 } else {
1040 // The writer transaction is gone.
1041 if (op == WI_CREATE_ENTRY)
1042 pending_op->disk_entry->Doom();
1043 pending_op->disk_entry->Close();
1044 pending_op->disk_entry = NULL;
1045 fail_requests = true;
1046 }
1047 }
1048
1049 // We are about to notify a bunch of transactions, and they may decide to
1050 // re-issue a request (or send a different one). If we don't delete
1051 // pending_op, the new request will be appended to the end of the list, and
1052 // we'll see it again from this point before it has a chance to complete (and
1053 // we'll be messing out the request order). The down side is that if for some
1054 // reason notifying request A ends up cancelling request B (for the same key),
1055 // we won't find request B anywhere (because it would be in a local variable
1056 // here) and that's bad. If there is a chance for that to happen, we'll have
1057 // to move the callback used to be a CancelableCallback. By the way, for this
1058 // to happen the action (to cancel B) has to be synchronous to the
1059 // notification for request A.
1060 WorkItemList pending_items;
1061 pending_items.swap(pending_op->pending_queue);
1062 DeletePendingOp(pending_op);
1063
1064 item->NotifyTransaction(result, entry);
1065
1066 while (!pending_items.empty()) {
1067 item.reset(pending_items.front());
1068 pending_items.pop_front();
1069
1070 if (item->operation() == WI_DOOM_ENTRY) {
1071 // A queued doom request is always a race.
1072 fail_requests = true;
1073 } else if (result == OK) {
1074 entry = FindActiveEntry(key);
1075 if (!entry)
1076 fail_requests = true;
1077 }
1078
1079 if (fail_requests) {
1080 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1081 continue;
1082 }
1083
1084 if (item->operation() == WI_CREATE_ENTRY) {
1085 if (result == OK) {
1086 // A second Create request, but the first request succeded.
1087 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1088 } else {
1089 if (op != WI_CREATE_ENTRY) {
1090 // Failed Open followed by a Create.
1091 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1092 fail_requests = true;
1093 } else {
1094 item->NotifyTransaction(result, entry);
1095 }
1096 }
1097 } else {
1098 if (op == WI_CREATE_ENTRY && result != OK) {
1099 // Failed Create followed by an Open.
1100 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1101 fail_requests = true;
1102 } else {
1103 item->NotifyTransaction(result, entry);
1104 }
1105 }
1106 }
1107 }
1108
OnBackendCreated(int result,PendingOp * pending_op)1109 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1110 scoped_ptr<WorkItem> item(pending_op->writer);
1111 WorkItemOperation op = item->operation();
1112 DCHECK_EQ(WI_CREATE_BACKEND, op);
1113
1114 // We don't need the callback anymore.
1115 pending_op->callback = NULL;
1116 disk_cache::Backend* backend = pending_op->backend;
1117
1118 if (backend_factory_.get()) {
1119 // We may end up calling OnBackendCreated multiple times if we have pending
1120 // work items. The first call saves the backend and releases the factory,
1121 // and the last call clears building_backend_.
1122 backend_factory_.reset(); // Reclaim memory.
1123 if (result == OK)
1124 disk_cache_.reset(backend);
1125 }
1126
1127 if (!pending_op->pending_queue.empty()) {
1128 WorkItem* pending_item = pending_op->pending_queue.front();
1129 pending_op->pending_queue.pop_front();
1130 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1131
1132 // We want to process a single callback at a time, because the cache may
1133 // go away from the callback.
1134 pending_op->writer = pending_item;
1135
1136 MessageLoop::current()->PostTask(
1137 FROM_HERE,
1138 task_factory_.NewRunnableMethod(&HttpCache::OnBackendCreated,
1139 result, pending_op));
1140 } else {
1141 building_backend_ = false;
1142 DeletePendingOp(pending_op);
1143 }
1144
1145 // The cache may be gone when we return from the callback.
1146 if (!item->DoCallback(result, backend))
1147 item->NotifyTransaction(result, NULL);
1148 }
1149
1150 } // namespace net
1151