[go: nahoru, domu]

blob: ad95a0789bd04fbf0ecd995554979191fdafe200 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/services/storage/service_worker/service_worker_resource_ops.h"
#include "base/numerics/checked_math.h"
#include "base/pickle.h"
#include "base/task/sequenced_task_runner.h"
#include "components/services/storage/public/cpp/big_io_buffer.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "net/http/http_response_info.h"
#include "services/network/public/cpp/net_adapters.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/common/blob/blob_utils.h"
namespace storage {
namespace {
// Disk cache entry data indices.
//
// This enum pertains to data persisted on disk. Do not remove or reuse values.
enum {
kResponseInfoIndex = 0,
kResponseContentIndex = 1,
kResponseMetadataIndex = 2,
};
// Convert an HttpResponseInfo retrieved from disk_cache to URLResponseHead.
network::mojom::URLResponseHeadPtr ConvertHttpResponseInfo(
const net::HttpResponseInfo& http_info,
int64_t response_data_size) {
auto response_head = network::mojom::URLResponseHead::New();
response_head->request_time = http_info.request_time;
response_head->response_time = http_info.response_time;
response_head->headers = http_info.headers;
response_head->headers->GetMimeType(&response_head->mime_type);
response_head->headers->GetCharset(&response_head->charset);
response_head->content_length = response_data_size;
response_head->was_fetched_via_spdy = http_info.was_fetched_via_spdy;
response_head->was_alpn_negotiated = http_info.was_alpn_negotiated;
response_head->connection_info = http_info.connection_info;
response_head->alpn_negotiated_protocol = http_info.alpn_negotiated_protocol;
response_head->remote_endpoint = http_info.remote_endpoint;
response_head->cert_status = http_info.ssl_info.cert_status;
// See ConvertToPickle(), where an invalid ssl_info is put into storage in
// case of |response_head->ssl_info| being nullptr. Here we restore
// |response_head->ssl_info| to nullptr if we got the invalid ssl_info from
// storage.
if (http_info.ssl_info.is_valid())
response_head->ssl_info = http_info.ssl_info;
return response_head;
}
// Convert a URLResponseHead to base::Pickle. Used to persist the response to
// disk.
std::unique_ptr<base::Pickle> ConvertToPickle(
network::mojom::URLResponseHeadPtr response_head) {
net::HttpResponseInfo response_info;
response_info.headers = response_head->headers;
if (response_head->ssl_info.has_value()) {
response_info.ssl_info = *response_head->ssl_info;
DCHECK(response_info.ssl_info.is_valid());
}
response_info.was_fetched_via_spdy = response_head->was_fetched_via_spdy;
response_info.was_alpn_negotiated = response_head->was_alpn_negotiated;
response_info.alpn_negotiated_protocol =
response_head->alpn_negotiated_protocol;
response_info.connection_info = response_head->connection_info;
response_info.remote_endpoint = response_head->remote_endpoint;
response_info.response_time = response_head->response_time;
const bool kSkipTransientHeaders = true;
const bool kTruncated = false;
auto pickle = std::make_unique<base::Pickle>();
response_info.Persist(pickle.get(), kSkipTransientHeaders, kTruncated);
return pickle;
}
// An IOBuffer that wraps a pickle's data. Used to write URLResponseHead.
class WrappedPickleIOBuffer : public net::WrappedIOBuffer {
public:
explicit WrappedPickleIOBuffer(std::unique_ptr<const base::Pickle> pickle)
: net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle->data()),
pickle->size()),
pickle_(std::move(pickle)) {
DCHECK(pickle_->data());
}
size_t size() const { return pickle_->size(); }
private:
~WrappedPickleIOBuffer() override {
// `data_` is a pointer on `pickle_` and should be nullified before that to
// prevent it from being dangling.
data_ = nullptr;
}
const std::unique_ptr<const base::Pickle> pickle_;
};
} // namespace
DiskEntryCreator::DiskEntryCreator(
int64_t resource_id,
base::WeakPtr<ServiceWorkerDiskCache> disk_cache)
: resource_id_(resource_id), disk_cache_(std::move(disk_cache)) {
DCHECK_NE(resource_id_, blink::mojom::kInvalidServiceWorkerResourceId);
DCHECK(disk_cache_);
}
DiskEntryCreator::~DiskEntryCreator() = default;
void DiskEntryCreator::EnsureEntryIsCreated(base::OnceClosure callback) {
DCHECK(creation_phase_ == CreationPhase::kNoAttempt ||
creation_phase_ == CreationPhase::kDone);
DCHECK(!ensure_entry_is_created_callback_);
ensure_entry_is_created_callback_ = std::move(callback);
if (entry_) {
RunEnsureEntryIsCreatedCallback();
return;
}
if (!disk_cache_) {
entry_.reset();
RunEnsureEntryIsCreatedCallback();
return;
}
creation_phase_ = CreationPhase::kInitialAttempt;
disk_cache_->CreateEntry(
resource_id_,
base::BindOnce(&DiskEntryCreator::DidCreateEntryForFirstAttempt,
weak_factory_.GetWeakPtr()));
}
void DiskEntryCreator::DidCreateEntryForFirstAttempt(
int rv,
std::unique_ptr<ServiceWorkerDiskCacheEntry> entry) {
DCHECK_EQ(creation_phase_, CreationPhase::kInitialAttempt);
DCHECK(!entry_);
if (!disk_cache_) {
entry_.reset();
RunEnsureEntryIsCreatedCallback();
return;
}
if (rv != net::OK) {
// The first attempt to create an entry is failed. Try to overwrite the
// existing entry.
creation_phase_ = CreationPhase::kDoomExisting;
disk_cache_->DoomEntry(
resource_id_, base::BindOnce(&DiskEntryCreator::DidDoomExistingEntry,
weak_factory_.GetWeakPtr()));
return;
}
DCHECK(entry);
entry_ = std::move(entry);
RunEnsureEntryIsCreatedCallback();
}
void DiskEntryCreator::DidDoomExistingEntry(int rv) {
DCHECK_EQ(creation_phase_, CreationPhase::kDoomExisting);
DCHECK(!entry_);
if (!disk_cache_) {
entry_.reset();
RunEnsureEntryIsCreatedCallback();
return;
}
creation_phase_ = CreationPhase::kSecondAttempt;
disk_cache_->CreateEntry(
resource_id_,
base::BindOnce(&DiskEntryCreator::DidCreateEntryForSecondAttempt,
weak_factory_.GetWeakPtr()));
}
void DiskEntryCreator::DidCreateEntryForSecondAttempt(
int rv,
std::unique_ptr<ServiceWorkerDiskCacheEntry> entry) {
DCHECK_EQ(creation_phase_, CreationPhase::kSecondAttempt);
if (!disk_cache_) {
entry_.reset();
RunEnsureEntryIsCreatedCallback();
return;
}
if (rv != net::OK) {
// The second attempt is also failed. Give up creating an entry.
entry_.reset();
RunEnsureEntryIsCreatedCallback();
return;
}
DCHECK(!entry_);
DCHECK(entry);
entry_ = std::move(entry);
RunEnsureEntryIsCreatedCallback();
}
void DiskEntryCreator::RunEnsureEntryIsCreatedCallback() {
creation_phase_ = CreationPhase::kDone;
std::move(ensure_entry_is_created_callback_).Run();
}
DiskEntryOpener::DiskEntryOpener(
int64_t resource_id,
base::WeakPtr<ServiceWorkerDiskCache> disk_cache)
: resource_id_(resource_id), disk_cache_(std::move(disk_cache)) {
DCHECK_NE(resource_id_, blink::mojom::kInvalidServiceWorkerResourceId);
DCHECK(disk_cache_);
}
DiskEntryOpener::~DiskEntryOpener() = default;
void DiskEntryOpener::EnsureEntryIsOpen(base::OnceClosure callback) {
if (entry_) {
std::move(callback).Run();
return;
}
if (!disk_cache_) {
std::move(callback).Run();
return;
}
disk_cache_->OpenEntry(
resource_id_,
base::BindOnce(&DiskEntryOpener::DidOpenEntry, weak_factory_.GetWeakPtr(),
std::move(callback)));
}
void DiskEntryOpener::DidOpenEntry(
base::OnceClosure callback,
int rv,
std::unique_ptr<ServiceWorkerDiskCacheEntry> entry) {
if (!entry_ && rv == net::OK) {
DCHECK(entry);
entry_ = std::move(entry);
}
std::move(callback).Run();
}
class ServiceWorkerResourceReaderImpl::DataReader {
public:
DataReader(base::WeakPtr<ServiceWorkerResourceReaderImpl> owner,
size_t total_bytes_to_read,
mojo::ScopedDataPipeProducerHandle producer_handle)
: owner_(std::move(owner)),
total_bytes_to_read_(total_bytes_to_read),
producer_handle_(std::move(producer_handle)),
watcher_(FROM_HERE,
mojo::SimpleWatcher::ArmingPolicy::MANUAL,
base::SequencedTaskRunner::GetCurrentDefault()) {
DCHECK(owner_);
}
~DataReader() = default;
DataReader(const DataReader&) = delete;
DataReader operator=(const DataReader&) = delete;
void Start(ReadDataCallback callback) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kInitialized);
state_ = State::kStarted;
#endif
DCHECK(!callback_);
callback_ = std::move(callback);
owner_->entry_opener_.EnsureEntryIsOpen(base::BindOnce(
&DataReader::ContinueReadData, weak_factory_.GetWeakPtr()));
}
private:
void ContinueReadData() {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kStarted);
state_ = State::kCacheEntryOpened;
#endif
if (!owner_) {
Complete(net::ERR_ABORTED);
return;
}
if (!owner_->entry_opener_.entry()) {
Complete(net::ERR_CACHE_MISS);
return;
}
watcher_.Watch(producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
base::BindRepeating(&DataReader::OnWritable,
weak_factory_.GetWeakPtr()));
watcher_.ArmOrNotify();
}
void OnWritable(MojoResult) {
#if DCHECK_IS_ON()
DCHECK(state_ == State::kCacheEntryOpened || state_ == State::kDataRead);
state_ = State::kProducerWritable;
#endif
DCHECK(producer_handle_.is_valid());
DCHECK(!pending_buffer_);
if (!owner_ || !owner_->entry_opener_.entry()) {
Complete(net::ERR_ABORTED);
return;
}
MojoResult rv = network::NetToMojoPendingBuffer::BeginWrite(
&producer_handle_, &pending_buffer_);
switch (rv) {
case MOJO_RESULT_INVALID_ARGUMENT:
case MOJO_RESULT_BUSY:
NOTREACHED();
return;
case MOJO_RESULT_FAILED_PRECONDITION:
Complete(net::ERR_ABORTED);
return;
case MOJO_RESULT_SHOULD_WAIT:
watcher_.ArmOrNotify();
return;
case MOJO_RESULT_OK:
// |producer__handle_| must have been taken by |pending_buffer_|.
DCHECK(pending_buffer_);
DCHECK(!producer_handle_.is_valid());
break;
}
uint32_t num_bytes = pending_buffer_->size();
num_bytes = std::min(num_bytes, blink::BlobUtils::GetDataPipeChunkSize());
scoped_refptr<network::NetToMojoIOBuffer> buffer =
base::MakeRefCounted<network::NetToMojoIOBuffer>(pending_buffer_.get());
net::IOBuffer* raw_buffer = buffer.get();
int read_bytes = owner_->entry_opener_.entry()->Read(
kResponseContentIndex, current_bytes_read_, raw_buffer, num_bytes,
base::BindOnce(&DataReader::DidReadData, weak_factory_.GetWeakPtr(),
buffer));
if (read_bytes != net::ERR_IO_PENDING) {
DidReadData(std::move(buffer), read_bytes);
}
}
void DidReadData(scoped_refptr<network::NetToMojoIOBuffer> buffer,
int read_bytes) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kProducerWritable);
state_ = State::kDataRead;
#endif
if (read_bytes < 0) {
Complete(read_bytes);
return;
}
producer_handle_ = pending_buffer_->Complete(read_bytes);
DCHECK(producer_handle_.is_valid());
pending_buffer_.reset();
current_bytes_read_ += read_bytes;
if (read_bytes == 0 || current_bytes_read_ == total_bytes_to_read_) {
// All data has been read.
Complete(current_bytes_read_);
return;
}
watcher_.ArmOrNotify();
}
void Complete(int status) {
#if DCHECK_IS_ON()
DCHECK_NE(state_, State::kInitialized);
DCHECK_NE(state_, State::kComplete);
state_ = State::kComplete;
#endif
watcher_.Cancel();
producer_handle_.reset();
if (callback_) {
std::move(callback_).Run(status);
}
if (owner_) {
owner_->DidReadDataComplete();
}
}
base::WeakPtr<ServiceWorkerResourceReaderImpl> owner_;
const size_t total_bytes_to_read_;
size_t current_bytes_read_ = 0;
ReadDataCallback callback_;
mojo::ScopedDataPipeProducerHandle producer_handle_;
mojo::SimpleWatcher watcher_;
scoped_refptr<network::NetToMojoPendingBuffer> pending_buffer_;
#if DCHECK_IS_ON()
enum class State {
kInitialized,
kStarted,
kCacheEntryOpened,
kProducerWritable,
kDataRead,
kComplete,
};
State state_ = State::kInitialized;
#endif // DCHECK_IS_ON()
base::WeakPtrFactory<DataReader> weak_factory_{this};
};
ServiceWorkerResourceReaderImpl::ServiceWorkerResourceReaderImpl(
int64_t resource_id,
base::WeakPtr<ServiceWorkerDiskCache> disk_cache,
mojo::PendingReceiver<mojom::ServiceWorkerResourceReader> receiver,
base::OnceClosure disconnect_handler)
: entry_opener_(resource_id, std::move(disk_cache)),
receiver_(this, std::move(receiver)) {
receiver_.set_disconnect_handler(std::move(disconnect_handler));
}
ServiceWorkerResourceReaderImpl::~ServiceWorkerResourceReaderImpl() = default;
void ServiceWorkerResourceReaderImpl::ReadResponseHead(
ReadResponseHeadCallback callback) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kIdle);
state_ = State::kReadResponseHeadStarted;
#endif
DCHECK(!read_response_head_callback_) << __func__ << " already called";
DCHECK(!response_head_) << " another ReadResponseHead() in progress";
DCHECK(!metadata_buffer_);
DCHECK(!data_reader_);
read_response_head_callback_ = std::move(callback);
entry_opener_.EnsureEntryIsOpen(
base::BindOnce(&ServiceWorkerResourceReaderImpl::ContinueReadResponseHead,
weak_factory_.GetWeakPtr()));
}
void ServiceWorkerResourceReaderImpl::PrepareReadData(
int64_t size,
PrepareReadDataCallback callback) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kIdle);
state_ = State::kReadDataPrepared;
#endif
DCHECK(!read_response_head_callback_) << "ReadResponseHead() being operating";
DCHECK(!response_head_);
DCHECK(!metadata_buffer_);
DCHECK(!data_reader_);
MojoCreateDataPipeOptions options;
options.struct_size = sizeof(MojoCreateDataPipeOptions);
options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE;
options.element_num_bytes = 1;
options.capacity_num_bytes = blink::BlobUtils::GetDataPipeCapacity(size);
mojo::ScopedDataPipeConsumerHandle consumer_handle;
mojo::ScopedDataPipeProducerHandle producer_handle;
MojoResult rv =
mojo::CreateDataPipe(&options, producer_handle, consumer_handle);
if (rv != MOJO_RESULT_OK) {
std::move(callback).Run(mojo::ScopedDataPipeConsumerHandle());
return;
}
data_reader_ = std::make_unique<DataReader>(weak_factory_.GetWeakPtr(), size,
std::move(producer_handle));
std::move(callback).Run(std::move(consumer_handle));
}
void ServiceWorkerResourceReaderImpl::ReadData(ReadDataCallback callback) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kReadDataPrepared);
state_ = State::kReadDataStarted;
#endif
data_reader_->Start(std::move(callback));
}
void ServiceWorkerResourceReaderImpl::ContinueReadResponseHead() {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kReadResponseHeadStarted);
state_ = State::kCacheEntryOpened;
#endif
DCHECK(read_response_head_callback_);
if (!entry_opener_.entry()) {
FailReadResponseHead(net::ERR_CACHE_MISS);
return;
}
int64_t size = entry_opener_.entry()->GetSize(kResponseInfoIndex);
if (size <= 0) {
FailReadResponseHead(net::ERR_CACHE_MISS);
return;
}
auto buffer =
base::MakeRefCounted<net::IOBuffer>(base::checked_cast<size_t>(size));
int rv = entry_opener_.entry()->Read(
kResponseInfoIndex, /*offset=*/0, buffer.get(), size,
base::BindOnce(&ServiceWorkerResourceReaderImpl::DidReadHttpResponseInfo,
weak_factory_.GetWeakPtr(), buffer));
if (rv != net::ERR_IO_PENDING) {
DidReadHttpResponseInfo(std::move(buffer), rv);
}
}
void ServiceWorkerResourceReaderImpl::DidReadHttpResponseInfo(
scoped_refptr<net::IOBuffer> buffer,
int status) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kCacheEntryOpened);
state_ = State::kResponseInfoRead;
#endif
DCHECK(read_response_head_callback_);
DCHECK(entry_opener_.entry());
if (status < 0) {
FailReadResponseHead(status);
return;
}
// Deserialize the http info structure, ensuring we got headers.
base::Pickle pickle(buffer->data(), status);
auto http_info = std::make_unique<net::HttpResponseInfo>();
bool response_truncated = false;
if (!http_info->InitFromPickle(pickle, &response_truncated) ||
!http_info->headers.get()) {
FailReadResponseHead(net::ERR_FAILED);
return;
}
DCHECK(!response_truncated);
int64_t response_data_size =
entry_opener_.entry()->GetSize(kResponseContentIndex);
response_head_ = ConvertHttpResponseInfo(*http_info, response_data_size);
int64_t metadata_size =
entry_opener_.entry()->GetSize(kResponseMetadataIndex);
DCHECK_GE(metadata_size, 0);
if (metadata_size <= 0) {
CompleteReadResponseHead(status);
return;
}
// Read metadata.
metadata_buffer_ = base::MakeRefCounted<BigIOBuffer>(
mojo_base::BigBuffer(base::checked_cast<size_t>(metadata_size)));
int rv = entry_opener_.entry()->Read(
kResponseMetadataIndex, /*offset=*/0, metadata_buffer_.get(),
metadata_size,
base::BindOnce(&ServiceWorkerResourceReaderImpl::DidReadMetadata,
weak_factory_.GetWeakPtr()));
if (rv != net::ERR_IO_PENDING) {
DidReadMetadata(rv);
}
}
void ServiceWorkerResourceReaderImpl::DidReadMetadata(int status) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kResponseInfoRead);
state_ = State::kMetadataRead;
#endif
DCHECK(read_response_head_callback_);
DCHECK(metadata_buffer_);
if (status < 0) {
FailReadResponseHead(status);
return;
}
CompleteReadResponseHead(status);
}
void ServiceWorkerResourceReaderImpl::FailReadResponseHead(int status) {
DCHECK_NE(net::OK, status);
response_head_ = nullptr;
metadata_buffer_ = nullptr;
CompleteReadResponseHead(status);
}
void ServiceWorkerResourceReaderImpl::CompleteReadResponseHead(int status) {
#if DCHECK_IS_ON()
DCHECK_NE(state_, State::kIdle);
state_ = State::kIdle;
#endif
DCHECK(read_response_head_callback_);
absl::optional<mojo_base::BigBuffer> metadata =
metadata_buffer_
? absl::optional<mojo_base::BigBuffer>(metadata_buffer_->TakeBuffer())
: absl::nullopt;
metadata_buffer_ = nullptr;
std::move(read_response_head_callback_)
.Run(status, std::move(response_head_), std::move(metadata));
}
void ServiceWorkerResourceReaderImpl::DidReadDataComplete() {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kReadDataStarted);
state_ = State::kIdle;
#endif
DCHECK(data_reader_);
data_reader_.reset();
}
ServiceWorkerResourceWriterImpl::ServiceWorkerResourceWriterImpl(
int64_t resource_id,
base::WeakPtr<ServiceWorkerDiskCache> disk_cache,
mojo::PendingReceiver<mojom::ServiceWorkerResourceWriter> receiver,
base::OnceClosure disconnect_handler)
: entry_creator_(resource_id, std::move(disk_cache)),
receiver_(this, std::move(receiver)) {
receiver_.set_disconnect_handler(std::move(disconnect_handler));
}
ServiceWorkerResourceWriterImpl::~ServiceWorkerResourceWriterImpl() = default;
void ServiceWorkerResourceWriterImpl::WriteResponseHead(
network::mojom::URLResponseHeadPtr response_head,
WriteResponseHeadCallback callback) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kIdle);
state_ = State::kWriteResponseHeadStarted;
#endif
entry_creator_.EnsureEntryIsCreated(
base::BindOnce(&ServiceWorkerResourceWriterImpl::WriteResponseHeadToEntry,
weak_factory_.GetWeakPtr(), std::move(response_head),
std::move(callback)));
}
void ServiceWorkerResourceWriterImpl::WriteData(mojo_base::BigBuffer data,
WriteDataCallback callback) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kIdle);
state_ = State::kWriteDataStarted;
#endif
entry_creator_.EnsureEntryIsCreated(base::BindOnce(
&ServiceWorkerResourceWriterImpl::WriteDataToEntry,
weak_factory_.GetWeakPtr(), std::move(data), std::move(callback)));
}
void ServiceWorkerResourceWriterImpl::WriteResponseHeadToEntry(
network::mojom::URLResponseHeadPtr response_head,
WriteResponseHeadCallback callback) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kWriteResponseHeadStarted);
state_ = State::kWriteResponseHeadHasEntry;
#endif
if (!entry_creator_.entry()) {
std::move(callback).Run(net::ERR_FAILED);
return;
}
DCHECK(!write_callback_);
write_callback_ = std::move(callback);
std::unique_ptr<const base::Pickle> pickle =
ConvertToPickle(std::move(response_head));
auto buffer = base::MakeRefCounted<WrappedPickleIOBuffer>(std::move(pickle));
size_t write_amount = buffer->size();
int rv = entry_creator_.entry()->Write(
kResponseInfoIndex, /*offset=*/0, buffer.get(), write_amount,
base::BindOnce(&ServiceWorkerResourceWriterImpl::DidWriteResponseHead,
weak_factory_.GetWeakPtr(), buffer, write_amount));
if (rv != net::ERR_IO_PENDING) {
DidWriteResponseHead(std::move(buffer), write_amount, rv);
}
}
void ServiceWorkerResourceWriterImpl::DidWriteResponseHead(
scoped_refptr<net::IOBuffer> buffer,
size_t write_amount,
int rv) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kWriteResponseHeadHasEntry);
state_ = State::kIdle;
#endif
DCHECK(write_callback_);
DCHECK(rv < 0 || base::checked_cast<size_t>(rv) == write_amount);
std::move(write_callback_).Run(rv);
}
void ServiceWorkerResourceWriterImpl::WriteDataToEntry(
mojo_base::BigBuffer data,
WriteDataCallback callback) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kWriteDataStarted);
state_ = State::kWriteDataHasEntry;
#endif
if (!entry_creator_.entry()) {
std::move(callback).Run(net::ERR_FAILED);
return;
}
DCHECK(!write_callback_);
write_callback_ = std::move(callback);
size_t write_amount = data.size();
auto buffer = base::MakeRefCounted<BigIOBuffer>(std::move(data));
int rv = entry_creator_.entry()->Write(
kResponseContentIndex, write_position_, buffer.get(), write_amount,
base::BindOnce(&ServiceWorkerResourceWriterImpl::DidWriteData,
weak_factory_.GetWeakPtr(), buffer, write_amount));
if (rv != net::ERR_IO_PENDING) {
DidWriteData(std::move(buffer), write_amount, rv);
}
}
void ServiceWorkerResourceWriterImpl::DidWriteData(
scoped_refptr<net::IOBuffer> buffer,
size_t write_amount,
int rv) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kWriteDataHasEntry);
state_ = State::kIdle;
#endif
DCHECK(write_callback_);
if (rv >= 0) {
DCHECK(base::checked_cast<size_t>(rv) == write_amount);
write_position_ += write_amount;
}
std::move(write_callback_).Run(rv);
}
ServiceWorkerResourceMetadataWriterImpl::
ServiceWorkerResourceMetadataWriterImpl(
int64_t resource_id,
base::WeakPtr<ServiceWorkerDiskCache> disk_cache,
mojo::PendingReceiver<mojom::ServiceWorkerResourceMetadataWriter>
receiver,
base::OnceClosure disconnect_handler)
: entry_opener_(resource_id, std::move(disk_cache)),
receiver_(this, std::move(receiver)) {
receiver_.set_disconnect_handler(std::move(disconnect_handler));
}
ServiceWorkerResourceMetadataWriterImpl::
~ServiceWorkerResourceMetadataWriterImpl() = default;
void ServiceWorkerResourceMetadataWriterImpl::WriteMetadata(
mojo_base::BigBuffer data,
WriteMetadataCallback callback) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kIdle);
state_ = State::kWriteMetadataStarted;
#endif
entry_opener_.EnsureEntryIsOpen(base::BindOnce(
&ServiceWorkerResourceMetadataWriterImpl::ContinueWriteMetadata,
weak_factory_.GetWeakPtr(), std::move(data), std::move(callback)));
}
void ServiceWorkerResourceMetadataWriterImpl::ContinueWriteMetadata(
mojo_base::BigBuffer data,
WriteMetadataCallback callback) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kWriteMetadataStarted);
state_ = State::kWriteMetadataHasEntry;
#endif
if (!entry_opener_.entry()) {
std::move(callback).Run(net::ERR_FAILED);
return;
}
DCHECK(!write_metadata_callback_);
write_metadata_callback_ = std::move(callback);
size_t write_amount = data.size();
auto buffer = base::MakeRefCounted<BigIOBuffer>(std::move(data));
int rv = entry_opener_.entry()->Write(
kResponseMetadataIndex, /*offset=*/0, buffer.get(), write_amount,
base::BindOnce(&ServiceWorkerResourceMetadataWriterImpl::DidWriteMetadata,
weak_factory_.GetWeakPtr(), buffer, write_amount));
if (rv != net::ERR_IO_PENDING) {
DidWriteMetadata(std::move(buffer), write_amount, rv);
}
}
void ServiceWorkerResourceMetadataWriterImpl::DidWriteMetadata(
scoped_refptr<net::IOBuffer> buffer,
size_t write_amount,
int rv) {
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kWriteMetadataHasEntry);
state_ = State::kIdle;
#endif
DCHECK(rv < 0 || base::checked_cast<size_t>(rv) == write_amount);
DCHECK(write_metadata_callback_);
std::move(write_metadata_callback_).Run(rv);
}
} // namespace storage