849 lines
26 KiB
C++
849 lines
26 KiB
C++
// Copyright 2012 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/http/mock_http_cache.h"
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "base/feature_list.h"
|
|
#include "base/functional/bind.h"
|
|
#include "base/functional/callback.h"
|
|
#include "base/functional/callback_helpers.h"
|
|
#include "base/location.h"
|
|
#include "base/task/single_thread_task_runner.h"
|
|
#include "net/base/features.h"
|
|
#include "net/base/net_errors.h"
|
|
#include "net/disk_cache/disk_cache_test_util.h"
|
|
#include "net/http/http_cache_writers.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace net {
|
|
|
|
namespace {
|
|
|
|
// During testing, we are going to limit the size of a cache entry to this many
|
|
// bytes using DCHECKs in order to prevent a test from causing unbounded memory
|
|
// growth. In practice cache entry shouldn't come anywhere near this limit for
|
|
// tests that use the mock cache. If they do, that's likely a problem with the
|
|
// test. If a test requires using massive cache entries, they should use a real
|
|
// cache backend instead.
|
|
const int kMaxMockCacheEntrySize = 100 * 1000 * 1000;
|
|
|
|
// We can override the test mode for a given operation by setting this global
|
|
// variable.
|
|
int g_test_mode = 0;
|
|
|
|
int GetTestModeForEntry(const std::string& key) {
|
|
GURL url(HttpCache::GetResourceURLFromHttpCacheKey(key));
|
|
const MockTransaction* t = FindMockTransaction(url);
|
|
DCHECK(t);
|
|
return t->test_mode;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
struct MockDiskEntry::CallbackInfo {
|
|
scoped_refptr<MockDiskEntry> entry;
|
|
base::OnceClosure callback;
|
|
};
|
|
|
|
MockDiskEntry::MockDiskEntry(const std::string& key)
|
|
: key_(key), max_file_size_(std::numeric_limits<int>::max()) {
|
|
test_mode_ = GetTestModeForEntry(key);
|
|
}
|
|
|
|
void MockDiskEntry::Doom() {
|
|
doomed_ = true;
|
|
}
|
|
|
|
void MockDiskEntry::Close() {
|
|
Release();
|
|
}
|
|
|
|
std::string MockDiskEntry::GetKey() const {
|
|
return key_;
|
|
}
|
|
|
|
base::Time MockDiskEntry::GetLastUsed() const {
|
|
return base::Time::Now();
|
|
}
|
|
|
|
base::Time MockDiskEntry::GetLastModified() const {
|
|
return base::Time::Now();
|
|
}
|
|
|
|
int32_t MockDiskEntry::GetDataSize(int index) const {
|
|
DCHECK(index >= 0 && index < kNumCacheEntryDataIndices);
|
|
return static_cast<int32_t>(data_[index].size());
|
|
}
|
|
|
|
int MockDiskEntry::ReadData(int index,
|
|
int offset,
|
|
IOBuffer* buf,
|
|
int buf_len,
|
|
CompletionOnceCallback callback) {
|
|
DCHECK(index >= 0 && index < kNumCacheEntryDataIndices);
|
|
DCHECK(!callback.is_null());
|
|
|
|
if (fail_requests_ & FAIL_READ)
|
|
return ERR_CACHE_READ_FAILURE;
|
|
|
|
if (offset < 0 || offset > static_cast<int>(data_[index].size()))
|
|
return ERR_FAILED;
|
|
if (static_cast<size_t>(offset) == data_[index].size())
|
|
return 0;
|
|
|
|
int num = std::min(buf_len, static_cast<int>(data_[index].size()) - offset);
|
|
memcpy(buf->data(), &data_[index][offset], num);
|
|
|
|
if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ)
|
|
return num;
|
|
|
|
// Pause and resume.
|
|
if (defer_op_ == DEFER_READ) {
|
|
defer_op_ = DEFER_NONE;
|
|
resume_callback_ = std::move(callback);
|
|
resume_return_code_ = num;
|
|
return ERR_IO_PENDING;
|
|
}
|
|
|
|
CallbackLater(std::move(callback), num);
|
|
return ERR_IO_PENDING;
|
|
}
|
|
|
|
void MockDiskEntry::ResumeDiskEntryOperation() {
|
|
DCHECK(!resume_callback_.is_null());
|
|
CallbackLater(std::move(resume_callback_), resume_return_code_);
|
|
resume_return_code_ = 0;
|
|
}
|
|
|
|
int MockDiskEntry::WriteData(int index,
|
|
int offset,
|
|
IOBuffer* buf,
|
|
int buf_len,
|
|
CompletionOnceCallback callback,
|
|
bool truncate) {
|
|
DCHECK(index >= 0 && index < kNumCacheEntryDataIndices);
|
|
DCHECK(!callback.is_null());
|
|
DCHECK(truncate);
|
|
|
|
if (fail_requests_ & FAIL_WRITE) {
|
|
CallbackLater(std::move(callback), ERR_CACHE_READ_FAILURE);
|
|
return ERR_IO_PENDING;
|
|
}
|
|
|
|
if (offset < 0 || offset > static_cast<int>(data_[index].size()))
|
|
return ERR_FAILED;
|
|
|
|
DCHECK_LT(offset + buf_len, kMaxMockCacheEntrySize);
|
|
if (offset + buf_len > max_file_size_ && index == 1)
|
|
return net::ERR_FAILED;
|
|
|
|
data_[index].resize(offset + buf_len);
|
|
if (buf_len)
|
|
memcpy(&data_[index][offset], buf->data(), buf_len);
|
|
|
|
if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE)
|
|
return buf_len;
|
|
|
|
if (defer_op_ == DEFER_WRITE) {
|
|
defer_op_ = DEFER_NONE;
|
|
resume_callback_ = std::move(callback);
|
|
resume_return_code_ = buf_len;
|
|
return ERR_IO_PENDING;
|
|
}
|
|
|
|
CallbackLater(std::move(callback), buf_len);
|
|
return ERR_IO_PENDING;
|
|
}
|
|
|
|
int MockDiskEntry::ReadSparseData(int64_t offset,
|
|
IOBuffer* buf,
|
|
int buf_len,
|
|
CompletionOnceCallback callback) {
|
|
DCHECK(!callback.is_null());
|
|
if (fail_sparse_requests_)
|
|
return ERR_NOT_IMPLEMENTED;
|
|
if (!sparse_ || busy_ || cancel_)
|
|
return ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
|
if (offset < 0)
|
|
return ERR_FAILED;
|
|
|
|
if (fail_requests_ & FAIL_READ_SPARSE)
|
|
return ERR_CACHE_READ_FAILURE;
|
|
|
|
DCHECK(offset < std::numeric_limits<int32_t>::max());
|
|
int real_offset = static_cast<int>(offset);
|
|
if (!buf_len)
|
|
return 0;
|
|
|
|
int num = std::min(static_cast<int>(data_[1].size()) - real_offset,
|
|
buf_len);
|
|
memcpy(buf->data(), &data_[1][real_offset], num);
|
|
|
|
if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ)
|
|
return num;
|
|
|
|
CallbackLater(std::move(callback), num);
|
|
busy_ = true;
|
|
delayed_ = false;
|
|
return ERR_IO_PENDING;
|
|
}
|
|
|
|
int MockDiskEntry::WriteSparseData(int64_t offset,
|
|
IOBuffer* buf,
|
|
int buf_len,
|
|
CompletionOnceCallback callback) {
|
|
DCHECK(!callback.is_null());
|
|
if (fail_sparse_requests_)
|
|
return ERR_NOT_IMPLEMENTED;
|
|
if (busy_ || cancel_)
|
|
return ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
|
if (!sparse_) {
|
|
if (data_[1].size())
|
|
return ERR_CACHE_OPERATION_NOT_SUPPORTED;
|
|
sparse_ = true;
|
|
}
|
|
if (offset < 0)
|
|
return ERR_FAILED;
|
|
if (!buf_len)
|
|
return 0;
|
|
|
|
if (fail_requests_ & FAIL_WRITE_SPARSE)
|
|
return ERR_CACHE_READ_FAILURE;
|
|
|
|
DCHECK(offset < std::numeric_limits<int32_t>::max());
|
|
int real_offset = static_cast<int>(offset);
|
|
|
|
if (static_cast<int>(data_[1].size()) < real_offset + buf_len) {
|
|
DCHECK_LT(real_offset + buf_len, kMaxMockCacheEntrySize);
|
|
data_[1].resize(real_offset + buf_len);
|
|
}
|
|
|
|
memcpy(&data_[1][real_offset], buf->data(), buf_len);
|
|
if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE)
|
|
return buf_len;
|
|
|
|
CallbackLater(std::move(callback), buf_len);
|
|
return ERR_IO_PENDING;
|
|
}
|
|
|
|
disk_cache::RangeResult MockDiskEntry::GetAvailableRange(
|
|
int64_t offset,
|
|
int len,
|
|
RangeResultCallback callback) {
|
|
DCHECK(!callback.is_null());
|
|
if (!sparse_ || busy_ || cancel_)
|
|
return RangeResult(ERR_CACHE_OPERATION_NOT_SUPPORTED);
|
|
if (offset < 0)
|
|
return RangeResult(ERR_FAILED);
|
|
|
|
if (fail_requests_ & FAIL_GET_AVAILABLE_RANGE)
|
|
return RangeResult(ERR_CACHE_READ_FAILURE);
|
|
|
|
RangeResult result;
|
|
result.net_error = OK;
|
|
result.start = offset;
|
|
result.available_len = 0;
|
|
DCHECK(offset < std::numeric_limits<int32_t>::max());
|
|
int real_offset = static_cast<int>(offset);
|
|
if (static_cast<int>(data_[1].size()) < real_offset)
|
|
return result;
|
|
|
|
int num = std::min(static_cast<int>(data_[1].size()) - real_offset, len);
|
|
for (; num > 0; num--, real_offset++) {
|
|
if (!result.available_len) {
|
|
if (data_[1][real_offset]) {
|
|
result.available_len++;
|
|
result.start = real_offset;
|
|
}
|
|
} else {
|
|
if (!data_[1][real_offset])
|
|
break;
|
|
result.available_len++;
|
|
}
|
|
}
|
|
if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE) {
|
|
return result;
|
|
}
|
|
|
|
CallbackLater(base::BindOnce(std::move(callback), result));
|
|
return RangeResult(ERR_IO_PENDING);
|
|
}
|
|
|
|
bool MockDiskEntry::CouldBeSparse() const {
|
|
if (fail_sparse_requests_)
|
|
return false;
|
|
return sparse_;
|
|
}
|
|
|
|
void MockDiskEntry::CancelSparseIO() {
|
|
cancel_ = true;
|
|
}
|
|
|
|
net::Error MockDiskEntry::ReadyForSparseIO(CompletionOnceCallback callback) {
|
|
if (fail_sparse_requests_)
|
|
return ERR_NOT_IMPLEMENTED;
|
|
if (!cancel_)
|
|
return OK;
|
|
|
|
cancel_ = false;
|
|
DCHECK(!callback.is_null());
|
|
if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ)
|
|
return OK;
|
|
|
|
// The pending operation is already in the message loop (and hopefully
|
|
// already in the second pass). Just notify the caller that it finished.
|
|
CallbackLater(std::move(callback), 0);
|
|
return ERR_IO_PENDING;
|
|
}
|
|
|
|
void MockDiskEntry::SetLastUsedTimeForTest(base::Time time) {
|
|
NOTREACHED();
|
|
}
|
|
|
|
// If |value| is true, don't deliver any completion callbacks until called
|
|
// again with |value| set to false. Caution: remember to enable callbacks
|
|
// again or all subsequent tests will fail.
|
|
// Static.
|
|
void MockDiskEntry::IgnoreCallbacks(bool value) {
|
|
if (ignore_callbacks_ == value)
|
|
return;
|
|
ignore_callbacks_ = value;
|
|
if (!value)
|
|
StoreAndDeliverCallbacks(false, nullptr, base::OnceClosure());
|
|
}
|
|
|
|
MockDiskEntry::~MockDiskEntry() = default;
|
|
|
|
// Unlike the callbacks for MockHttpTransaction, we want this one to run even
|
|
// if the consumer called Close on the MockDiskEntry. We achieve that by
|
|
// leveraging the fact that this class is reference counted.
|
|
void MockDiskEntry::CallbackLater(base::OnceClosure callback) {
|
|
if (ignore_callbacks_)
|
|
return StoreAndDeliverCallbacks(true, this, std::move(callback));
|
|
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
|
|
FROM_HERE,
|
|
base::BindOnce(&MockDiskEntry::RunCallback, this, std::move(callback)));
|
|
}
|
|
|
|
void MockDiskEntry::CallbackLater(CompletionOnceCallback callback, int result) {
|
|
CallbackLater(base::BindOnce(std::move(callback), result));
|
|
}
|
|
|
|
void MockDiskEntry::RunCallback(base::OnceClosure callback) {
|
|
if (busy_) {
|
|
// This is kind of hacky, but controlling the behavior of just this entry
|
|
// from a test is sort of complicated. What we really want to do is
|
|
// delay the delivery of a sparse IO operation a little more so that the
|
|
// request start operation (async) will finish without seeing the end of
|
|
// this operation (already posted to the message loop)... and without
|
|
// just delaying for n mS (which may cause trouble with slow bots). So
|
|
// we re-post this operation (all async sparse IO operations will take two
|
|
// trips through the message loop instead of one).
|
|
if (!delayed_) {
|
|
delayed_ = true;
|
|
return CallbackLater(std::move(callback));
|
|
}
|
|
}
|
|
busy_ = false;
|
|
std::move(callback).Run();
|
|
}
|
|
|
|
// When |store| is true, stores the callback to be delivered later; otherwise
|
|
// delivers any callback previously stored.
|
|
// Static.
|
|
void MockDiskEntry::StoreAndDeliverCallbacks(bool store,
|
|
MockDiskEntry* entry,
|
|
base::OnceClosure callback) {
|
|
static std::vector<CallbackInfo> callback_list;
|
|
if (store) {
|
|
CallbackInfo c = {entry, std::move(callback)};
|
|
callback_list.push_back(std::move(c));
|
|
} else {
|
|
for (auto& callback_info : callback_list) {
|
|
callback_info.entry->CallbackLater(std::move(callback_info.callback));
|
|
}
|
|
callback_list.clear();
|
|
}
|
|
}
|
|
|
|
// Statics.
|
|
bool MockDiskEntry::ignore_callbacks_ = false;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
MockDiskCache::MockDiskCache()
|
|
: Backend(DISK_CACHE), max_file_size_(std::numeric_limits<int>::max()) {}
|
|
|
|
MockDiskCache::~MockDiskCache() {
|
|
ReleaseAll();
|
|
}
|
|
|
|
int32_t MockDiskCache::GetEntryCount() const {
|
|
return static_cast<int32_t>(entries_.size());
|
|
}
|
|
|
|
disk_cache::EntryResult MockDiskCache::OpenOrCreateEntry(
|
|
const std::string& key,
|
|
net::RequestPriority request_priority,
|
|
EntryResultCallback callback) {
|
|
DCHECK(!callback.is_null());
|
|
|
|
if (force_fail_callback_later_) {
|
|
CallbackLater(base::BindOnce(
|
|
std::move(callback),
|
|
EntryResult::MakeError(ERR_CACHE_OPEN_OR_CREATE_FAILURE)));
|
|
return EntryResult::MakeError(ERR_IO_PENDING);
|
|
}
|
|
|
|
if (fail_requests_)
|
|
return EntryResult::MakeError(ERR_CACHE_OPEN_OR_CREATE_FAILURE);
|
|
|
|
EntryResult result;
|
|
|
|
// First try opening the entry.
|
|
auto split_callback = base::SplitOnceCallback(std::move(callback));
|
|
result = OpenEntry(key, request_priority, std::move(split_callback.first));
|
|
if (result.net_error() == OK || result.net_error() == ERR_IO_PENDING)
|
|
return result;
|
|
|
|
// Unable to open, try creating the entry.
|
|
result = CreateEntry(key, request_priority, std::move(split_callback.second));
|
|
if (result.net_error() == OK || result.net_error() == ERR_IO_PENDING)
|
|
return result;
|
|
|
|
return EntryResult::MakeError(ERR_CACHE_OPEN_OR_CREATE_FAILURE);
|
|
}
|
|
|
|
disk_cache::EntryResult MockDiskCache::OpenEntry(
|
|
const std::string& key,
|
|
net::RequestPriority request_priority,
|
|
EntryResultCallback callback) {
|
|
DCHECK(!callback.is_null());
|
|
if (force_fail_callback_later_) {
|
|
CallbackLater(base::BindOnce(
|
|
std::move(callback), EntryResult::MakeError(ERR_CACHE_OPEN_FAILURE)));
|
|
return EntryResult::MakeError(ERR_IO_PENDING);
|
|
}
|
|
|
|
if (fail_requests_)
|
|
return EntryResult::MakeError(ERR_CACHE_OPEN_FAILURE);
|
|
|
|
auto it = entries_.find(key);
|
|
if (it == entries_.end())
|
|
return EntryResult::MakeError(ERR_CACHE_OPEN_FAILURE);
|
|
|
|
if (it->second->is_doomed()) {
|
|
it->second->Release();
|
|
entries_.erase(it);
|
|
return EntryResult::MakeError(ERR_CACHE_OPEN_FAILURE);
|
|
}
|
|
|
|
open_count_++;
|
|
|
|
MockDiskEntry* entry = it->second;
|
|
entry->AddRef();
|
|
|
|
if (soft_failures_ || soft_failures_one_instance_) {
|
|
entry->set_fail_requests(soft_failures_ | soft_failures_one_instance_);
|
|
soft_failures_one_instance_ = 0;
|
|
}
|
|
|
|
entry->set_max_file_size(max_file_size_);
|
|
|
|
EntryResult result = EntryResult::MakeOpened(entry);
|
|
if (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START)
|
|
return result;
|
|
|
|
CallbackLater(base::BindOnce(std::move(callback), std::move(result)));
|
|
return EntryResult::MakeError(ERR_IO_PENDING);
|
|
}
|
|
|
|
disk_cache::EntryResult MockDiskCache::CreateEntry(
|
|
const std::string& key,
|
|
net::RequestPriority request_priority,
|
|
EntryResultCallback callback) {
|
|
DCHECK(!callback.is_null());
|
|
if (force_fail_callback_later_) {
|
|
CallbackLater(base::BindOnce(
|
|
std::move(callback), EntryResult::MakeError(ERR_CACHE_CREATE_FAILURE)));
|
|
return EntryResult::MakeError(ERR_IO_PENDING);
|
|
}
|
|
|
|
if (fail_requests_)
|
|
return EntryResult::MakeError(ERR_CACHE_CREATE_FAILURE);
|
|
|
|
auto it = entries_.find(key);
|
|
if (it != entries_.end()) {
|
|
if (!it->second->is_doomed()) {
|
|
if (double_create_check_)
|
|
NOTREACHED();
|
|
else
|
|
return EntryResult::MakeError(ERR_CACHE_CREATE_FAILURE);
|
|
}
|
|
it->second->Release();
|
|
entries_.erase(it);
|
|
}
|
|
|
|
create_count_++;
|
|
|
|
MockDiskEntry* new_entry = new MockDiskEntry(key);
|
|
|
|
new_entry->AddRef();
|
|
entries_[key] = new_entry;
|
|
|
|
new_entry->AddRef();
|
|
|
|
if (soft_failures_ || soft_failures_one_instance_) {
|
|
new_entry->set_fail_requests(soft_failures_ | soft_failures_one_instance_);
|
|
soft_failures_one_instance_ = 0;
|
|
}
|
|
|
|
if (fail_sparse_requests_)
|
|
new_entry->set_fail_sparse_requests();
|
|
|
|
new_entry->set_max_file_size(max_file_size_);
|
|
|
|
EntryResult result = EntryResult::MakeCreated(new_entry);
|
|
if (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START)
|
|
return result;
|
|
|
|
// Pause and resume.
|
|
if (defer_op_ == MockDiskEntry::DEFER_CREATE) {
|
|
defer_op_ = MockDiskEntry::DEFER_NONE;
|
|
resume_callback_ = base::BindOnce(std::move(callback), std::move(result));
|
|
return EntryResult::MakeError(ERR_IO_PENDING);
|
|
}
|
|
|
|
CallbackLater(base::BindOnce(std::move(callback), std::move(result)));
|
|
return EntryResult::MakeError(ERR_IO_PENDING);
|
|
}
|
|
|
|
net::Error MockDiskCache::DoomEntry(const std::string& key,
|
|
net::RequestPriority request_priority,
|
|
CompletionOnceCallback callback) {
|
|
DCHECK(!callback.is_null());
|
|
if (force_fail_callback_later_) {
|
|
CallbackLater(base::BindOnce(std::move(callback), ERR_CACHE_DOOM_FAILURE));
|
|
return ERR_IO_PENDING;
|
|
}
|
|
|
|
if (fail_requests_)
|
|
return ERR_CACHE_DOOM_FAILURE;
|
|
|
|
auto it = entries_.find(key);
|
|
if (it != entries_.end()) {
|
|
it->second->Release();
|
|
entries_.erase(it);
|
|
doomed_count_++;
|
|
}
|
|
|
|
if (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START)
|
|
return OK;
|
|
|
|
CallbackLater(base::BindOnce(std::move(callback), OK));
|
|
return ERR_IO_PENDING;
|
|
}
|
|
|
|
net::Error MockDiskCache::DoomAllEntries(CompletionOnceCallback callback) {
|
|
return ERR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
net::Error MockDiskCache::DoomEntriesBetween(const base::Time initial_time,
|
|
const base::Time end_time,
|
|
CompletionOnceCallback callback) {
|
|
return ERR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
net::Error MockDiskCache::DoomEntriesSince(const base::Time initial_time,
|
|
CompletionOnceCallback callback) {
|
|
return ERR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
int64_t MockDiskCache::CalculateSizeOfAllEntries(
|
|
Int64CompletionOnceCallback callback) {
|
|
return ERR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
class MockDiskCache::NotImplementedIterator : public Iterator {
|
|
public:
|
|
EntryResult OpenNextEntry(EntryResultCallback callback) override {
|
|
return EntryResult::MakeError(ERR_NOT_IMPLEMENTED);
|
|
}
|
|
};
|
|
|
|
std::unique_ptr<disk_cache::Backend::Iterator> MockDiskCache::CreateIterator() {
|
|
return std::make_unique<NotImplementedIterator>();
|
|
}
|
|
|
|
void MockDiskCache::GetStats(base::StringPairs* stats) {
|
|
}
|
|
|
|
void MockDiskCache::OnExternalCacheHit(const std::string& key) {
|
|
external_cache_hits_.push_back(key);
|
|
}
|
|
|
|
uint8_t MockDiskCache::GetEntryInMemoryData(const std::string& key) {
|
|
if (!support_in_memory_entry_data_)
|
|
return 0;
|
|
|
|
auto it = entries_.find(key);
|
|
if (it != entries_.end())
|
|
return it->second->in_memory_data();
|
|
return 0;
|
|
}
|
|
|
|
void MockDiskCache::SetEntryInMemoryData(const std::string& key, uint8_t data) {
|
|
auto it = entries_.find(key);
|
|
if (it != entries_.end())
|
|
it->second->set_in_memory_data(data);
|
|
}
|
|
|
|
int64_t MockDiskCache::MaxFileSize() const {
|
|
return max_file_size_;
|
|
}
|
|
|
|
void MockDiskCache::ReleaseAll() {
|
|
for (auto entry : entries_)
|
|
entry.second->Release();
|
|
entries_.clear();
|
|
}
|
|
|
|
void MockDiskCache::CallbackLater(base::OnceClosure callback) {
|
|
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
|
|
FROM_HERE, std::move(callback));
|
|
}
|
|
|
|
bool MockDiskCache::IsDiskEntryDoomed(const std::string& key) {
|
|
auto it = entries_.find(key);
|
|
if (it != entries_.end())
|
|
return it->second->is_doomed();
|
|
|
|
return false;
|
|
}
|
|
|
|
void MockDiskCache::ResumeCacheOperation() {
|
|
DCHECK(!resume_callback_.is_null());
|
|
CallbackLater(std::move(resume_callback_));
|
|
}
|
|
|
|
scoped_refptr<MockDiskEntry> MockDiskCache::GetDiskEntryRef(
|
|
const std::string& key) {
|
|
auto it = entries_.find(key);
|
|
if (it == entries_.end())
|
|
return nullptr;
|
|
return it->second;
|
|
}
|
|
|
|
const std::vector<std::string>& MockDiskCache::GetExternalCacheHits() const {
|
|
return external_cache_hits_;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
disk_cache::BackendResult MockBackendFactory::CreateBackend(
|
|
NetLog* net_log,
|
|
disk_cache::BackendResultCallback callback) {
|
|
return disk_cache::BackendResult::Make(std::make_unique<MockDiskCache>());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
MockHttpCache::MockHttpCache()
|
|
: MockHttpCache(std::make_unique<MockBackendFactory>()) {}
|
|
|
|
MockHttpCache::MockHttpCache(
|
|
std::unique_ptr<HttpCache::BackendFactory> disk_cache_factory)
|
|
: http_cache_(std::make_unique<MockNetworkLayer>(),
|
|
std::move(disk_cache_factory)) {}
|
|
|
|
disk_cache::Backend* MockHttpCache::backend() {
|
|
TestCompletionCallback cb;
|
|
disk_cache::Backend* backend;
|
|
int rv = http_cache_.GetBackend(&backend, cb.callback());
|
|
rv = cb.GetResult(rv);
|
|
return (rv == OK) ? backend : nullptr;
|
|
}
|
|
|
|
MockDiskCache* MockHttpCache::disk_cache() {
|
|
return static_cast<MockDiskCache*>(backend());
|
|
}
|
|
|
|
int MockHttpCache::CreateTransaction(std::unique_ptr<HttpTransaction>* trans) {
|
|
return http_cache_.CreateTransaction(DEFAULT_PRIORITY, trans);
|
|
}
|
|
|
|
void MockHttpCache::SimulateCacheLockTimeout() {
|
|
http_cache_.SimulateCacheLockTimeoutForTesting();
|
|
}
|
|
|
|
void MockHttpCache::SimulateCacheLockTimeoutAfterHeaders() {
|
|
http_cache_.SimulateCacheLockTimeoutAfterHeadersForTesting();
|
|
}
|
|
|
|
void MockHttpCache::FailConditionalizations() {
|
|
http_cache_.FailConditionalizationForTest();
|
|
}
|
|
|
|
bool MockHttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry,
|
|
HttpResponseInfo* response_info,
|
|
bool* response_truncated) {
|
|
int size = disk_entry->GetDataSize(0);
|
|
|
|
TestCompletionCallback cb;
|
|
scoped_refptr<IOBuffer> buffer = base::MakeRefCounted<IOBuffer>(size);
|
|
int rv = disk_entry->ReadData(0, 0, buffer.get(), size, cb.callback());
|
|
rv = cb.GetResult(rv);
|
|
EXPECT_EQ(size, rv);
|
|
|
|
return HttpCache::ParseResponseInfo(buffer->data(), size, response_info,
|
|
response_truncated);
|
|
}
|
|
|
|
bool MockHttpCache::WriteResponseInfo(disk_cache::Entry* disk_entry,
|
|
const HttpResponseInfo* response_info,
|
|
bool skip_transient_headers,
|
|
bool response_truncated) {
|
|
base::Pickle pickle;
|
|
response_info->Persist(
|
|
&pickle, skip_transient_headers, response_truncated);
|
|
|
|
TestCompletionCallback cb;
|
|
scoped_refptr<WrappedIOBuffer> data = base::MakeRefCounted<WrappedIOBuffer>(
|
|
reinterpret_cast<const char*>(pickle.data()));
|
|
int len = static_cast<int>(pickle.size());
|
|
|
|
int rv = disk_entry->WriteData(0, 0, data.get(), len, cb.callback(), true);
|
|
rv = cb.GetResult(rv);
|
|
return (rv == len);
|
|
}
|
|
|
|
bool MockHttpCache::OpenBackendEntry(const std::string& key,
|
|
disk_cache::Entry** entry) {
|
|
TestEntryResultCompletionCallback cb;
|
|
disk_cache::EntryResult result =
|
|
backend()->OpenEntry(key, net::HIGHEST, cb.callback());
|
|
result = cb.GetResult(std::move(result));
|
|
if (result.net_error() == OK) {
|
|
*entry = result.ReleaseEntry();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool MockHttpCache::CreateBackendEntry(const std::string& key,
|
|
disk_cache::Entry** entry,
|
|
NetLog* net_log) {
|
|
TestEntryResultCompletionCallback cb;
|
|
disk_cache::EntryResult result =
|
|
backend()->CreateEntry(key, net::HIGHEST, cb.callback());
|
|
result = cb.GetResult(std::move(result));
|
|
if (result.net_error() == OK) {
|
|
*entry = result.ReleaseEntry();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Static.
|
|
int MockHttpCache::GetTestMode(int test_mode) {
|
|
if (!g_test_mode)
|
|
return test_mode;
|
|
|
|
return g_test_mode;
|
|
}
|
|
|
|
// Static.
|
|
void MockHttpCache::SetTestMode(int test_mode) {
|
|
g_test_mode = test_mode;
|
|
}
|
|
|
|
bool MockHttpCache::IsWriterPresent(const std::string& key) {
|
|
HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
|
|
return entry && entry->writers && !entry->writers->IsEmpty();
|
|
}
|
|
|
|
bool MockHttpCache::IsHeadersTransactionPresent(const std::string& key) {
|
|
HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
|
|
return entry && entry->headers_transaction;
|
|
}
|
|
|
|
int MockHttpCache::GetCountReaders(const std::string& key) {
|
|
HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
|
|
return entry ? entry->readers.size() : 0;
|
|
}
|
|
|
|
int MockHttpCache::GetCountAddToEntryQueue(const std::string& key) {
|
|
HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
|
|
return entry ? entry->add_to_entry_queue.size() : 0;
|
|
}
|
|
|
|
int MockHttpCache::GetCountDoneHeadersQueue(const std::string& key) {
|
|
HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
|
|
return entry ? entry->done_headers_queue.size() : 0;
|
|
}
|
|
|
|
int MockHttpCache::GetCountWriterTransactions(const std::string& key) {
|
|
HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
|
|
return entry && entry->writers ? entry->writers->GetTransactionsCount() : 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
disk_cache::EntryResult MockDiskCacheNoCB::CreateEntry(
|
|
const std::string& key,
|
|
net::RequestPriority request_priority,
|
|
EntryResultCallback callback) {
|
|
return EntryResult::MakeError(ERR_IO_PENDING);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
disk_cache::BackendResult MockBackendNoCbFactory::CreateBackend(
|
|
NetLog* net_log,
|
|
disk_cache::BackendResultCallback callback) {
|
|
return disk_cache::BackendResult::Make(std::make_unique<MockDiskCacheNoCB>());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
MockBlockingBackendFactory::MockBlockingBackendFactory() = default;
|
|
MockBlockingBackendFactory::~MockBlockingBackendFactory() = default;
|
|
|
|
disk_cache::BackendResult MockBlockingBackendFactory::CreateBackend(
|
|
NetLog* net_log,
|
|
disk_cache::BackendResultCallback callback) {
|
|
if (!block_) {
|
|
return MakeResult();
|
|
}
|
|
|
|
callback_ = std::move(callback);
|
|
return disk_cache::BackendResult::MakeError(ERR_IO_PENDING);
|
|
}
|
|
|
|
void MockBlockingBackendFactory::FinishCreation() {
|
|
block_ = false;
|
|
if (!callback_.is_null()) {
|
|
// Running the callback might delete |this|.
|
|
std::move(callback_).Run(MakeResult());
|
|
}
|
|
}
|
|
|
|
disk_cache::BackendResult MockBlockingBackendFactory::MakeResult() {
|
|
if (fail_)
|
|
return disk_cache::BackendResult::MakeError(ERR_FAILED);
|
|
else
|
|
return disk_cache::BackendResult::Make(std::make_unique<MockDiskCache>());
|
|
}
|
|
|
|
} // namespace net
|