1 // Copyright (c) 2006-2009 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/format_macros.h"
16 #include "base/message_loop.h"
17 #include "base/pickle.h"
18 #include "base/ref_counted.h"
19 #include "base/string_util.h"
20 #include "net/base/io_buffer.h"
21 #include "net/base/net_errors.h"
22 #include "net/disk_cache/disk_cache.h"
23 #include "net/flip/flip_session_pool.h"
24 #include "net/http/http_cache_transaction.h"
25 #include "net/http/http_network_layer.h"
26 #include "net/http/http_network_session.h"
27 #include "net/http/http_request_info.h"
28 #include "net/http/http_response_headers.h"
29 #include "net/http/http_response_info.h"
30
31 namespace net {
32
33 // disk cache entry data indices.
34 enum {
35 kResponseInfoIndex,
36 kResponseContentIndex
37 };
38
39 //-----------------------------------------------------------------------------
40
ActiveEntry(disk_cache::Entry * e)41 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* e)
42 : disk_entry(e),
43 writer(NULL),
44 will_process_pending_queue(false),
45 doomed(false) {
46 }
47
~ActiveEntry()48 HttpCache::ActiveEntry::~ActiveEntry() {
49 if (disk_entry)
50 disk_entry->Close();
51 }
52
53 //-----------------------------------------------------------------------------
54
55 // This structure keeps track of work items that are attempting to create or
56 // open cache entries.
57 struct HttpCache::NewEntry {
NewEntrynet::HttpCache::NewEntry58 NewEntry() : disk_entry(NULL), writer(NULL) {}
~NewEntrynet::HttpCache::NewEntry59 ~NewEntry() {}
60
61 disk_cache::Entry* disk_entry;
62 WorkItem* writer;
63 WorkItemList pending_queue;
64 };
65
66 //-----------------------------------------------------------------------------
67
68 // The type of operation represented by a work item.
69 enum WorkItemOperation {
70 WI_OPEN_ENTRY,
71 WI_CREATE_ENTRY,
72 WI_DOOM_ENTRY
73 };
74
75 // A work item encapsulates a single request for cache entry with all the
76 // information needed to complete that request.
77 class HttpCache::WorkItem {
78 public:
WorkItem(ActiveEntry ** entry,CompletionCallback * callback,WorkItemOperation operation)79 WorkItem(ActiveEntry** entry, CompletionCallback* callback,
80 WorkItemOperation operation)
81 : entry_(entry), callback_(callback), operation_(operation) {}
~WorkItem()82 ~WorkItem() {}
operation()83 WorkItemOperation operation() { return operation_; }
84
85 // Calls back the transaction with the result of the operation.
NotifyTransaction(int result,ActiveEntry * entry)86 void NotifyTransaction(int result, ActiveEntry* entry) {
87 if (entry_)
88 *entry_ = entry;
89 if (callback_)
90 callback_->Run(result);
91 }
92
ClearCallback()93 void ClearCallback() { callback_ = NULL; }
ClearEntry()94 void ClearEntry() { entry_ = NULL; }
Matches(CompletionCallback * cb) const95 bool Matches(CompletionCallback* cb) const { return cb == callback_; }
IsValid() const96 bool IsValid() const { return callback_ || entry_; }
97
98 private:
99 ActiveEntry** entry_;
100 CompletionCallback* callback_;
101 WorkItemOperation operation_;
102 };
103
104 //-----------------------------------------------------------------------------
105
106 // This class is a specialized type of CompletionCallback that allows us to
107 // pass multiple arguments to the completion routine.
108 class HttpCache::BackendCallback : public CallbackRunner<Tuple1<int> > {
109 public:
BackendCallback(HttpCache * cache,NewEntry * entry)110 BackendCallback(HttpCache* cache, NewEntry* entry)
111 : cache_(cache), entry_(entry) {}
~BackendCallback()112 ~BackendCallback() {}
113
RunWithParams(const Tuple1<int> & params)114 virtual void RunWithParams(const Tuple1<int>& params) {
115 cache_->OnIOComplete(params.a, entry_);
116 delete this;
117 }
118
119 private:
120 HttpCache* cache_;
121 NewEntry* entry_;
122 DISALLOW_COPY_AND_ASSIGN(BackendCallback);
123 };
124
125 //-----------------------------------------------------------------------------
126
HttpCache(NetworkChangeNotifier * network_change_notifier,HostResolver * host_resolver,ProxyService * proxy_service,SSLConfigService * ssl_config_service,const FilePath & cache_dir,int cache_size)127 HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier,
128 HostResolver* host_resolver,
129 ProxyService* proxy_service,
130 SSLConfigService* ssl_config_service,
131 const FilePath& cache_dir,
132 int cache_size)
133 : disk_cache_dir_(cache_dir),
134 mode_(NORMAL),
135 type_(DISK_CACHE),
136 network_layer_(HttpNetworkLayer::CreateFactory(
137 network_change_notifier, host_resolver, proxy_service,
138 ssl_config_service)),
139 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
140 enable_range_support_(true),
141 cache_size_(cache_size) {
142 }
143
HttpCache(HttpNetworkSession * session,const FilePath & cache_dir,int cache_size)144 HttpCache::HttpCache(HttpNetworkSession* session,
145 const FilePath& cache_dir,
146 int cache_size)
147 : disk_cache_dir_(cache_dir),
148 mode_(NORMAL),
149 type_(DISK_CACHE),
150 network_layer_(HttpNetworkLayer::CreateFactory(session)),
151 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
152 enable_range_support_(true),
153 cache_size_(cache_size) {
154 }
155
HttpCache(NetworkChangeNotifier * network_change_notifier,HostResolver * host_resolver,ProxyService * proxy_service,SSLConfigService * ssl_config_service,int cache_size)156 HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier,
157 HostResolver* host_resolver,
158 ProxyService* proxy_service,
159 SSLConfigService* ssl_config_service,
160 int cache_size)
161 : mode_(NORMAL),
162 type_(MEMORY_CACHE),
163 network_layer_(HttpNetworkLayer::CreateFactory(
164 network_change_notifier, host_resolver, proxy_service,
165 ssl_config_service)),
166 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
167 enable_range_support_(true),
168 cache_size_(cache_size) {
169 }
170
HttpCache(HttpTransactionFactory * network_layer,disk_cache::Backend * disk_cache)171 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
172 disk_cache::Backend* disk_cache)
173 : mode_(NORMAL),
174 type_(DISK_CACHE),
175 network_layer_(network_layer),
176 disk_cache_(disk_cache),
177 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
178 enable_range_support_(true),
179 cache_size_(0) {
180 }
181
~HttpCache()182 HttpCache::~HttpCache() {
183 // If we have any active entries remaining, then we need to deactivate them.
184 // We may have some pending calls to OnProcessPendingQueue, but since those
185 // won't run (due to our destruction), we can simply ignore the corresponding
186 // will_process_pending_queue flag.
187 while (!active_entries_.empty()) {
188 ActiveEntry* entry = active_entries_.begin()->second;
189 entry->will_process_pending_queue = false;
190 entry->pending_queue.clear();
191 entry->readers.clear();
192 entry->writer = NULL;
193 DeactivateEntry(entry);
194 }
195
196 ActiveEntriesSet::iterator it = doomed_entries_.begin();
197 for (; it != doomed_entries_.end(); ++it)
198 delete *it;
199 }
200
GetBackend()201 disk_cache::Backend* HttpCache::GetBackend() {
202 if (disk_cache_.get())
203 return disk_cache_.get();
204
205 DCHECK_GE(cache_size_, 0);
206 if (type_ == MEMORY_CACHE) {
207 // We may end up with no folder name and no cache if the initialization
208 // of the disk cache fails. We want to be sure that what we wanted to have
209 // was an in-memory cache.
210 disk_cache_.reset(disk_cache::CreateInMemoryCacheBackend(cache_size_));
211 } else if (!disk_cache_dir_.empty()) {
212 disk_cache_.reset(disk_cache::CreateCacheBackend(disk_cache_dir_, true,
213 cache_size_, type_));
214 disk_cache_dir_ = FilePath(); // Reclaim memory.
215 }
216 return disk_cache_.get();
217 }
218
CreateTransaction(scoped_ptr<HttpTransaction> * trans)219 int HttpCache::CreateTransaction(scoped_ptr<HttpTransaction>* trans) {
220 // Do lazy initialization of disk cache if needed.
221 GetBackend();
222 trans->reset(new HttpCache::Transaction(this, enable_range_support_));
223 return OK;
224 }
225
GetCache()226 HttpCache* HttpCache::GetCache() {
227 return this;
228 }
229
GetSession()230 HttpNetworkSession* HttpCache::GetSession() {
231 net::HttpNetworkLayer* network =
232 static_cast<net::HttpNetworkLayer*>(network_layer_.get());
233 return network->GetSession();
234 }
235
Suspend(bool suspend)236 void HttpCache::Suspend(bool suspend) {
237 network_layer_->Suspend(suspend);
238 }
239
240 // static
ParseResponseInfo(const char * data,int len,HttpResponseInfo * response_info,bool * response_truncated)241 bool HttpCache::ParseResponseInfo(const char* data, int len,
242 HttpResponseInfo* response_info,
243 bool* response_truncated) {
244 Pickle pickle(data, len);
245 return response_info->InitFromPickle(pickle, response_truncated);
246 }
247
248 // static
ReadResponseInfo(disk_cache::Entry * disk_entry,HttpResponseInfo * response_info,bool * response_truncated)249 bool HttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry,
250 HttpResponseInfo* response_info,
251 bool* response_truncated) {
252 int size = disk_entry->GetDataSize(kResponseInfoIndex);
253
254 scoped_refptr<IOBuffer> buffer = new IOBuffer(size);
255 int rv = disk_entry->ReadData(kResponseInfoIndex, 0, buffer, size, NULL);
256 if (rv != size) {
257 DLOG(ERROR) << "ReadData failed: " << rv;
258 return false;
259 }
260
261 return ParseResponseInfo(buffer->data(), size, response_info,
262 response_truncated);
263 }
264
265 // static
WriteResponseInfo(disk_cache::Entry * disk_entry,const HttpResponseInfo * response_info,bool skip_transient_headers,bool response_truncated)266 bool HttpCache::WriteResponseInfo(disk_cache::Entry* disk_entry,
267 const HttpResponseInfo* response_info,
268 bool skip_transient_headers,
269 bool response_truncated) {
270 Pickle pickle;
271 response_info->Persist(
272 &pickle, skip_transient_headers, response_truncated);
273
274 scoped_refptr<WrappedIOBuffer> data = new WrappedIOBuffer(
275 reinterpret_cast<const char*>(pickle.data()));
276 int len = static_cast<int>(pickle.size());
277
278 return disk_entry->WriteData(kResponseInfoIndex, 0, data, len, NULL,
279 true) == len;
280 }
281
282 // Generate a key that can be used inside the cache.
GenerateCacheKey(const HttpRequestInfo * request)283 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
284 // Strip out the reference, username, and password sections of the URL.
285 std::string url = HttpUtil::SpecForRequest(request->url);
286
287 DCHECK(mode_ != DISABLE);
288 if (mode_ == NORMAL) {
289 // No valid URL can begin with numerals, so we should not have to worry
290 // about collisions with normal URLs.
291 if (request->upload_data && request->upload_data->identifier()) {
292 url.insert(0, StringPrintf("%" PRId64 "/",
293 request->upload_data->identifier()));
294 }
295 return url;
296 }
297
298 // In playback and record mode, we cache everything.
299
300 // Lazily initialize.
301 if (playback_cache_map_ == NULL)
302 playback_cache_map_.reset(new PlaybackCacheMap());
303
304 // Each time we request an item from the cache, we tag it with a
305 // generation number. During playback, multiple fetches for the same
306 // item will use the same generation number and pull the proper
307 // instance of an URL from the cache.
308 int generation = 0;
309 DCHECK(playback_cache_map_ != NULL);
310 if (playback_cache_map_->find(url) != playback_cache_map_->end())
311 generation = (*playback_cache_map_)[url];
312 (*playback_cache_map_)[url] = generation + 1;
313
314 // The key into the cache is GENERATION # + METHOD + URL.
315 std::string result = IntToString(generation);
316 result.append(request->method);
317 result.append(url);
318 return result;
319 }
320
DoomEntry(const std::string & key,CompletionCallback * callback)321 int HttpCache::DoomEntry(const std::string& key, CompletionCallback* callback) {
322 // Need to abandon the ActiveEntry, but any transaction attached to the entry
323 // should not be impacted. Dooming an entry only means that it will no
324 // longer be returned by FindActiveEntry (and it will also be destroyed once
325 // all consumers are finished with the entry).
326 ActiveEntriesMap::iterator it = active_entries_.find(key);
327 if (it == active_entries_.end()) {
328 return AsyncDoomEntry(key, callback);
329 }
330
331 ActiveEntry* entry = it->second;
332 active_entries_.erase(it);
333
334 // We keep track of doomed entries so that we can ensure that they are
335 // cleaned up properly when the cache is destroyed.
336 doomed_entries_.insert(entry);
337
338 entry->disk_entry->Doom();
339 entry->doomed = true;
340
341 DCHECK(entry->writer || !entry->readers.empty());
342 return OK;
343 }
344
AsyncDoomEntry(const std::string & key,CompletionCallback * callback)345 int HttpCache::AsyncDoomEntry(const std::string& key,
346 CompletionCallback* callback) {
347 DCHECK(callback);
348 WorkItem* item = new WorkItem(NULL, callback, WI_DOOM_ENTRY);
349 NewEntry* new_entry = GetNewEntry(key);
350 if (new_entry->writer) {
351 new_entry->pending_queue.push_back(item);
352 return ERR_IO_PENDING;
353 }
354
355 DCHECK(new_entry->pending_queue.empty());
356
357 new_entry->writer = item;
358 BackendCallback* my_callback = new BackendCallback(this, new_entry);
359
360 int rv = disk_cache_->DoomEntry(key, my_callback);
361 if (rv != ERR_IO_PENDING) {
362 item->ClearCallback();
363 my_callback->Run(rv);
364 }
365
366 return rv;
367 }
368
FinalizeDoomedEntry(ActiveEntry * entry)369 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
370 DCHECK(entry->doomed);
371 DCHECK(!entry->writer);
372 DCHECK(entry->readers.empty());
373 DCHECK(entry->pending_queue.empty());
374
375 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
376 DCHECK(it != doomed_entries_.end());
377 doomed_entries_.erase(it);
378
379 delete entry;
380 }
381
FindActiveEntry(const std::string & key)382 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
383 ActiveEntriesMap::const_iterator it = active_entries_.find(key);
384 return it != active_entries_.end() ? it->second : NULL;
385 }
386
GetNewEntry(const std::string & key)387 HttpCache::NewEntry* HttpCache::GetNewEntry(const std::string& key) {
388 DCHECK(!FindActiveEntry(key));
389
390 NewEntriesMap::const_iterator it = new_entries_.find(key);
391 if (it != new_entries_.end())
392 return it->second;
393
394 NewEntry* entry = new NewEntry();
395 new_entries_[key] = entry;
396 return entry;
397 }
398
DeleteNewEntry(NewEntry * entry)399 void HttpCache::DeleteNewEntry(NewEntry* entry) {
400 std::string key;
401 if (entry->disk_entry)
402 key = entry->disk_entry->GetKey();
403
404 if (!key.empty()) {
405 NewEntriesMap::iterator it = new_entries_.find(key);
406 DCHECK(it != new_entries_.end());
407 new_entries_.erase(it);
408 } else {
409 for (NewEntriesMap::iterator it = new_entries_.begin();
410 it != new_entries_.end(); ++it) {
411 if (it->second == entry) {
412 new_entries_.erase(it);
413 break;
414 }
415 }
416 }
417
418 delete entry;
419 }
420
OpenEntry(const std::string & key,ActiveEntry ** entry,CompletionCallback * callback)421 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
422 CompletionCallback* callback) {
423 ActiveEntry* active_entry = FindActiveEntry(key);
424 if (active_entry) {
425 *entry = active_entry;
426 return OK;
427 }
428
429 WorkItem* item = new WorkItem(entry, callback, WI_OPEN_ENTRY);
430 NewEntry* new_entry = GetNewEntry(key);
431 if (new_entry->writer) {
432 new_entry->pending_queue.push_back(item);
433 return ERR_IO_PENDING;
434 }
435
436 DCHECK(new_entry->pending_queue.empty());
437
438 new_entry->writer = item;
439 BackendCallback* my_callback = new BackendCallback(this, new_entry);
440
441 int rv = disk_cache_->OpenEntry(key, &(new_entry->disk_entry), my_callback);
442 if (rv != ERR_IO_PENDING) {
443 item->ClearCallback();
444 my_callback->Run(rv);
445 }
446
447 return rv;
448 }
449
CreateEntry(const std::string & key,ActiveEntry ** entry,CompletionCallback * callback)450 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
451 CompletionCallback* callback) {
452 DCHECK(!FindActiveEntry(key));
453
454 WorkItem* item = new WorkItem(entry, callback, WI_CREATE_ENTRY);
455 NewEntry* new_entry = GetNewEntry(key);
456 if (new_entry->writer) {
457 new_entry->pending_queue.push_back(item);
458 return ERR_IO_PENDING;
459 }
460
461 DCHECK(new_entry->pending_queue.empty());
462
463 new_entry->writer = item;
464 BackendCallback* my_callback = new BackendCallback(this, new_entry);
465
466 int rv = disk_cache_->CreateEntry(key, &(new_entry->disk_entry), my_callback);
467 if (rv != ERR_IO_PENDING) {
468 item->ClearCallback();
469 my_callback->Run(rv);
470 }
471
472 return rv;
473 }
474
DestroyEntry(ActiveEntry * entry)475 void HttpCache::DestroyEntry(ActiveEntry* entry) {
476 if (entry->doomed) {
477 FinalizeDoomedEntry(entry);
478 } else {
479 DeactivateEntry(entry);
480 }
481 }
482
ActivateEntry(const std::string & key,disk_cache::Entry * disk_entry)483 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
484 const std::string& key,
485 disk_cache::Entry* disk_entry) {
486 DCHECK(!FindActiveEntry(key));
487 ActiveEntry* entry = new ActiveEntry(disk_entry);
488 active_entries_[key] = entry;
489 return entry;
490 }
491
DeactivateEntry(ActiveEntry * entry)492 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
493 DCHECK(!entry->will_process_pending_queue);
494 DCHECK(!entry->doomed);
495 DCHECK(!entry->writer);
496 DCHECK(entry->readers.empty());
497 DCHECK(entry->pending_queue.empty());
498
499 std::string key = entry->disk_entry->GetKey();
500 if (key.empty())
501 return SlowDeactivateEntry(entry);
502
503 ActiveEntriesMap::iterator it = active_entries_.find(key);
504 DCHECK(it != active_entries_.end());
505 DCHECK(it->second == entry);
506
507 active_entries_.erase(it);
508 delete entry;
509 }
510
511 // We don't know this entry's key so we have to find it without it.
SlowDeactivateEntry(ActiveEntry * entry)512 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
513 for (ActiveEntriesMap::iterator it = active_entries_.begin();
514 it != active_entries_.end(); ++it) {
515 if (it->second == entry) {
516 active_entries_.erase(it);
517 delete entry;
518 break;
519 }
520 }
521 }
522
AddTransactionToEntry(ActiveEntry * entry,Transaction * trans)523 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
524 DCHECK(entry);
525
526 // We implement a basic reader/writer lock for the disk cache entry. If
527 // there is already a writer, then everyone has to wait for the writer to
528 // finish before they can access the cache entry. There can be multiple
529 // readers.
530 //
531 // NOTE: If the transaction can only write, then the entry should not be in
532 // use (since any existing entry should have already been doomed).
533
534 if (entry->writer || entry->will_process_pending_queue) {
535 entry->pending_queue.push_back(trans);
536 return ERR_IO_PENDING;
537 }
538
539 if (trans->mode() & Transaction::WRITE) {
540 // transaction needs exclusive access to the entry
541 if (entry->readers.empty()) {
542 entry->writer = trans;
543 } else {
544 entry->pending_queue.push_back(trans);
545 return ERR_IO_PENDING;
546 }
547 } else {
548 // transaction needs read access to the entry
549 entry->readers.push_back(trans);
550 }
551
552 // We do this before calling EntryAvailable to force any further calls to
553 // AddTransactionToEntry to add their transaction to the pending queue, which
554 // ensures FIFO ordering.
555 if (!entry->writer && !entry->pending_queue.empty())
556 ProcessPendingQueue(entry);
557
558 return trans->EntryAvailable(entry);
559 }
560
DoneWithEntry(ActiveEntry * entry,Transaction * trans,bool cancel)561 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
562 bool cancel) {
563 // If we already posted a task to move on to the next transaction and this was
564 // the writer, there is nothing to cancel.
565 if (entry->will_process_pending_queue && entry->readers.empty())
566 return;
567
568 if (entry->writer) {
569 DCHECK(trans == entry->writer);
570
571 // Assume there was a failure.
572 bool success = false;
573 if (cancel) {
574 DCHECK(entry->disk_entry);
575 // This is a successful operation in the sense that we want to keep the
576 // entry.
577 success = trans->AddTruncatedFlag();
578 }
579 DoneWritingToEntry(entry, success);
580 } else {
581 DoneReadingFromEntry(entry, trans);
582 }
583 }
584
DoneWritingToEntry(ActiveEntry * entry,bool success)585 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
586 DCHECK(entry->readers.empty());
587
588 entry->writer = NULL;
589
590 if (success) {
591 ProcessPendingQueue(entry);
592 } else {
593 DCHECK(!entry->will_process_pending_queue);
594
595 // We failed to create this entry.
596 TransactionList pending_queue;
597 pending_queue.swap(entry->pending_queue);
598
599 entry->disk_entry->Doom();
600 DestroyEntry(entry);
601
602 // We need to do something about these pending entries, which now need to
603 // be added to a new entry.
604 while (!pending_queue.empty()) {
605 pending_queue.front()->AddToEntry();
606 pending_queue.pop_front();
607 }
608 }
609 }
610
DoneReadingFromEntry(ActiveEntry * entry,Transaction * trans)611 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
612 DCHECK(!entry->writer);
613
614 TransactionList::iterator it =
615 std::find(entry->readers.begin(), entry->readers.end(), trans);
616 DCHECK(it != entry->readers.end());
617
618 entry->readers.erase(it);
619
620 ProcessPendingQueue(entry);
621 }
622
ConvertWriterToReader(ActiveEntry * entry)623 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
624 DCHECK(entry->writer);
625 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
626 DCHECK(entry->readers.empty());
627
628 Transaction* trans = entry->writer;
629
630 entry->writer = NULL;
631 entry->readers.push_back(trans);
632
633 ProcessPendingQueue(entry);
634 }
635
RemovePendingTransaction(Transaction * trans,CompletionCallback * cb)636 void HttpCache::RemovePendingTransaction(Transaction* trans,
637 CompletionCallback* cb) {
638 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
639 bool found = false;
640 if (i != active_entries_.end())
641 found = RemovePendingTransactionFromEntry(i->second, trans);
642
643 if (found)
644 return;
645
646 NewEntriesMap::const_iterator j = new_entries_.find(trans->key());
647 if (j != new_entries_.end())
648 found = RemovePendingCallbackFromNewEntry(j->second, cb);
649
650 ActiveEntriesSet::iterator k = doomed_entries_.begin();
651 for (; k != doomed_entries_.end() && !found; ++k)
652 found = RemovePendingTransactionFromEntry(*k, trans);
653
654 DCHECK(found) << "Pending transaction not found";
655 }
656
RemovePendingTransactionFromEntry(ActiveEntry * entry,Transaction * trans)657 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
658 Transaction* trans) {
659 TransactionList& pending_queue = entry->pending_queue;
660
661 TransactionList::iterator j =
662 find(pending_queue.begin(), pending_queue.end(), trans);
663 if (j == pending_queue.end())
664 return false;
665
666 pending_queue.erase(j);
667 return true;
668 }
669
RemovePendingCallbackFromNewEntry(NewEntry * entry,CompletionCallback * cb)670 bool HttpCache::RemovePendingCallbackFromNewEntry(NewEntry* entry,
671 CompletionCallback* cb) {
672 if (entry->writer->Matches(cb)) {
673 entry->writer->ClearCallback();
674 entry->writer->ClearEntry();
675 return true;
676 }
677 WorkItemList& pending_queue = entry->pending_queue;
678
679 WorkItemList::iterator it = pending_queue.begin();
680 for (; it != pending_queue.end(); ++it) {
681 if ((*it)->Matches(cb)) {
682 delete *it;
683 pending_queue.erase(it);
684 return true;
685 }
686 }
687 return false;
688 }
689
ProcessPendingQueue(ActiveEntry * entry)690 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
691 // Multiple readers may finish with an entry at once, so we want to batch up
692 // calls to OnProcessPendingQueue. This flag also tells us that we should
693 // not delete the entry before OnProcessPendingQueue runs.
694 if (entry->will_process_pending_queue)
695 return;
696 entry->will_process_pending_queue = true;
697
698 MessageLoop::current()->PostTask(FROM_HERE,
699 task_factory_.NewRunnableMethod(&HttpCache::OnProcessPendingQueue,
700 entry));
701 }
702
OnProcessPendingQueue(ActiveEntry * entry)703 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
704 entry->will_process_pending_queue = false;
705 DCHECK(!entry->writer);
706
707 // If no one is interested in this entry, then we can de-activate it.
708 if (entry->pending_queue.empty()) {
709 if (entry->readers.empty())
710 DestroyEntry(entry);
711 return;
712 }
713
714 // Promote next transaction from the pending queue.
715 Transaction* next = entry->pending_queue.front();
716 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
717 return; // Have to wait.
718
719 entry->pending_queue.erase(entry->pending_queue.begin());
720
721 AddTransactionToEntry(entry, next);
722 }
723
OnIOComplete(int result,NewEntry * new_entry)724 void HttpCache::OnIOComplete(int result, NewEntry* new_entry) {
725 scoped_ptr<WorkItem> item(new_entry->writer);
726 WorkItemOperation op = item->operation();
727 bool fail_requests = false;
728
729 ActiveEntry* entry = NULL;
730 std::string key;
731 if (result == OK) {
732 if (op == WI_DOOM_ENTRY) {
733 // Anything after a Doom has to be restarted.
734 fail_requests = true;
735 } else if (item->IsValid()) {
736 key = new_entry->disk_entry->GetKey();
737 entry = ActivateEntry(key, new_entry->disk_entry);
738 } else {
739 // The writer transaction is gone.
740 if (op == WI_CREATE_ENTRY)
741 new_entry->disk_entry->Doom();
742 new_entry->disk_entry->Close();
743 fail_requests = true;
744 }
745 }
746
747 // We are about to notify a bunch of transactions, and they may decide to
748 // re-issue a request (or send a different one). If we don't delete new_entry,
749 // the new request will be appended to the end of the list, and we'll see it
750 // again from this point before it has a chance to complete (and we'll be
751 // messing out the request order). The down side is that if for some reason
752 // notifying request A ends up cancelling request B (for the same key), we
753 // won't find request B anywhere (because it would be in a local variable
754 // here) and that's bad. If there is a chance for that to happen, we'll have
755 // to move the callback used to be a CancelableCallback. By the way, for this
756 // to happen the action (to cancel B) has to be synchronous to the
757 // notification for request A.
758 WorkItemList pending_items;
759 pending_items.swap(new_entry->pending_queue);
760 DeleteNewEntry(new_entry);
761
762 item->NotifyTransaction(result, entry);
763
764 while (!pending_items.empty()) {
765 item.reset(pending_items.front());
766 pending_items.pop_front();
767
768 if (item->operation() == WI_DOOM_ENTRY) {
769 // A queued doom request is always a race.
770 fail_requests = true;
771 } else if (result == OK) {
772 entry = FindActiveEntry(key);
773 if (!entry)
774 fail_requests = true;
775 }
776
777 if (fail_requests) {
778 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
779 continue;
780 }
781
782 if (item->operation() == WI_CREATE_ENTRY) {
783 if (result == OK) {
784 // A second Create request, but the first request succeded.
785 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
786 } else {
787 if (op != WI_CREATE_ENTRY) {
788 // Failed Open followed by a Create.
789 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
790 fail_requests = true;
791 } else {
792 item->NotifyTransaction(result, entry);
793 }
794 }
795 } else {
796 if (op == WI_CREATE_ENTRY && result != OK) {
797 // Failed Create followed by an Open.
798 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
799 fail_requests = true;
800 } else {
801 item->NotifyTransaction(result, entry);
802 }
803 }
804 }
805 }
806
CloseCurrentConnections()807 void HttpCache::CloseCurrentConnections() {
808 net::HttpNetworkLayer* network =
809 static_cast<net::HttpNetworkLayer*>(network_layer_.get());
810 HttpNetworkSession* session = network->GetSession();
811 if (session) {
812 session->tcp_socket_pool()->CloseIdleSockets();
813 if (session->flip_session_pool())
814 session->flip_session_pool()->CloseAllSessions();
815 session->ReplaceTCPSocketPool();
816 }
817 }
818
819 //-----------------------------------------------------------------------------
820
821 } // namespace net
822