blob: 3d505ab5ecf97acd0dab169f792b6073e15201e0 [file] [log] [blame]
// Copyright 2023 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/webapps/isolated_web_apps/reading/response_reader.h"
#include <memory>
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/types/expected.h"
#include "base/types/expected_macros.h"
#include "components/web_package/mojom/web_bundle_parser.mojom-forward.h"
#include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
#include "components/webapps/isolated_web_apps/reading/signed_web_bundle_reader.h"
#include "services/network/public/cpp/resource_request.h"
#include "url/gurl.h"
namespace web_app {
namespace {
network::ResourceRequest RemoveQuery(
network::ResourceRequest resource_request) {
GURL::Replacements replacements;
replacements.ClearQuery();
resource_request.url = resource_request.url.ReplaceComponents(replacements);
return resource_request;
}
} // namespace
IsolatedWebAppResponseReaderImpl::IsolatedWebAppResponseReaderImpl(
std::unique_ptr<SignedWebBundleReader> reader,
content::BrowserContext* browser_context)
: reader_(std::move(reader)), browser_context_(*browser_context) {}
IsolatedWebAppResponseReaderImpl::~IsolatedWebAppResponseReaderImpl() = default;
web_package::SignedWebBundleIntegrityBlock
IsolatedWebAppResponseReaderImpl::GetIntegrityBlock() {
return reader_->GetIntegrityBlock();
}
void IsolatedWebAppResponseReaderImpl::ReadResponse(
const network::ResourceRequest& resource_request,
ReadResponseCallback callback) {
// Remove query parameters from the request URL, if it has any. Resources
// within Signed Web Bundles used for Isolated Web Apps never have username,
// password, or fragment, just like resources within Signed Web Bundles and
// normal Web Bundles. Removing these from request URLs is done by the
// `SignedWebBundleReader`. However, in addition, resources in Signed Web
// Bundles used for Isolated Web Apps can also never have query parameters,
// which we need to remove here.
//
// Conceptually, we treat the resources in Signed Web Bundles for Isolated Web
// Apps more like files served by a file server (which also strips query
// parameters before looking up the file), and not like HTTP exchanges like
// they are used for Signed Exchanges (SXG).
reader_->ReadResponse(
resource_request.url.has_query() ? RemoveQuery(resource_request)
: resource_request,
base::BindOnce(&IsolatedWebAppResponseReaderImpl::OnResponseRead,
// `base::Unretained` is safe to use here, since `this`
// owns `reader_`.
base::Unretained(this), std::move(callback)));
}
void IsolatedWebAppResponseReaderImpl::OnResponseRead(
ReadResponseCallback callback,
base::expected<web_package::mojom::BundleResponsePtr,
SignedWebBundleReader::ReadResponseError> response_head) {
std::move(callback).Run(
std::move(response_head)
.transform([this](web_package::mojom::BundleResponsePtr ptr) {
// Since `this` owns `reader_`, we only pass a weak pointer to it to
// the `Response` object. If `this` is deleted, it makes sense that
// the pointer to the `reader_` contained in `Response` also becomes
// invalid.
return Response(std::move(ptr), reader_->AsWeakPtr());
})
.transform_error(&Error::FromSignedWebBundleReaderError));
}
void IsolatedWebAppResponseReaderImpl::Close(base::OnceClosure callback) {
reader_->Close(base::BindOnce(&IsolatedWebAppResponseReaderImpl::OnClosed,
base::Unretained(this), std::move(callback)));
}
void IsolatedWebAppResponseReaderImpl::OnClosed(base::OnceClosure callback) {
std::move(callback).Run();
}
IsolatedWebAppResponseReader::Response::Response(
web_package::mojom::BundleResponsePtr head,
base::WeakPtr<SignedWebBundleReader> reader)
: head_(std::move(head)), reader_(std::move(reader)) {}
IsolatedWebAppResponseReader::Response::Response(Response&&) = default;
IsolatedWebAppResponseReader::Response&
IsolatedWebAppResponseReader::Response::operator=(Response&&) = default;
IsolatedWebAppResponseReader::Response::~Response() = default;
void IsolatedWebAppResponseReader::Response::ReadBody(
mojo::ScopedDataPipeProducerHandle producer_handle,
base::OnceCallback<void(net::Error net_error)> callback) {
if (!reader_ || reader_->IsClosed()) {
// The weak pointer to `reader_` might no longer be valid when this is
// called. Also the reader might be closed.
std::move(callback).Run(net::ERR_FAILED);
return;
}
reader_->ReadResponseBody(head_->Clone(), std::move(producer_handle),
std::move(callback));
}
// static
IsolatedWebAppResponseReader::Error
IsolatedWebAppResponseReader::Error::FromSignedWebBundleReaderError(
const SignedWebBundleReader::ReadResponseError& error) {
using Type = SignedWebBundleReader::ReadResponseError::Type;
switch (error.type) {
case Type::kFormatError:
return Error(Error::Type::kFormatError, error.message);
case Type::kParserInternalError:
return Error(Error::Type::kParserInternalError, error.message);
case Type::kResponseNotFound:
return Error(Error::Type::kResponseNotFound, error.message);
}
}
} // namespace web_app