[go: nahoru, domu]

blob: d96b738868cf980cea6d5a2e687c7750143eff48 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/cache_storage/cache_storage_cache_entry_handler.h"
#include "base/functional/callback_helpers.h"
#include "base/uuid.h"
#include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
#include "content/browser/cache_storage/background_fetch_cache_entry_handler_impl.h"
#include "content/browser/cache_storage/cache_storage.h"
#include "content/browser/cache_storage/cache_storage_manager.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/filter/source_stream.h"
#include "services/network/public/cpp/source_stream_to_data_pipe.h"
#include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_impl.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/blob/blob_utils.h"
namespace content {
namespace {
// Adapter for a DiskCacheBlobEntry to be read as a net::SourceStream.
class DiskCacheStream : public net::SourceStream {
public:
DiskCacheStream(
scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>
blob_entry,
CacheStorageCache::EntryIndex cache_index,
uint64_t offset,
uint64_t length)
: SourceStream(net::SourceStream::SourceType::TYPE_NONE),
blob_entry_(blob_entry),
cache_index_(cache_index),
orig_offset_(offset),
orig_length_(length) {}
int Read(net::IOBuffer* dst_buffer,
int buffer_size,
net::CompletionOnceCallback callback) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
uint64_t offset = orig_offset_ + bytes_read_;
// Finished reading.
if (!MayHaveMoreBytes())
return 0;
if (buffer_size < 0)
return net::ERR_INVALID_ARGUMENT;
uint64_t length = std::min(static_cast<uint64_t>(buffer_size),
orig_length_ - bytes_read_);
int result = blob_entry_->Read(
std::move(dst_buffer), cache_index_, offset, length,
base::BindOnce(
[](DiskCacheStream* stream, net::CompletionOnceCallback callback,
int result) {
// |blob_entry_| is strongly owned by |stream| so this can be
// safely Unretained.
if (result > 0)
stream->bytes_read_ += result;
std::move(callback).Run(result);
},
base::Unretained(this), std::move(callback)));
if (result > 0)
bytes_read_ += result;
return result;
}
std::string Description() const override { return "DiskCacheStream"; }
bool MayHaveMoreBytes() const override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return bytes_read_ < orig_length_;
}
private:
const scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>
blob_entry_;
const CacheStorageCache::EntryIndex cache_index_;
const uint64_t orig_offset_;
const uint64_t orig_length_;
uint64_t bytes_read_ = 0;
SEQUENCE_CHECKER(sequence_checker_);
};
// A |storage::mojom::BlobDataItemReader| implementation that
// wraps a |DiskCacheBlobEntry|. In addition, each |EntryReaderImpl| maps the
// main and side data to particular disk_cache indices.
class EntryReaderImpl : public storage::mojom::BlobDataItemReader {
public:
EntryReaderImpl(
scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>
blob_entry,
CacheStorageCache::EntryIndex disk_cache_index,
CacheStorageCache::EntryIndex side_data_disk_cache_index)
: blob_entry_(std::move(blob_entry)),
disk_cache_index_(disk_cache_index),
side_data_disk_cache_index_(side_data_disk_cache_index) {}
EntryReaderImpl(const EntryReaderImpl&) = delete;
EntryReaderImpl& operator=(const EntryReaderImpl&) = delete;
void Read(uint64_t offset,
uint64_t length,
mojo::ScopedDataPipeProducerHandle pipe,
ReadCallback callback) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
uint64_t size = blob_entry_->GetSize(disk_cache_index_);
if (offset > size) {
std::move(callback).Run(net::ERR_INVALID_ARGUMENT);
return;
}
if (length > size - offset) {
std::move(callback).Run(net::ERR_INVALID_ARGUMENT);
return;
}
auto stream = std::make_unique<DiskCacheStream>(
blob_entry_, disk_cache_index_, offset, length);
auto adapter = std::make_unique<network::SourceStreamToDataPipe>(
std::move(stream), std::move(pipe));
auto* adapter_raw = adapter.get();
adapter_raw->Start(base::BindOnce(
[](ReadCallback callback,
std::unique_ptr<network::SourceStreamToDataPipe> adapter,
int result) { std::move(callback).Run(result); },
std::move(callback), std::move(adapter)));
}
void ReadSideData(ReadSideDataCallback callback) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Use a WrappedIOBuffer so that the DiskCacheBlobEntry writes directly
// to the BigBuffer without a copy.
int length = blob_entry_->GetSize(side_data_disk_cache_index_);
mojo_base::BigBuffer output_buf(static_cast<size_t>(length));
auto wrapped_buf = base::MakeRefCounted<net::WrappedIOBuffer>(
reinterpret_cast<char*>(output_buf.data()), output_buf.size());
auto split_callback = base::SplitOnceCallback(base::BindOnce(
[](mojo_base::BigBuffer output_buf, ReadSideDataCallback callback,
int result) {
std::move(callback).Run(result, std::move(output_buf));
},
std::move(output_buf), std::move(callback)));
uint64_t offset = 0;
int result =
blob_entry_->Read(std::move(wrapped_buf), side_data_disk_cache_index_,
offset, length, std::move(split_callback.first));
if (result == net::ERR_IO_PENDING)
return;
std::move(split_callback.second).Run(result);
}
private:
const scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>
blob_entry_;
const CacheStorageCache::EntryIndex disk_cache_index_;
const CacheStorageCache::EntryIndex side_data_disk_cache_index_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace
CacheStorageCacheEntryHandler::DiskCacheBlobEntry::DiskCacheBlobEntry(
base::PassKey<CacheStorageCacheEntryHandler> key,
base::WeakPtr<CacheStorageCacheEntryHandler> entry_handler,
CacheStorageCacheHandle cache_handle,
disk_cache::ScopedEntryPtr disk_cache_entry)
: entry_handler_(std::move(entry_handler)),
cache_handle_(std::move(cache_handle)),
disk_cache_entry_(std::move(disk_cache_entry)) {}
int CacheStorageCacheEntryHandler::DiskCacheBlobEntry::Read(
scoped_refptr<net::IOBuffer> dst_buffer,
CacheStorageCache::EntryIndex disk_cache_index,
uint64_t offset,
int bytes_to_read,
base::OnceCallback<void(int)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!disk_cache_entry_)
return net::ERR_CACHE_READ_FAILURE;
return disk_cache_entry_->ReadData(disk_cache_index, offset, dst_buffer.get(),
bytes_to_read, std::move(callback));
}
int CacheStorageCacheEntryHandler::DiskCacheBlobEntry::GetSize(
CacheStorageCache::EntryIndex disk_cache_index) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!disk_cache_entry_)
return 0;
switch (disk_cache_index) {
case CacheStorageCache::INDEX_INVALID:
return 0;
case CacheStorageCache::INDEX_HEADERS:
return disk_cache_entry_->GetDataSize(CacheStorageCache::INDEX_HEADERS);
case CacheStorageCache::INDEX_RESPONSE_BODY:
return disk_cache_entry_->GetDataSize(
CacheStorageCache::INDEX_RESPONSE_BODY);
case CacheStorageCache::INDEX_SIDE_DATA:
return disk_cache_entry_->GetDataSize(CacheStorageCache::INDEX_SIDE_DATA);
}
NOTREACHED();
}
void CacheStorageCacheEntryHandler::DiskCacheBlobEntry::Invalidate() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
cache_handle_ = absl::nullopt;
entry_handler_ = nullptr;
disk_cache_entry_ = nullptr;
}
disk_cache::ScopedEntryPtr&
CacheStorageCacheEntryHandler::DiskCacheBlobEntry::disk_cache_entry() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return disk_cache_entry_;
}
CacheStorageCacheEntryHandler::DiskCacheBlobEntry::~DiskCacheBlobEntry() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (entry_handler_)
entry_handler_->EraseDiskCacheBlobEntry(this);
}
PutContext::PutContext(blink::mojom::FetchAPIRequestPtr request,
blink::mojom::FetchAPIResponsePtr response,
mojo::PendingRemote<blink::mojom::Blob> blob,
uint64_t blob_size,
mojo::PendingRemote<blink::mojom::Blob> side_data_blob,
uint64_t side_data_blob_size,
int64_t trace_id)
: request(std::move(request)),
response(std::move(response)),
blob(std::move(blob)),
blob_size(blob_size),
side_data_blob(std::move(side_data_blob)),
side_data_blob_size(side_data_blob_size),
trace_id(trace_id) {}
PutContext::~PutContext() = default;
// Default implementation of CacheStorageCacheEntryHandler.
class CacheStorageCacheEntryHandlerImpl : public CacheStorageCacheEntryHandler {
public:
CacheStorageCacheEntryHandlerImpl(
scoped_refptr<BlobStorageContextWrapper> blob_storage_context)
: CacheStorageCacheEntryHandler(std::move(blob_storage_context)) {}
~CacheStorageCacheEntryHandlerImpl() override = default;
std::unique_ptr<PutContext> CreatePutContext(
blink::mojom::FetchAPIRequestPtr request,
blink::mojom::FetchAPIResponsePtr response,
int64_t trace_id) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
mojo::PendingRemote<blink::mojom::Blob> blob;
uint64_t blob_size = blink::BlobUtils::kUnknownSize;
mojo::PendingRemote<blink::mojom::Blob> side_data_blob;
uint64_t side_data_blob_size = blink::BlobUtils::kUnknownSize;
if (response->blob) {
blob = std::move(response->blob->blob);
blob_size = response->blob->size;
}
if (response->side_data_blob_for_cache_put) {
side_data_blob = std::move(response->side_data_blob_for_cache_put->blob);
side_data_blob_size = response->side_data_blob_for_cache_put->size;
}
return std::make_unique<PutContext>(
std::move(request), std::move(response), std::move(blob), blob_size,
std::move(side_data_blob), side_data_blob_size, trace_id);
}
void PopulateResponseBody(scoped_refptr<DiskCacheBlobEntry> blob_entry,
blink::mojom::FetchAPIResponse* response) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// First create the blob and store it in the field for the main body
// loading.
response->blob = CreateBlobWithSideData(
std::move(blob_entry), CacheStorageCache::INDEX_RESPONSE_BODY,
CacheStorageCache::INDEX_SIDE_DATA);
// Then clone the blob to the |side_data_blob| field for loading code_cache.
mojo::Remote<blink::mojom::Blob> blob_remote(
std::move(response->blob->blob));
blob_remote->Clone(response->blob->blob.InitWithNewPipeAndPassReceiver());
response->side_data_blob = blink::mojom::SerializedBlob::New(
response->blob->uuid, response->blob->content_type,
response->blob->size, blob_remote.Unbind());
}
void PopulateRequestBody(scoped_refptr<DiskCacheBlobEntry> blob_entry,
blink::mojom::FetchAPIRequest* request) override {}
private:
base::WeakPtr<CacheStorageCacheEntryHandler> GetWeakPtr() override {
return weak_ptr_factory_.GetWeakPtr();
}
base::WeakPtrFactory<CacheStorageCacheEntryHandlerImpl> weak_ptr_factory_{
this};
};
CacheStorageCacheEntryHandler::CacheStorageCacheEntryHandler(
scoped_refptr<BlobStorageContextWrapper> blob_storage_context)
: blob_storage_context_(std::move(blob_storage_context)) {}
scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>
CacheStorageCacheEntryHandler::CreateDiskCacheBlobEntry(
CacheStorageCacheHandle cache_handle,
disk_cache::ScopedEntryPtr disk_cache_entry) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto blob_entry =
base::MakeRefCounted<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>(
base::PassKey<CacheStorageCacheEntryHandler>(), GetWeakPtr(),
std::move(cache_handle), std::move(disk_cache_entry));
DCHECK_EQ(blob_entries_.count(blob_entry.get()), 0u);
blob_entries_.insert(blob_entry.get());
return blob_entry;
}
CacheStorageCacheEntryHandler::~CacheStorageCacheEntryHandler() = default;
void CacheStorageCacheEntryHandler::InvalidateDiskCacheBlobEntrys() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Calling Invalidate() can cause the CacheStorageCacheEntryHandler to be
// destroyed. Be careful not to touch |this| after calling Invalidate().
std::set<DiskCacheBlobEntry*> entries = std::move(blob_entries_);
for (auto* entry : entries)
entry->Invalidate();
}
void CacheStorageCacheEntryHandler::EraseDiskCacheBlobEntry(
DiskCacheBlobEntry* blob_entry) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_NE(blob_entries_.count(blob_entry), 0u);
blob_entries_.erase(blob_entry);
}
// static
std::unique_ptr<CacheStorageCacheEntryHandler>
CacheStorageCacheEntryHandler::CreateCacheEntryHandler(
storage::mojom::CacheStorageOwner owner,
scoped_refptr<BlobStorageContextWrapper> blob_storage_context) {
switch (owner) {
case storage::mojom::CacheStorageOwner::kCacheAPI:
return std::make_unique<CacheStorageCacheEntryHandlerImpl>(
std::move(blob_storage_context));
case storage::mojom::CacheStorageOwner::kBackgroundFetch:
return std::make_unique<BackgroundFetchCacheEntryHandlerImpl>(
std::move(blob_storage_context));
}
NOTREACHED();
}
blink::mojom::SerializedBlobPtr CacheStorageCacheEntryHandler::CreateBlob(
scoped_refptr<DiskCacheBlobEntry> blob_entry,
CacheStorageCache::EntryIndex disk_cache_index) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return CreateBlobWithSideData(std::move(blob_entry), disk_cache_index,
CacheStorageCache::INDEX_INVALID);
}
blink::mojom::SerializedBlobPtr
CacheStorageCacheEntryHandler::CreateBlobWithSideData(
scoped_refptr<DiskCacheBlobEntry> blob_entry,
CacheStorageCache::EntryIndex disk_cache_index,
CacheStorageCache::EntryIndex side_data_disk_cache_index) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto blob = blink::mojom::SerializedBlob::New();
blob->size = blob_entry->GetSize(disk_cache_index);
blob->uuid = base::Uuid::GenerateRandomV4().AsLowercaseString();
auto element = storage::mojom::BlobDataItem::New();
element->size = blob_entry->GetSize(disk_cache_index);
element->side_data_size =
side_data_disk_cache_index == CacheStorageCache::INDEX_INVALID
? 0
: blob_entry->GetSize(side_data_disk_cache_index);
element->type = storage::mojom::BlobDataItemType::kCacheStorage;
auto handle = std::make_unique<EntryReaderImpl>(
std::move(blob_entry), disk_cache_index, side_data_disk_cache_index);
mojo::MakeSelfOwnedReceiver(std::move(handle),
element->reader.InitWithNewPipeAndPassReceiver());
blob_storage_context_->context()->RegisterFromDataItem(
blob->blob.InitWithNewPipeAndPassReceiver(), blob->uuid,
std::move(element));
return blob;
}
} // namespace content