Commit 8157f7c3 authored by Kenichi Ishibashi's avatar Kenichi Ishibashi Committed by Commit Bot

Stop using AppCacheResponseWriter from ServiceWorkerResourceWriter

This CL removes AppCacheResponseReader dependency from
ServiceWorkerResourceWriter by re-implementing resource writing
logic. See [1] for the reasoning of the rewrite.

[1] https://docs.google.com/document/d/1UhdZnaeEP8GywZR0Gik6wF6jKmNY3je6aDhPfJ05YhU/edit?usp=sharing

This CL should not have behavior changes, and existing tests like
ServiceWorkerResourceStorageTest cover this change.

Bug: 1117369
Change-Id: I2fe4075ddff1c185768e0dd245e49f1ac5c26962
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2430687
Commit-Queue: Kenichi Ishibashi <bashi@chromium.org>
Reviewed-by: default avatarVictor Costan <pwnall@chromium.org>
Reviewed-by: default avatarMakoto Shimazu <shimazu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#814137}
parent e03ce6a7
......@@ -14,11 +14,6 @@ namespace content {
ServiceWorkerDiskCache::ServiceWorkerDiskCache()
: AppCacheDiskCache(/*use_simple_cache=*/true) {}
ServiceWorkerResponseWriter::ServiceWorkerResponseWriter(
int64_t resource_id,
base::WeakPtr<AppCacheDiskCache> disk_cache)
: AppCacheResponseWriter(resource_id, std::move(disk_cache)) {}
ServiceWorkerResponseMetadataWriter::ServiceWorkerResponseMetadataWriter(
int64_t resource_id,
base::WeakPtr<AppCacheDiskCache> disk_cache)
......
......@@ -26,18 +26,8 @@ class CONTENT_EXPORT ServiceWorkerDiskCache : public AppCacheDiskCache {
ServiceWorkerDiskCache();
};
// TODO(crbug.com/1060076): Migrate to
// storage::mojom::ServiceWorkerResourceWriter.
class CONTENT_EXPORT ServiceWorkerResponseWriter
: public AppCacheResponseWriter {
protected:
// Should only be constructed by the storage class.
friend class ServiceWorkerStorage;
ServiceWorkerResponseWriter(int64_t resource_id,
base::WeakPtr<AppCacheDiskCache> disk_cache);
};
// TODO(crbug.com/1117369): Migrate to
// storage::mojom::ServiceWorkerResourceMetadataWriter.
class CONTENT_EXPORT ServiceWorkerResponseMetadataWriter
: public AppCacheResponseMetadataWriter {
protected:
......
......@@ -47,6 +47,46 @@ network::mojom::URLResponseHeadPtr ConvertHttpResponseInfo(
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;
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_(std::move(pickle)) {
DCHECK(pickle_->data());
}
size_t size() const { return pickle_->size(); }
private:
~WrappedPickleIOBuffer() override = default;
const std::unique_ptr<const base::Pickle> pickle_;
};
} // namespace
// BigBuffer backed IOBuffer.
......@@ -84,6 +124,154 @@ mojo_base::BigBuffer BigIOBuffer::TakeBuffer() {
return std::move(buffer_);
}
DiskEntryManager::DiskEntryManager(int64_t resource_id,
base::WeakPtr<AppCacheDiskCache> disk_cache)
: resource_id_(resource_id), disk_cache_(std::move(disk_cache)) {
DCHECK_NE(resource_id_, blink::mojom::kInvalidServiceWorkerResourceId);
DCHECK(disk_cache_);
}
DiskEntryManager::~DiskEntryManager() {
if (entry_) {
entry_->Close();
}
}
void DiskEntryManager::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_ = nullptr;
RunEnsureEntryIsCreatedCallback();
return;
}
AppCacheDiskCacheEntry** entry_ptr = new AppCacheDiskCacheEntry*;
creation_phase_ = CreationPhase::kInitialAttempt;
int rv = disk_cache_->CreateEntry(
resource_id_, entry_ptr,
base::BindOnce(&DidCreateEntryForFirstAttempt, weak_factory_.GetWeakPtr(),
entry_ptr));
if (rv != net::ERR_IO_PENDING) {
DidCreateEntryForFirstAttempt(weak_factory_.GetWeakPtr(), entry_ptr, rv);
}
}
// static
void DiskEntryManager::DidCreateEntryForFirstAttempt(
base::WeakPtr<DiskEntryManager> entry_manager,
AppCacheDiskCacheEntry** entry,
int rv) {
if (!entry_manager) {
delete entry;
return;
}
DCHECK_EQ(entry_manager->creation_phase_, CreationPhase::kInitialAttempt);
DCHECK(!entry_manager->entry_);
if (!entry_manager->disk_cache_) {
delete entry;
entry_manager->entry_ = nullptr;
entry_manager->RunEnsureEntryIsCreatedCallback();
return;
}
if (rv != net::OK) {
// The first attempt to create an entry is failed. Try to overwrite the
// existing entry.
delete entry;
entry_manager->creation_phase_ = CreationPhase::kDoomExisting;
rv = entry_manager->disk_cache_->DoomEntry(
entry_manager->resource_id_,
base::BindOnce(&DiskEntryManager::DidDoomExistingEntry, entry_manager));
if (rv != net::ERR_IO_PENDING) {
DidDoomExistingEntry(entry_manager, rv);
}
return;
}
DCHECK(entry);
entry_manager->entry_ = *entry;
delete entry;
entry_manager->RunEnsureEntryIsCreatedCallback();
}
// static
void DiskEntryManager::DidDoomExistingEntry(
base::WeakPtr<DiskEntryManager> entry_manager,
int rv) {
if (!entry_manager) {
return;
}
DCHECK_EQ(entry_manager->creation_phase_, CreationPhase::kDoomExisting);
DCHECK(!entry_manager->entry_);
if (!entry_manager->disk_cache_) {
entry_manager->entry_ = nullptr;
entry_manager->RunEnsureEntryIsCreatedCallback();
return;
}
entry_manager->creation_phase_ = CreationPhase::kSecondAttempt;
auto** entry_ptr = new AppCacheDiskCacheEntry*;
rv = entry_manager->disk_cache_->CreateEntry(
entry_manager->resource_id_, entry_ptr,
base::BindOnce(&DiskEntryManager::DidCreateEntryForSecondAttempt,
entry_manager, entry_ptr));
if (rv != net::ERR_IO_PENDING) {
DidCreateEntryForSecondAttempt(entry_manager, entry_ptr, rv);
}
}
// static
void DiskEntryManager::DidCreateEntryForSecondAttempt(
base::WeakPtr<DiskEntryManager> entry_manager,
AppCacheDiskCacheEntry** entry,
int rv) {
if (!entry_manager) {
delete entry;
return;
}
DCHECK_EQ(entry_manager->creation_phase_, CreationPhase::kSecondAttempt);
if (!entry_manager->disk_cache_) {
delete entry;
entry_manager->entry_ = nullptr;
entry_manager->RunEnsureEntryIsCreatedCallback();
return;
}
if (rv != net::OK) {
// The second attempt is also failed. Give up creating an entry.
delete entry;
entry_manager->entry_ = nullptr;
entry_manager->RunEnsureEntryIsCreatedCallback();
return;
}
DCHECK(!entry_manager->entry_);
DCHECK(entry);
entry_manager->entry_ = *entry;
entry_manager->RunEnsureEntryIsCreatedCallback();
delete entry;
}
void DiskEntryManager::RunEnsureEntryIsCreatedCallback() {
creation_phase_ = CreationPhase::kDone;
std::move(ensure_entry_is_created_callback_).Run();
}
class ServiceWorkerResourceReaderImpl::DataReader {
public:
DataReader(
......@@ -495,39 +683,118 @@ void ServiceWorkerResourceReaderImpl::DidOpenEntry(
}
ServiceWorkerResourceWriterImpl::ServiceWorkerResourceWriterImpl(
std::unique_ptr<ServiceWorkerResponseWriter> writer)
: writer_(std::move(writer)) {
DCHECK(writer_);
}
int64_t resource_id,
base::WeakPtr<AppCacheDiskCache> disk_cache)
: entry_manager_(resource_id, std::move(disk_cache)) {}
ServiceWorkerResourceWriterImpl::~ServiceWorkerResourceWriterImpl() = default;
void ServiceWorkerResourceWriterImpl::WriteResponseHead(
network::mojom::URLResponseHeadPtr response_head,
WriteResponseHeadCallback callback) {
// Convert URLResponseHead to HttpResponseInfo.
auto response_info = std::make_unique<net::HttpResponseInfo>();
response_info->headers = response_head->headers;
if (response_head->ssl_info.has_value())
response_info->ssl_info = *response_head->ssl_info;
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;
auto info_buffer =
base::MakeRefCounted<HttpResponseInfoIOBuffer>(std::move(response_info));
writer_->WriteInfo(info_buffer.get(), std::move(callback));
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kIdle);
state_ = State::kWriteResponseHeadStarted;
#endif
entry_manager_.EnsureEntryIsCreated(
base::BindOnce(&ServiceWorkerResourceWriterImpl::WriteResponseHeadToEntry,
weak_factory_.GetWeakPtr(), std::move(response_head),
std::move(callback)));
}
void ServiceWorkerResourceWriterImpl::WriteData(mojo_base::BigBuffer data,
WriteDataCallback callback) {
int buf_len = data.size();
#if DCHECK_IS_ON()
DCHECK_EQ(state_, State::kIdle);
state_ = State::kWriteDataStarted;
#endif
entry_manager_.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_manager_.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_manager_.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_manager_.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));
writer_->WriteData(buffer.get(), buf_len, std::move(callback));
int rv = entry_manager_.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::
......
......@@ -13,6 +13,76 @@ namespace content {
class BigIOBuffer;
// Manages a service worker disk cache entry. Creates and owns an entry.
// TODO(bashi): Used by resource writers. Use in readers as well.
class DiskEntryManager {
public:
DiskEntryManager(int64_t resource_id,
base::WeakPtr<AppCacheDiskCache> disk_cache);
~DiskEntryManager();
DiskEntryManager(const DiskEntryManager&) = delete;
DiskEntryManager& operator=(const DiskEntryManager&) = delete;
// Can be nullptr when a disk cache error occurs.
AppCacheDiskCacheEntry* entry() {
DCHECK_EQ(creation_phase_, CreationPhase::kDone);
return entry_;
}
// Calls the callback when entry() is created and can be used.
//
// Overlapping calls are not allowed. Specifically, once the method is called,
// it must not be called again until it calls the callback.
//
// If necessary, kicks off the creation of a disk cache entry for the
// `resource_id` passed to the constructor. After the callback is called,
// `entry()` can be safely called to obtain the created entry.
//
// Has a retry mechanism. If the first attempt fails, dooms the existing
// entry, then tries to create an entry again.
void EnsureEntryIsCreated(base::OnceClosure callback);
private:
// State of creating a disk_cache entry.
enum class CreationPhase {
kNoAttempt,
kInitialAttempt,
kDoomExisting,
kSecondAttempt,
kDone,
};
// Callbacks of EnsureEntryIsCreated(). These are static to manage the
// ownership of AppCacheDiskCacheEntry correctly.
// TODO(crbug.com/586174): Refactor service worker's disk cache to use
// disk_cache::EntryResult to make these callbacks non-static.
static void DidCreateEntryForFirstAttempt(
base::WeakPtr<DiskEntryManager> entry_manager,
AppCacheDiskCacheEntry** entry,
int rv);
static void DidDoomExistingEntry(
base::WeakPtr<DiskEntryManager> entry_manager,
int rv);
static void DidCreateEntryForSecondAttempt(
base::WeakPtr<DiskEntryManager> entry_manager,
AppCacheDiskCacheEntry** entry,
int rv);
void RunEnsureEntryIsCreatedCallback();
const int64_t resource_id_;
base::WeakPtr<AppCacheDiskCache> disk_cache_;
AppCacheDiskCacheEntry* entry_ = nullptr;
CreationPhase creation_phase_ = CreationPhase::kNoAttempt;
// Stored as a data member to handle //net-style maybe-async methods.
base::OnceClosure ensure_entry_is_created_callback_;
base::WeakPtrFactory<DiskEntryManager> weak_factory_{this};
};
// The implementation of storage::mojom::ServiceWorkerResourceReader.
class ServiceWorkerResourceReaderImpl
: public storage::mojom::ServiceWorkerResourceReader {
......@@ -66,7 +136,7 @@ class ServiceWorkerResourceReaderImpl
// Used to read metadata from disk cache.
scoped_refptr<BigIOBuffer> metadata_buffer_;
// Holds the return value of ReadResponseHead(). Stored as a member field
// to handle net style maybe-async methods.
// to handle //net-style maybe-async methods.
network::mojom::URLResponseHeadPtr response_head_;
// Holds the callback of ReadResponseHead(). Stored as a member field to
// handle //net-style maybe-async methods.
......@@ -76,7 +146,7 @@ class ServiceWorkerResourceReaderImpl
std::unique_ptr<DataReader> data_reader_;
// Holds the callback of EnsureEntryIsOpen(). Stored as a data member to
// handle net style maybe-async methods.
// handle //net-style maybe-async methods.
base::OnceClosure open_entry_callback_;
#if DCHECK_IS_ON()
......@@ -95,15 +165,11 @@ class ServiceWorkerResourceReaderImpl
};
// The implementation of storage::mojom::ServiceWorkerResourceWriter.
// Currently this class is an adaptor that uses ServiceWorkerResponseWriter
// internally.
// TODO(crbug.com/1055677): Fork the implementation of
// ServiceWorkerResponseWriter and stop using it.
class ServiceWorkerResourceWriterImpl
: public storage::mojom::ServiceWorkerResourceWriter {
public:
explicit ServiceWorkerResourceWriterImpl(
std::unique_ptr<ServiceWorkerResponseWriter> writer);
ServiceWorkerResourceWriterImpl(int64_t resource_id,
base::WeakPtr<AppCacheDiskCache> disk_cache);
ServiceWorkerResourceWriterImpl(const ServiceWorkerResourceWriterImpl&) =
delete;
......@@ -112,14 +178,48 @@ class ServiceWorkerResourceWriterImpl
~ServiceWorkerResourceWriterImpl() override;
private:
// storage::mojom::ServiceWorkerResourceWriter implementations:
void WriteResponseHead(network::mojom::URLResponseHeadPtr response_head,
WriteResponseHeadCallback callback) override;
void WriteData(mojo_base::BigBuffer data,
WriteDataCallback callback) override;
const std::unique_ptr<ServiceWorkerResponseWriter> writer_;
private:
// Called while executing WriteResponseHead().
void WriteResponseHeadToEntry(
network::mojom::URLResponseHeadPtr response_head,
WriteResponseHeadCallback callback);
void DidWriteResponseHead(scoped_refptr<net::IOBuffer> buffer,
size_t write_amount,
int rv);
// Called while executing WriteData().
void WriteDataToEntry(mojo_base::BigBuffer data, WriteDataCallback callback);
void DidWriteData(scoped_refptr<net::IOBuffer> buffer,
size_t write_amount,
int rv);
DiskEntryManager entry_manager_;
// Points the current write position of WriteData().
size_t write_position_ = 0;
// Holds the callback of WriteResponseHead() or WriteData(). Stored as a data
// member to handle //net-style maybe-async methods.
net::CompletionOnceCallback write_callback_;
#if DCHECK_IS_ON()
enum class State {
kIdle,
kWriteResponseHeadStarted,
kWriteResponseHeadHasEntry,
kWriteDataStarted,
kWriteDataHasEntry,
};
State state_ = State::kIdle;
#endif // DCHECK_IS_ON()
base::WeakPtrFactory<ServiceWorkerResourceWriterImpl> weak_factory_{this};
};
// The implementation of storage::mojom::ServiceWorkerResourceMetadataWriter.
......
......@@ -592,10 +592,10 @@ ServiceWorkerStorage::CreateResourceReader(int64_t resource_id) {
resource_id, disk_cache()->GetWeakPtr());
}
std::unique_ptr<ServiceWorkerResponseWriter>
ServiceWorkerStorage::CreateResponseWriter(int64_t resource_id) {
return base::WrapUnique(
new ServiceWorkerResponseWriter(resource_id, disk_cache()->GetWeakPtr()));
std::unique_ptr<ServiceWorkerResourceWriterImpl>
ServiceWorkerStorage::CreateResourceWriter(int64_t resource_id) {
return std::make_unique<ServiceWorkerResourceWriterImpl>(
resource_id, disk_cache()->GetWeakPtr());
}
std::unique_ptr<ServiceWorkerResponseMetadataWriter>
......
......@@ -40,7 +40,6 @@ namespace content {
class ServiceWorkerDiskCache;
class ServiceWorkerResponseMetadataWriter;
class ServiceWorkerResponseWriter;
class ServiceWorkerStorageControlImplTest;
namespace service_worker_storage_unittest {
......@@ -95,10 +94,6 @@ class CONTENT_EXPORT ServiceWorkerStorage {
int64_t deleted_version_id,
const std::vector<int64_t>& newly_purgeable_resources)>;
using ResponseWriterCreationCallback = base::OnceCallback<void(
int64_t resource_id,
std::unique_ptr<ServiceWorkerResponseWriter> response_writer)>;
using DatabaseStatusCallback =
base::OnceCallback<void(ServiceWorkerDatabase::Status status)>;
using GetUserDataInDBCallback =
......@@ -191,7 +186,7 @@ class CONTENT_EXPORT ServiceWorkerStorage {
// associated with the disabled disk cache if the storage is disabled.
std::unique_ptr<ServiceWorkerResourceReaderImpl> CreateResourceReader(
int64_t resource_id);
std::unique_ptr<ServiceWorkerResponseWriter> CreateResponseWriter(
std::unique_ptr<ServiceWorkerResourceWriterImpl> CreateResourceWriter(
int64_t resource_id);
std::unique_ptr<ServiceWorkerResponseMetadataWriter>
CreateResponseMetadataWriter(int64_t resource_id);
......
......@@ -262,8 +262,7 @@ void ServiceWorkerStorageControlImpl::CreateResourceWriter(
int64_t resource_id,
mojo::PendingReceiver<storage::mojom::ServiceWorkerResourceWriter> writer) {
DCHECK_NE(resource_id, blink::mojom::kInvalidServiceWorkerResourceId);
mojo::MakeSelfOwnedReceiver(std::make_unique<ServiceWorkerResourceWriterImpl>(
storage_->CreateResponseWriter(resource_id)),
mojo::MakeSelfOwnedReceiver(storage_->CreateResourceWriter(resource_id),
std::move(writer));
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment