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