summaryrefslogtreecommitdiffstats
path: root/net/http
diff options
context:
space:
mode:
authorrvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-07 22:31:20 +0000
committerrvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-07 22:31:20 +0000
commit46773166fdfb7bcfecad9bb84050ba0745955708 (patch)
treef782df29fb31ccb82d1b219967d71a7def3d9e3b /net/http
parent1f4da86c4928ccdc3c6f626d52a109b2f5da21d1 (diff)
downloadchromium_src-46773166fdfb7bcfecad9bb84050ba0745955708.zip
chromium_src-46773166fdfb7bcfecad9bb84050ba0745955708.tar.gz
chromium_src-46773166fdfb7bcfecad9bb84050ba0745955708.tar.bz2
Http Cache: Handle the asynchronous instantiation of the
backend. We queue all trasnactions that reach Start before we are done creating the disk cache. BUG=26729 TEST=unittests. Review URL: http://codereview.chromium.org/2002002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46745 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http')
-rw-r--r--net/http/http_cache.cc345
-rw-r--r--net/http/http_cache.h53
-rw-r--r--net/http/http_cache_transaction.cc113
-rw-r--r--net/http/http_cache_transaction.h12
-rw-r--r--net/http/http_cache_unittest.cc292
5 files changed, 624 insertions, 191 deletions
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index fc7905b..08b9aa6 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -17,6 +17,7 @@
#include "base/message_loop.h"
#include "base/pickle.h"
#include "base/ref_counted.h"
+#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
@@ -49,13 +50,14 @@ HttpCache::ActiveEntry::~ActiveEntry() {
//-----------------------------------------------------------------------------
// This structure keeps track of work items that are attempting to create or
-// open cache entries.
-struct HttpCache::NewEntry {
- NewEntry() : disk_entry(NULL), writer(NULL) {}
- ~NewEntry() {}
+// open cache entries or the backend itself.
+struct HttpCache::PendingOp {
+ PendingOp() : disk_entry(NULL), writer(NULL), callback(NULL) {}
+ ~PendingOp() {}
disk_cache::Entry* disk_entry;
WorkItem* writer;
+ CompletionCallback* callback; // BackendCallback.
WorkItemList pending_queue;
};
@@ -63,19 +65,24 @@ struct HttpCache::NewEntry {
// The type of operation represented by a work item.
enum WorkItemOperation {
+ WI_CREATE_BACKEND,
WI_OPEN_ENTRY,
WI_CREATE_ENTRY,
WI_DOOM_ENTRY
};
-// A work item encapsulates a single request for cache entry with all the
+// A work item encapsulates a single request to the backend with all the
// information needed to complete that request.
class HttpCache::WorkItem {
public:
- WorkItem(ActiveEntry** entry, Transaction* trans, WorkItemOperation operation)
- : entry_(entry), trans_(trans), operation_(operation) {}
+ WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
+ : operation_(operation), trans_(trans), entry_(entry), callback_(NULL),
+ backend_(NULL) {}
+ WorkItem(WorkItemOperation operation, Transaction* trans,
+ CompletionCallback* cb, disk_cache::Backend** backend)
+ : operation_(operation), trans_(trans), entry_(NULL), callback_(cb),
+ backend_(backend) {}
~WorkItem() {}
- WorkItemOperation operation() { return operation_; }
// Calls back the transaction with the result of the operation.
void NotifyTransaction(int result, ActiveEntry* entry) {
@@ -85,15 +92,27 @@ class HttpCache::WorkItem {
trans_->io_callback()->Run(result);
}
+ // Notifies the caller about the operation completion.
+ void DoCallback(int result, disk_cache::Backend* backend) {
+ if (backend_)
+ *backend_ = backend;
+ if (callback_)
+ callback_->Run(result);
+ }
+
+ WorkItemOperation operation() { return operation_; }
void ClearTransaction() { trans_ = NULL; }
void ClearEntry() { entry_ = NULL; }
+ void ClearCallback() { callback_ = NULL; }
bool Matches(Transaction* trans) const { return trans == trans_; }
- bool IsValid() const { return trans_ || entry_; }
+ bool IsValid() const { return trans_ || entry_ || callback_; }
private:
- ActiveEntry** entry_;
- Transaction* trans_;
WorkItemOperation operation_;
+ Transaction* trans_;
+ ActiveEntry** entry_;
+ CompletionCallback* callback_; // User callback.
+ disk_cache::Backend** backend_;
};
//-----------------------------------------------------------------------------
@@ -102,18 +121,18 @@ class HttpCache::WorkItem {
// pass multiple arguments to the completion routine.
class HttpCache::BackendCallback : public CallbackRunner<Tuple1<int> > {
public:
- BackendCallback(HttpCache* cache, NewEntry* entry)
- : cache_(cache), entry_(entry) {}
+ BackendCallback(HttpCache* cache, PendingOp* pending_op)
+ : cache_(cache), pending_op_(pending_op) {}
~BackendCallback() {}
virtual void RunWithParams(const Tuple1<int>& params) {
- cache_->OnIOComplete(params.a, entry_);
+ cache_->OnIOComplete(params.a, pending_op_);
delete this;
}
private:
HttpCache* cache_;
- NewEntry* entry_;
+ PendingOp* pending_op_;
DISALLOW_COPY_AND_ASSIGN(BackendCallback);
};
@@ -205,6 +224,8 @@ HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier,
int cache_size)
: disk_cache_dir_(cache_dir),
cache_thread_(cache_thread),
+ temp_backend_(NULL),
+ building_backend_(false),
mode_(NORMAL),
type_(DISK_CACHE),
network_layer_(HttpNetworkLayer::CreateFactory(
@@ -212,7 +233,8 @@ HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier,
ssl_config_service, http_auth_handler_factory)),
ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
enable_range_support_(true),
- cache_size_(cache_size) {
+ cache_size_(cache_size),
+ create_backend_fn_(NULL) {
}
HttpCache::HttpCache(HttpNetworkSession* session,
@@ -221,12 +243,15 @@ HttpCache::HttpCache(HttpNetworkSession* session,
int cache_size)
: disk_cache_dir_(cache_dir),
cache_thread_(cache_thread),
+ temp_backend_(NULL),
+ building_backend_(false),
mode_(NORMAL),
type_(DISK_CACHE),
network_layer_(HttpNetworkLayer::CreateFactory(session)),
ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
enable_range_support_(true),
- cache_size_(cache_size) {
+ cache_size_(cache_size),
+ create_backend_fn_(NULL) {
}
HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier,
@@ -235,25 +260,33 @@ HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier,
SSLConfigService* ssl_config_service,
HttpAuthHandlerFactory* http_auth_handler_factory,
int cache_size)
- : mode_(NORMAL),
+ : cache_thread_(NULL),
+ temp_backend_(NULL),
+ building_backend_(false),
+ mode_(NORMAL),
type_(MEMORY_CACHE),
network_layer_(HttpNetworkLayer::CreateFactory(
network_change_notifier, host_resolver, proxy_service,
ssl_config_service, http_auth_handler_factory)),
ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
enable_range_support_(true),
- cache_size_(cache_size) {
+ cache_size_(cache_size),
+ create_backend_fn_(NULL) {
}
HttpCache::HttpCache(HttpTransactionFactory* network_layer,
disk_cache::Backend* disk_cache)
- : mode_(NORMAL),
+ : cache_thread_(NULL),
+ temp_backend_(NULL),
+ building_backend_(false),
+ mode_(NORMAL),
type_(DISK_CACHE),
network_layer_(network_layer),
disk_cache_(disk_cache),
ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
enable_range_support_(true),
- cache_size_(0) {
+ cache_size_(0),
+ create_backend_fn_(NULL) {
}
HttpCache::~HttpCache() {
@@ -270,9 +303,19 @@ HttpCache::~HttpCache() {
DeactivateEntry(entry);
}
- ActiveEntriesSet::iterator it = doomed_entries_.begin();
- for (; it != doomed_entries_.end(); ++it)
- delete *it;
+ STLDeleteElements(&doomed_entries_);
+
+ PendingOpsMap::iterator pending_it = pending_ops_.begin();
+ for (; pending_it != pending_ops_.end(); ++pending_it) {
+ // We are not notifying the transactions about the cache going away, even
+ // though they are waiting for a callback that will never fire.
+ PendingOp* pending_op = pending_it->second;
+ delete pending_op->writer;
+ delete pending_op->callback;
+
+ STLDeleteElements(&pending_op->pending_queue);
+ delete pending_op;
+ }
}
disk_cache::Backend* HttpCache::GetBackend() {
@@ -296,13 +339,21 @@ disk_cache::Backend* HttpCache::GetBackend() {
int HttpCache::GetBackend(disk_cache::Backend** backend,
CompletionCallback* callback) {
DCHECK(callback != NULL);
- *backend = GetBackend();
- return OK;
+
+ if (disk_cache_.get()) {
+ *backend = disk_cache_.get();
+ return OK;
+ }
+
+ DCHECK_GE(cache_size_, 0);
+ return CreateBackend(backend, callback);
}
int HttpCache::CreateTransaction(scoped_ptr<HttpTransaction>* trans) {
// Do lazy initialization of disk cache if needed.
- GetBackend();
+ if (!disk_cache_.get())
+ CreateBackend(NULL, NULL); // We don't care about the result.
+
trans->reset(new HttpCache::Transaction(this, enable_range_support_));
return OK;
}
@@ -392,6 +443,68 @@ void HttpCache::CloseCurrentConnections() {
//-----------------------------------------------------------------------------
+int HttpCache::CreateBackend(disk_cache::Backend** backend,
+ CompletionCallback* callback) {
+ // We may end up with no folder name and no cache if the initialization
+ // of the disk cache fails.
+ if (type_ != MEMORY_CACHE && disk_cache_dir_.empty())
+ return ERR_FAILED;
+
+ DCHECK_GE(cache_size_, 0);
+ building_backend_ = true;
+
+ scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
+ backend));
+
+ // This is the only operation that we can do that is not related to any given
+ // entry, so we use an empty key for it.
+ PendingOp* pending_op = GetPendingOp("");
+ if (pending_op->writer) {
+ if (callback)
+ pending_op->pending_queue.push_back(item.release());
+ return ERR_IO_PENDING;
+ }
+
+ DCHECK(pending_op->pending_queue.empty());
+
+ pending_op->writer = item.release();
+ BackendCallback* my_callback = new BackendCallback(this, pending_op);
+ pending_op->callback = my_callback;
+
+ // See if this is a unit test. TODO(rvargas): Cleanup this after removing the
+ // overloaded method.
+ int rv;
+ if (create_backend_fn_) {
+ rv = create_backend_fn_(type_, disk_cache_dir_, cache_size_, true,
+ cache_thread_, &temp_backend_, my_callback);
+ } else {
+ rv = disk_cache::CreateCacheBackend(type_, disk_cache_dir_, cache_size_,
+ true, cache_thread_, &temp_backend_,
+ my_callback);
+ }
+
+ if (rv != ERR_IO_PENDING) {
+ pending_op->writer->ClearCallback();
+ my_callback->Run(rv);
+ }
+
+ return rv;
+}
+
+int HttpCache::GetBackendForTransaction(Transaction* trans) {
+ if (disk_cache_.get())
+ return OK;
+
+ if (!building_backend_)
+ return ERR_FAILED;
+
+ WorkItem* item = new WorkItem(WI_CREATE_BACKEND, trans, NULL, NULL);
+ PendingOp* pending_op = GetPendingOp("");
+ DCHECK(pending_op->writer);
+ pending_op->pending_queue.push_back(item);
+ return ERR_IO_PENDING;
+}
+
// Generate a key that can be used inside the cache.
std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
// Strip out the reference, username, and password sections of the URL.
@@ -457,17 +570,18 @@ int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
DCHECK(trans);
- WorkItem* item = new WorkItem(NULL, trans, WI_DOOM_ENTRY);
- NewEntry* new_entry = GetNewEntry(key);
- if (new_entry->writer) {
- new_entry->pending_queue.push_back(item);
+ WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
+ PendingOp* pending_op = GetPendingOp(key);
+ if (pending_op->writer) {
+ pending_op->pending_queue.push_back(item);
return ERR_IO_PENDING;
}
- DCHECK(new_entry->pending_queue.empty());
+ DCHECK(pending_op->pending_queue.empty());
- new_entry->writer = item;
- BackendCallback* my_callback = new BackendCallback(this, new_entry);
+ pending_op->writer = item;
+ BackendCallback* my_callback = new BackendCallback(this, pending_op);
+ pending_op->callback = my_callback;
int rv = disk_cache_->DoomEntry(key, my_callback);
if (rv != ERR_IO_PENDING) {
@@ -536,38 +650,39 @@ void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
}
}
-HttpCache::NewEntry* HttpCache::GetNewEntry(const std::string& key) {
+HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
DCHECK(!FindActiveEntry(key));
- NewEntriesMap::const_iterator it = new_entries_.find(key);
- if (it != new_entries_.end())
+ PendingOpsMap::const_iterator it = pending_ops_.find(key);
+ if (it != pending_ops_.end())
return it->second;
- NewEntry* entry = new NewEntry();
- new_entries_[key] = entry;
- return entry;
+ PendingOp* operation = new PendingOp();
+ pending_ops_[key] = operation;
+ return operation;
}
-void HttpCache::DeleteNewEntry(NewEntry* entry) {
+void HttpCache::DeletePendingOp(PendingOp* pending_op) {
std::string key;
- if (entry->disk_entry)
- key = entry->disk_entry->GetKey();
+ if (pending_op->disk_entry)
+ key = pending_op->disk_entry->GetKey();
if (!key.empty()) {
- NewEntriesMap::iterator it = new_entries_.find(key);
- DCHECK(it != new_entries_.end());
- new_entries_.erase(it);
+ PendingOpsMap::iterator it = pending_ops_.find(key);
+ DCHECK(it != pending_ops_.end());
+ pending_ops_.erase(it);
} else {
- for (NewEntriesMap::iterator it = new_entries_.begin();
- it != new_entries_.end(); ++it) {
- if (it->second == entry) {
- new_entries_.erase(it);
+ for (PendingOpsMap::iterator it = pending_ops_.begin();
+ it != pending_ops_.end(); ++it) {
+ if (it->second == pending_op) {
+ pending_ops_.erase(it);
break;
}
}
}
+ DCHECK(pending_op->pending_queue.empty());
- delete entry;
+ delete pending_op;
}
int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
@@ -578,19 +693,20 @@ int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
return OK;
}
- WorkItem* item = new WorkItem(entry, trans, WI_OPEN_ENTRY);
- NewEntry* new_entry = GetNewEntry(key);
- if (new_entry->writer) {
- new_entry->pending_queue.push_back(item);
+ WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
+ PendingOp* pending_op = GetPendingOp(key);
+ if (pending_op->writer) {
+ pending_op->pending_queue.push_back(item);
return ERR_IO_PENDING;
}
- DCHECK(new_entry->pending_queue.empty());
+ DCHECK(pending_op->pending_queue.empty());
- new_entry->writer = item;
- BackendCallback* my_callback = new BackendCallback(this, new_entry);
+ pending_op->writer = item;
+ BackendCallback* my_callback = new BackendCallback(this, pending_op);
+ pending_op->callback = my_callback;
- int rv = disk_cache_->OpenEntry(key, &(new_entry->disk_entry), my_callback);
+ int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry), my_callback);
if (rv != ERR_IO_PENDING) {
item->ClearTransaction();
my_callback->Run(rv);
@@ -603,19 +719,21 @@ int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
Transaction* trans) {
DCHECK(!FindActiveEntry(key));
- WorkItem* item = new WorkItem(entry, trans, WI_CREATE_ENTRY);
- NewEntry* new_entry = GetNewEntry(key);
- if (new_entry->writer) {
- new_entry->pending_queue.push_back(item);
+ WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
+ PendingOp* pending_op = GetPendingOp(key);
+ if (pending_op->writer) {
+ pending_op->pending_queue.push_back(item);
return ERR_IO_PENDING;
}
- DCHECK(new_entry->pending_queue.empty());
+ DCHECK(pending_op->pending_queue.empty());
- new_entry->writer = item;
- BackendCallback* my_callback = new BackendCallback(this, new_entry);
+ pending_op->writer = item;
+ BackendCallback* my_callback = new BackendCallback(this, pending_op);
+ pending_op->callback = my_callback;
- int rv = disk_cache_->CreateEntry(key, &(new_entry->disk_entry), my_callback);
+ int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
+ my_callback);
if (rv != ERR_IO_PENDING) {
item->ClearTransaction();
my_callback->Run(rv);
@@ -755,9 +873,21 @@ void HttpCache::RemovePendingTransaction(Transaction* trans) {
if (found)
return;
- NewEntriesMap::const_iterator j = new_entries_.find(trans->key());
- if (j != new_entries_.end())
- found = RemovePendingTransactionFromNewEntry(j->second, trans);
+ if (building_backend_) {
+ PendingOpsMap::const_iterator j = pending_ops_.find("");
+ if (j != pending_ops_.end())
+ found = RemovePendingTransactionFromPendingOp(j->second, trans);
+
+ if (found)
+ return;
+ }
+
+ PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
+ if (j != pending_ops_.end())
+ found = RemovePendingTransactionFromPendingOp(j->second, trans);
+
+ if (found)
+ return;
ActiveEntriesSet::iterator k = doomed_entries_.begin();
for (; k != doomed_entries_.end() && !found; ++k)
@@ -779,14 +909,14 @@ bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
return true;
}
-bool HttpCache::RemovePendingTransactionFromNewEntry(NewEntry* entry,
- Transaction* trans) {
- if (entry->writer->Matches(trans)) {
- entry->writer->ClearTransaction();
- entry->writer->ClearEntry();
+bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
+ Transaction* trans) {
+ if (pending_op->writer->Matches(trans)) {
+ pending_op->writer->ClearTransaction();
+ pending_op->writer->ClearEntry();
return true;
}
- WorkItemList& pending_queue = entry->pending_queue;
+ WorkItemList& pending_queue = pending_op->pending_queue;
WorkItemList::iterator it = pending_queue.begin();
for (; it != pending_queue.end(); ++it) {
@@ -837,9 +967,14 @@ void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
}
}
-void HttpCache::OnIOComplete(int result, NewEntry* new_entry) {
- scoped_ptr<WorkItem> item(new_entry->writer);
- WorkItemOperation op = item->operation();
+void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
+ WorkItemOperation op = pending_op->writer->operation();
+
+ // Completing the creation of the backend is simpler than the other cases.
+ if (op == WI_CREATE_BACKEND)
+ return OnBackendCreated(result, pending_op);
+
+ scoped_ptr<WorkItem> item(pending_op->writer);
bool fail_requests = false;
ActiveEntry* entry = NULL;
@@ -849,31 +984,31 @@ void HttpCache::OnIOComplete(int result, NewEntry* new_entry) {
// Anything after a Doom has to be restarted.
fail_requests = true;
} else if (item->IsValid()) {
- key = new_entry->disk_entry->GetKey();
- entry = ActivateEntry(key, new_entry->disk_entry);
+ key = pending_op->disk_entry->GetKey();
+ entry = ActivateEntry(key, pending_op->disk_entry);
} else {
// The writer transaction is gone.
if (op == WI_CREATE_ENTRY)
- new_entry->disk_entry->Doom();
- new_entry->disk_entry->Close();
+ pending_op->disk_entry->Doom();
+ pending_op->disk_entry->Close();
fail_requests = true;
}
}
// We are about to notify a bunch of transactions, and they may decide to
- // re-issue a request (or send a different one). If we don't delete new_entry,
- // the new request will be appended to the end of the list, and we'll see it
- // again from this point before it has a chance to complete (and we'll be
- // messing out the request order). The down side is that if for some reason
- // notifying request A ends up cancelling request B (for the same key), we
- // won't find request B anywhere (because it would be in a local variable
+ // re-issue a request (or send a different one). If we don't delete
+ // pending_op, the new request will be appended to the end of the list, and
+ // we'll see it again from this point before it has a chance to complete (and
+ // we'll be messing out the request order). The down side is that if for some
+ // reason notifying request A ends up cancelling request B (for the same key),
+ // we won't find request B anywhere (because it would be in a local variable
// here) and that's bad. If there is a chance for that to happen, we'll have
// to move the callback used to be a CancelableCallback. By the way, for this
// to happen the action (to cancel B) has to be synchronous to the
// notification for request A.
WorkItemList pending_items;
- pending_items.swap(new_entry->pending_queue);
- DeleteNewEntry(new_entry);
+ pending_items.swap(pending_op->pending_queue);
+ DeletePendingOp(pending_op);
item->NotifyTransaction(result, entry);
@@ -920,4 +1055,32 @@ void HttpCache::OnIOComplete(int result, NewEntry* new_entry) {
}
}
+void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
+ scoped_ptr<WorkItem> item(pending_op->writer);
+ WorkItemOperation op = item->operation();
+ DCHECK_EQ(WI_CREATE_BACKEND, op);
+
+ if (type_ != MEMORY_CACHE)
+ disk_cache_dir_ = FilePath(); // Reclaim memory.
+
+ if (result == OK)
+ disk_cache_.reset(temp_backend_);
+
+ item->DoCallback(result, temp_backend_);
+
+ // Notify and all callers and delete all pending work items.
+ while (!pending_op->pending_queue.empty()) {
+ scoped_ptr<WorkItem> pending_item(pending_op->pending_queue.front());
+ pending_op->pending_queue.pop_front();
+ DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
+
+ // This could be an external caller or a transaction waiting on Start().
+ pending_item->DoCallback(result, temp_backend_);
+ pending_item->NotifyTransaction(result, NULL);
+ }
+
+ DeletePendingOp(pending_op);
+ building_backend_ = false;
+}
+
} // namespace net
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index 15561fb..472c272 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -26,6 +26,7 @@
#include "net/base/cache_type.h"
#include "net/base/completion_callback.h"
#include "net/http/http_transaction_factory.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
class GURL;
class MessageLoop;
@@ -184,6 +185,8 @@ class HttpCache : public HttpTransactionFactory,
friend class ::ViewCacheHelper;
private:
+ FRIEND_TEST(HttpCacheTest, SimpleGET_WaitForBackend);
+ FRIEND_TEST(HttpCacheTest, SimpleGET_WaitForBackend_CancelCreate);
// Types --------------------------------------------------------------------
@@ -192,7 +195,7 @@ class HttpCache : public HttpTransactionFactory,
class Transaction;
class WorkItem;
friend class Transaction;
- struct NewEntry; // Info for an entry under construction.
+ struct PendingOp; // Info for an entry under construction.
typedef std::list<Transaction*> TransactionList;
typedef std::list<WorkItem*> WorkItemList;
@@ -210,11 +213,27 @@ class HttpCache : public HttpTransactionFactory,
};
typedef base::hash_map<std::string, ActiveEntry*> ActiveEntriesMap;
- typedef base::hash_map<std::string, NewEntry*> NewEntriesMap;
+ typedef base::hash_map<std::string, PendingOp*> PendingOpsMap;
typedef std::set<ActiveEntry*> ActiveEntriesSet;
+ typedef int (*CreateCacheBackendFn)(CacheType, const FilePath&, int,
+ bool, MessageLoop*, disk_cache::Backend**,
+ CompletionCallback*);
+
// Methods ------------------------------------------------------------------
+ // Creates the |backend| object and notifies the |callback| when the operation
+ // completes. Returns an error code.
+ int CreateBackend(disk_cache::Backend** backend,
+ CompletionCallback* callback);
+
+ // Makes sure that the backend creation is complete before allowing the
+ // provided transaction to use the object. Returns an error code. |trans|
+ // will be notified via its IO callback if this method returns ERR_IO_PENDING.
+ // The transaction is free to use the backend directly at any time after
+ // receiving the notification.
+ int GetBackendForTransaction(Transaction* trans);
+
// Generates the cache key for this request.
std::string GenerateCacheKey(const HttpRequestInfo*);
@@ -246,12 +265,12 @@ class HttpCache : public HttpTransactionFactory,
// Deletes an ActiveEntry using an exhaustive search.
void SlowDeactivateEntry(ActiveEntry* entry);
- // Returns the NewEntry for the desired |key|. If an entry is not under
- // construction already, a new NewEntry structure is created.
- NewEntry* GetNewEntry(const std::string& key);
+ // Returns the PendingOp for the desired |key|. If an entry is not under
+ // construction already, a new PendingOp structure is created.
+ PendingOp* GetPendingOp(const std::string& key);
- // Deletes a NewEntry.
- void DeleteNewEntry(NewEntry* entry);
+ // Deletes a PendingOp.
+ void DeletePendingOp(PendingOp* pending_op);
// Opens the disk cache entry associated with |key|, returning an ActiveEntry
// in |*entry|. |trans| will be notified via its IO callback if this method
@@ -292,16 +311,16 @@ class HttpCache : public HttpTransactionFactory,
void ConvertWriterToReader(ActiveEntry* entry);
// Removes the transaction |trans|, from the pending list of an entry
- // (NewEntry, active or doomed entry).
+ // (PendingOp, active or doomed entry).
void RemovePendingTransaction(Transaction* trans);
// Removes the transaction |trans|, from the pending list of |entry|.
bool RemovePendingTransactionFromEntry(ActiveEntry* entry,
Transaction* trans);
- // Removes the transaction |trans|, from the pending list of |entry|.
- bool RemovePendingTransactionFromNewEntry(NewEntry* entry,
- Transaction* trans);
+ // Removes the transaction |trans|, from the pending list of |pending_op|.
+ bool RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
+ Transaction* trans);
// Resumes processing the pending list of |entry|.
void ProcessPendingQueue(ActiveEntry* entry);
@@ -313,7 +332,10 @@ class HttpCache : public HttpTransactionFactory,
// Callbacks ----------------------------------------------------------------
// Processes BackendCallback notifications.
- void OnIOComplete(int result, NewEntry* entry);
+ void OnIOComplete(int result, PendingOp* entry);
+
+ // Processes the backend creation notification.
+ void OnBackendCreated(int result, PendingOp* pending_op);
// Variables ----------------------------------------------------------------
@@ -321,6 +343,8 @@ class HttpCache : public HttpTransactionFactory,
// Used when lazily constructing the disk_cache_.
FilePath disk_cache_dir_;
MessageLoop* cache_thread_;
+ disk_cache::Backend* temp_backend_;
+ bool building_backend_;
Mode mode_;
CacheType type_;
@@ -335,7 +359,7 @@ class HttpCache : public HttpTransactionFactory,
ActiveEntriesSet doomed_entries_;
// The set of entries "under construction".
- NewEntriesMap new_entries_;
+ PendingOpsMap pending_ops_;
ScopedRunnableMethodFactory<HttpCache> task_factory_;
@@ -345,6 +369,9 @@ class HttpCache : public HttpTransactionFactory,
typedef base::hash_map<std::string, int> PlaybackCacheMap;
scoped_ptr<PlaybackCacheMap> playback_cache_map_;
+ // Used for unit tests.
+ CreateCacheBackendFn create_backend_fn_;
+
DISALLOW_COPY_AND_ASSIGN(HttpCache);
};
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index 8beb825..ffb5748 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -172,44 +172,9 @@ int HttpCache::Transaction::Start(const HttpRequestInfo* request,
SetRequest(net_log, request);
- int rv;
-
- if (!ShouldPassThrough()) {
- cache_key_ = cache_->GenerateCacheKey(request);
-
- // Requested cache access mode.
- if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
- mode_ = READ;
- } else if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
- mode_ = WRITE;
- } else {
- mode_ = READ_WRITE;
- }
-
- // Downgrade to UPDATE if the request has been externally conditionalized.
- if (external_validation_.initialized) {
- if (mode_ & WRITE) {
- // Strip off the READ_DATA bit (and maybe add back a READ_META bit
- // in case READ was off).
- mode_ = UPDATE;
- } else {
- mode_ = NONE;
- }
- }
- }
-
- // If must use cache, then we must fail. This can happen for back/forward
- // navigations to a page generated via a form post.
- if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE)
- return ERR_CACHE_MISS;
-
- if (mode_ == NONE) {
- if (partial_.get())
- partial_->RestoreHeaders(&custom_request_->extra_headers);
- rv = BeginNetworkRequest();
- } else {
- rv = AddToEntry();
- }
+ // We have to wait until the backend is initialized so we start the SM.
+ next_state_ = STATE_GET_BACKEND;
+ int rv = DoLoop(OK);
// Setting this here allows us to check for the existance of a callback_ to
// determine if we are still inside Start.
@@ -428,6 +393,13 @@ int HttpCache::Transaction::DoLoop(int result) {
State state = next_state_;
next_state_ = STATE_NONE;
switch (state) {
+ case STATE_GET_BACKEND:
+ DCHECK_EQ(OK, rv);
+ rv = DoGetBackend();
+ break;
+ case STATE_GET_BACKEND_COMPLETE:
+ rv = DoGetBackendComplete(rv);
+ break;
case STATE_SEND_REQUEST:
DCHECK_EQ(OK, rv);
rv = DoSendRequest();
@@ -562,6 +534,58 @@ int HttpCache::Transaction::DoLoop(int result) {
return rv;
}
+int HttpCache::Transaction::DoGetBackend() {
+ cache_pending_ = true;
+ next_state_ = STATE_GET_BACKEND_COMPLETE;
+ net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WAITING, NULL);
+ return cache_->GetBackendForTransaction(this);
+}
+
+int HttpCache::Transaction::DoGetBackendComplete(int result) {
+ DCHECK(result == OK || result == ERR_FAILED);
+ net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_WAITING, NULL);
+ cache_pending_ = false;
+
+ if (!ShouldPassThrough()) {
+ cache_key_ = cache_->GenerateCacheKey(request_);
+
+ // Requested cache access mode.
+ if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
+ mode_ = READ;
+ } else if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
+ mode_ = WRITE;
+ } else {
+ mode_ = READ_WRITE;
+ }
+
+ // Downgrade to UPDATE if the request has been externally conditionalized.
+ if (external_validation_.initialized) {
+ if (mode_ & WRITE) {
+ // Strip off the READ_DATA bit (and maybe add back a READ_META bit
+ // in case READ was off).
+ mode_ = UPDATE;
+ } else {
+ mode_ = NONE;
+ }
+ }
+ }
+
+ // If must use cache, then we must fail. This can happen for back/forward
+ // navigations to a page generated via a form post.
+ if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE)
+ return ERR_CACHE_MISS;
+
+ if (mode_ == NONE) {
+ if (partial_.get())
+ partial_->RestoreHeaders(&custom_request_->extra_headers);
+ next_state_ = STATE_SEND_REQUEST;
+ } else {
+ next_state_ = STATE_INIT_ENTRY;
+ }
+
+ return OK;
+}
+
int HttpCache::Transaction::DoSendRequest() {
DCHECK(mode_ & WRITE || mode_ == NONE);
DCHECK(!network_trans_.get());
@@ -902,7 +926,7 @@ int HttpCache::Transaction::DoOverwriteCachedResponse() {
response_ = *new_response_;
target_state_ = STATE_TRUNCATE_CACHED_DATA;
next_state_ = truncated_ ? STATE_CACHE_WRITE_TRUNCATED_RESPONSE :
- STATE_CACHE_WRITE_RESPONSE;
+ STATE_CACHE_WRITE_RESPONSE;
return OK;
}
@@ -1268,12 +1292,6 @@ bool HttpCache::Transaction::ShouldPassThrough() {
return true;
}
-int HttpCache::Transaction::AddToEntry() {
- next_state_ = STATE_INIT_ENTRY;
- cache_pending_ = false;
- return DoLoop(OK);
-}
-
int HttpCache::Transaction::BeginCacheRead() {
// We don't support any combination of LOAD_ONLY_FROM_CACHE and byte ranges.
if (response_.headers->response_code() == 206 || partial_.get()) {
@@ -1388,11 +1406,6 @@ int HttpCache::Transaction::BeginExternallyConditionalizedRequest() {
return OK;
}
-int HttpCache::Transaction::BeginNetworkRequest() {
- next_state_ = STATE_SEND_REQUEST;
- return DoLoop(OK);
-}
-
int HttpCache::Transaction::RestartNetworkRequest() {
DCHECK(mode_ & WRITE || mode_ == NONE);
DCHECK(network_trans_.get());
diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h
index 9d13b11..4d16a25 100644
--- a/net/http/http_cache_transaction.h
+++ b/net/http/http_cache_transaction.h
@@ -111,6 +111,8 @@ class HttpCache::Transaction : public HttpTransaction {
enum State {
STATE_NONE,
+ STATE_GET_BACKEND,
+ STATE_GET_BACKEND_COMPLETE,
STATE_SEND_REQUEST,
STATE_SEND_REQUEST_COMPLETE,
STATE_SUCCESSFUL_SEND_REQUEST,
@@ -160,6 +162,8 @@ class HttpCache::Transaction : public HttpTransaction {
// Each of these methods corresponds to a State value. If there is an
// argument, the value corresponds to the return of the previous state or
// corresponding callback.
+ int DoGetBackend();
+ int DoGetBackendComplete(int result);
int DoSendRequest();
int DoSendRequestComplete(int result);
int DoSuccessfulSendRequest();
@@ -202,9 +206,6 @@ class HttpCache::Transaction : public HttpTransaction {
// layer (skipping the cache entirely).
bool ShouldPassThrough();
- // Associates this transaction with a cache entry.
- int AddToEntry();
-
// Called to begin reading from the cache. Returns network error code.
int BeginCacheRead();
@@ -225,9 +226,6 @@ class HttpCache::Transaction : public HttpTransaction {
// Returns a network error code.
int BeginExternallyConditionalizedRequest();
- // Called to begin a network transaction. Returns network error code.
- int BeginNetworkRequest();
-
// Called to restart a network transaction after an error. Returns network
// error code.
int RestartNetworkRequest();
@@ -332,7 +330,7 @@ class HttpCache::Transaction : public HttpTransaction {
CompletionCallbackImpl<Transaction> io_callback_;
scoped_refptr<CancelableCompletionCallback<Transaction> > cache_callback_;
scoped_refptr<CancelableCompletionCallback<Transaction> >
- write_headers_callback_;
+ write_headers_callback_;
};
} // namespace net
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 32b3343..cf12dfc 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -570,6 +570,13 @@ class MockHttpCache {
net::HttpCache http_cache_;
};
+// This version of the disk cache doesn't invoke CreateEntry callbacks.
+class MockDiskCacheNoCB : public MockDiskCache {
+ virtual int CreateEntry(const std::string& key, disk_cache::Entry** entry,
+ net::CompletionCallback* callback) {
+ return net::ERR_IO_PENDING;
+ }
+};
//-----------------------------------------------------------------------------
// helpers
@@ -869,6 +876,168 @@ struct Context {
//-----------------------------------------------------------------------------
// tests
+namespace net {
+
+// This is a friend class of the HttpCache so that we can interact with it to
+// perform a few internal tests. We override the backend factory method so that
+// we can test the logic of backend instantiation.
+class HttpCacheTest : public testing::Test {
+ public:
+ // Controls whether to block the backend instantiation or not. If |block| is
+ // true, any blocked call will be notified via the provided callback.
+ void BlockCacheCreation(bool block) {
+ block_ = block;
+ if (!block_ && callback_) {
+ *backend_ = new MockDiskCache();
+ callback_->Run(OK);
+ callback_ = NULL;
+ }
+ }
+
+ protected:
+ virtual void SetUp() {
+ current_test_ = this;
+ backend_ = NULL;
+ callback_ = NULL;
+ block_ = false;
+ }
+
+ virtual void TearDown() { current_test_ = NULL; }
+
+ // Implements disk_cache::CreateCacheBackend().
+ static int MockCreateBackend(CacheType type, const FilePath& path,
+ int max_bytes, bool force, MessageLoop* thread,
+ disk_cache::Backend** backend,
+ CompletionCallback* callback) {
+ if (current_test_ == NULL) {
+ EXPECT_TRUE(current_test_);
+ return ERR_FAILED;
+ }
+ if (!current_test_->block_) {
+ *backend = new MockDiskCache();
+ return OK;
+ }
+
+ current_test_->backend_ = backend;
+ current_test_->callback_ = callback;
+ return ERR_IO_PENDING;
+ }
+
+ private:
+ static HttpCacheTest* current_test_;
+ disk_cache::Backend** backend_;
+ CompletionCallback* callback_;
+ bool block_;
+};
+
+// Static.
+HttpCacheTest* HttpCacheTest::current_test_ = NULL;
+
+// Tests that we queue requests when initializing the backend.
+TEST_F(HttpCacheTest, SimpleGET_WaitForBackend) {
+ MockHttpCache cache(NULL);
+ cache.http_cache()->create_backend_fn_ = MockCreateBackend;
+ cache.http_cache()->set_type(MEMORY_CACHE);
+ BlockCacheCreation(true);
+
+ MockHttpRequest request0(kSimpleGET_Transaction);
+ MockHttpRequest request1(kTypicalGET_Transaction);
+ MockHttpRequest request2(kETagGET_Transaction);
+
+ std::vector<Context*> context_list;
+ const int kNumTransactions = 3;
+
+ for (int i = 0; i < kNumTransactions; i++) {
+ context_list.push_back(new Context());
+ Context* c = context_list[i];
+
+ c->result = cache.http_cache()->CreateTransaction(&c->trans);
+ EXPECT_EQ(OK, c->result);
+ }
+
+ context_list[0]->result = context_list[0]->trans->Start(
+ &request0, &context_list[0]->callback, BoundNetLog());
+ context_list[1]->result = context_list[1]->trans->Start(
+ &request1, &context_list[1]->callback, BoundNetLog());
+ context_list[2]->result = context_list[2]->trans->Start(
+ &request2, &context_list[2]->callback, BoundNetLog());
+
+ // Just to make sure that everything is still pending.
+ MessageLoop::current()->RunAllPending();
+
+ // The first request should be creating the disk cache.
+ EXPECT_FALSE(context_list[0]->callback.have_result());
+
+ BlockCacheCreation(false);
+
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(3, cache.network_layer()->transaction_count());
+ EXPECT_EQ(3, cache.disk_cache()->create_count());
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ EXPECT_TRUE(context_list[i]->callback.have_result());
+ delete context_list[i];
+ }
+}
+
+// Tests that we can cancel requests that are queued waiting for the backend
+// to be initialized.
+TEST_F(HttpCacheTest, SimpleGET_WaitForBackend_CancelCreate) {
+ MockHttpCache cache(NULL);
+ cache.http_cache()->create_backend_fn_ = MockCreateBackend;
+ cache.http_cache()->set_type(MEMORY_CACHE);
+ BlockCacheCreation(true);
+
+ MockHttpRequest request0(kSimpleGET_Transaction);
+ MockHttpRequest request1(kTypicalGET_Transaction);
+ MockHttpRequest request2(kETagGET_Transaction);
+
+ std::vector<Context*> context_list;
+ const int kNumTransactions = 3;
+
+ for (int i = 0; i < kNumTransactions; i++) {
+ context_list.push_back(new Context());
+ Context* c = context_list[i];
+
+ c->result = cache.http_cache()->CreateTransaction(&c->trans);
+ EXPECT_EQ(OK, c->result);
+ }
+
+ context_list[0]->result = context_list[0]->trans->Start(
+ &request0, &context_list[0]->callback, BoundNetLog());
+ context_list[1]->result = context_list[1]->trans->Start(
+ &request1, &context_list[1]->callback, BoundNetLog());
+ context_list[2]->result = context_list[2]->trans->Start(
+ &request2, &context_list[2]->callback, BoundNetLog());
+
+ // Just to make sure that everything is still pending.
+ MessageLoop::current()->RunAllPending();
+
+ // The first request should be creating the disk cache.
+ EXPECT_FALSE(context_list[0]->callback.have_result());
+
+ // Cancel a request from the pending queue.
+ delete context_list[1];
+ context_list[1] = NULL;
+
+ // Cancel the request that is creating the entry.
+ delete context_list[0];
+ context_list[0] = NULL;
+
+ // Complete the last transaction.
+ BlockCacheCreation(false);
+
+ context_list[2]->result =
+ context_list[2]->callback.GetResult(context_list[2]->result);
+ ReadAndVerifyTransaction(context_list[2]->trans.get(), kETagGET_Transaction);
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ delete context_list[2];
+}
+
+} // net namespace.
TEST(HttpCache, CreateThenDestroy) {
MockHttpCache cache;
@@ -912,21 +1081,36 @@ TEST(HttpCache, SimpleGETNoDiskCache) {
// Check that the NetLog was filled as expected.
// (We attempted to both Open and Create entries, but both failed).
- EXPECT_EQ(4u, log.entries().size());
+ EXPECT_EQ(6u, log.entries().size());
+ EXPECT_TRUE(net::LogContainsBeginEvent(
+ log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+ EXPECT_TRUE(net::LogContainsEndEvent(
+ log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_WAITING));
EXPECT_TRUE(net::LogContainsBeginEvent(
- log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+ log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
EXPECT_TRUE(net::LogContainsEndEvent(
- log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+ log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
EXPECT_TRUE(net::LogContainsBeginEvent(
- log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+ log.entries(), 4, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
EXPECT_TRUE(net::LogContainsEndEvent(
- log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+ log.entries(), 5, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(0, cache.disk_cache()->open_count());
EXPECT_EQ(0, cache.disk_cache()->create_count());
}
+TEST(HttpCache, SimpleGETNoDiskCache2) {
+ // This will initialize a cache object with NULL backend.
+ MockHttpCache cache(NULL);
+
+ // Read from the network, and don't use the cache.
+ RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_FALSE(cache.http_cache()->GetBackend());
+}
+
TEST(HttpCache, SimpleGETWithDiskFailures) {
MockHttpCache cache;
@@ -996,19 +1180,23 @@ TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Hit) {
log.bound());
// Check that the NetLog was filled as expected.
- EXPECT_EQ(6u, log.entries().size());
+ EXPECT_EQ(8u, log.entries().size());
EXPECT_TRUE(net::LogContainsBeginEvent(
- log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+ log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_WAITING));
EXPECT_TRUE(net::LogContainsEndEvent(
- log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+ log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_WAITING));
EXPECT_TRUE(net::LogContainsBeginEvent(
- log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+ log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
EXPECT_TRUE(net::LogContainsEndEvent(
- log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+ log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
EXPECT_TRUE(net::LogContainsBeginEvent(
- log.entries(), 4, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+ log.entries(), 4, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
EXPECT_TRUE(net::LogContainsEndEvent(
- log.entries(), 5, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+ log.entries(), 5, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+ EXPECT_TRUE(net::LogContainsBeginEvent(
+ log.entries(), 6, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+ EXPECT_TRUE(net::LogContainsEndEvent(
+ log.entries(), 7, net::NetLog::TYPE_HTTP_CACHE_WAITING));
// force this transaction to read from the cache
MockTransaction transaction(kSimpleGET_Transaction);
@@ -1019,19 +1207,23 @@ TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Hit) {
RunTransactionTestWithLog(cache.http_cache(), transaction, log.bound());
// Check that the NetLog was filled as expected.
- EXPECT_EQ(6u, log.entries().size());
+ EXPECT_EQ(8u, log.entries().size());
EXPECT_TRUE(net::LogContainsBeginEvent(
- log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+ log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_WAITING));
EXPECT_TRUE(net::LogContainsEndEvent(
- log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+ log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_WAITING));
EXPECT_TRUE(net::LogContainsBeginEvent(
- log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+ log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
EXPECT_TRUE(net::LogContainsEndEvent(
- log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+ log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
EXPECT_TRUE(net::LogContainsBeginEvent(
- log.entries(), 4, net::NetLog::TYPE_HTTP_CACHE_READ_INFO));
+ log.entries(), 4, net::NetLog::TYPE_HTTP_CACHE_WAITING));
EXPECT_TRUE(net::LogContainsEndEvent(
- log.entries(), 5, net::NetLog::TYPE_HTTP_CACHE_READ_INFO));
+ log.entries(), 5, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+ EXPECT_TRUE(net::LogContainsBeginEvent(
+ log.entries(), 6, net::NetLog::TYPE_HTTP_CACHE_READ_INFO));
+ EXPECT_TRUE(net::LogContainsEndEvent(
+ log.entries(), 7, net::NetLog::TYPE_HTTP_CACHE_READ_INFO));
EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(1, cache.disk_cache()->open_count());
@@ -1111,19 +1303,23 @@ TEST(HttpCache, SimpleGET_LoadBypassCache) {
RunTransactionTestWithLog(cache.http_cache(), transaction, log.bound());
// Check that the NetLog was filled as expected.
- EXPECT_EQ(6u, log.entries().size());
+ EXPECT_EQ(8u, log.entries().size());
EXPECT_TRUE(net::LogContainsBeginEvent(
- log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY));
+ log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_WAITING));
EXPECT_TRUE(net::LogContainsEndEvent(
- log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY));
+ log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_WAITING));
EXPECT_TRUE(net::LogContainsBeginEvent(
- log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+ log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY));
EXPECT_TRUE(net::LogContainsEndEvent(
- log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+ log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY));
EXPECT_TRUE(net::LogContainsBeginEvent(
- log.entries(), 4, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+ log.entries(), 4, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
EXPECT_TRUE(net::LogContainsEndEvent(
- log.entries(), 5, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+ log.entries(), 5, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+ EXPECT_TRUE(net::LogContainsBeginEvent(
+ log.entries(), 6, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+ EXPECT_TRUE(net::LogContainsEndEvent(
+ log.entries(), 7, net::NetLog::TYPE_HTTP_CACHE_WAITING));
EXPECT_EQ(2, cache.network_layer()->transaction_count());
EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -1668,6 +1864,42 @@ TEST(HttpCache, SimpleGET_AbandonedCacheRead) {
MessageLoop::current()->RunAllPending();
}
+// Tests that we can delete the HttpCache and deal with queued transactions
+// ("waiting for the backend" as opposed to Active or Doomed entries).
+TEST(HttpCache, SimpleGET_ManyWriters_DeleteCache) {
+ scoped_ptr<MockHttpCache> cache(new MockHttpCache(new MockDiskCacheNoCB()));
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+
+ std::vector<Context*> context_list;
+ const int kNumTransactions = 5;
+
+ for (int i = 0; i < kNumTransactions; i++) {
+ context_list.push_back(new Context());
+ Context* c = context_list[i];
+
+ c->result = cache->http_cache()->CreateTransaction(&c->trans);
+ EXPECT_EQ(net::OK, c->result);
+
+ c->result = c->trans->Start(&request, &c->callback, net::BoundNetLog());
+ }
+
+ // The first request should be creating the disk cache entry and the others
+ // should be pending.
+
+ EXPECT_EQ(0, cache->network_layer()->transaction_count());
+ EXPECT_EQ(0, cache->disk_cache()->open_count());
+ EXPECT_EQ(0, cache->disk_cache()->create_count());
+
+ cache.reset();
+
+ // There is not much to do with the transactions at this point... they are
+ // waiting for a callback that will not fire.
+ for (int i = 0; i < kNumTransactions; ++i) {
+ delete context_list[i];
+ }
+}
+
TEST(HttpCache, TypicalGET_ConditionalRequest) {
MockHttpCache cache;
@@ -3885,12 +4117,12 @@ TEST(HttpCache, SyncRead) {
TEST_MODE_SYNC_CACHE_WRITE);
MockHttpRequest r1(transaction),
- r2(transaction),
- r3(transaction);
+ r2(transaction),
+ r3(transaction);
TestTransactionConsumer c1(cache.http_cache()),
- c2(cache.http_cache()),
- c3(cache.http_cache());
+ c2(cache.http_cache()),
+ c3(cache.http_cache());
c1.Start(&r1, net::BoundNetLog());