blob: ad722b67ceedcc7a4c21905908b3ce5c8b3267ca [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 "content/browser/loader/keep_alive_url_loader_service.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/loader/keep_alive_url_loader.h"
#include "content/browser/url_loader_factory_getter.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "third_party/blink/public/common/features.h"
namespace content {
namespace {
// A Context for the receiver of a KeepAliveURLLoaderFactory connection between
// a renderer and the browser.
//
// See `mojo::ReceiverSetBase` for more details.
struct BindContext {
explicit BindContext(scoped_refptr<network::SharedURLLoaderFactory> factory)
: factory(factory) {}
explicit BindContext(const std::unique_ptr<BindContext>& other)
: factory(other->factory) {}
~BindContext() = default;
// A refptr to the factory to use for the requests initiated from this
// context.
scoped_refptr<network::SharedURLLoaderFactory> factory;
// This must be the last member.
base::WeakPtrFactory<BindContext> weak_ptr_factory{this};
};
} // namespace
// A URLLoaderFactory to handle fetch keepalive requests.
//
// This factory can handle requests from multiple remotes of URLLoaderFactory.
// Users should call `BindFactory()` first to register a pending receiver with
// this factory.
//
// On requested by a remote, i.e. calling
// `network::mojom::URLLoaderFactory::CreateLoaderAndStart()`, this factory will
// create a KeepAliveURLLoader to load a keepalive request. The loader is held
// by the `KeepAliveURLLoaderService` owning this factory.
//
// This factory must be run in the browser process.
//
// See the "Implementation Details" section of the design doc
// https://docs.google.com/document/d/1ZzxMMBvpqn8VZBZKnb7Go8TWjnrGcXuLS_USwVVRUvY/edit#
class KeepAliveURLLoaderService::KeepAliveURLLoaderFactory final
: public network::mojom::URLLoaderFactory {
public:
explicit KeepAliveURLLoaderFactory(KeepAliveURLLoaderService* service)
: service_(service) {
DCHECK(service_);
}
~KeepAliveURLLoaderFactory() override = default;
// Not copyable.
KeepAliveURLLoaderFactory(const KeepAliveURLLoaderFactory&) = delete;
KeepAliveURLLoaderFactory& operator=(const KeepAliveURLLoaderFactory&) =
delete;
// Creates a `BindContext` to hold a refptr to
// network::SharedURLLoaderFactory, which is constructed with
// `pending_factory`, and then bound with `receiver`.
void BindFactory(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory);
// `network::mojom::URLLoaderFactory` overrides:
void CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> receiver,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& resource_request_in,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
override;
void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
override;
private:
// Guaranteed to exist, as `service_` owns this object.
raw_ptr<KeepAliveURLLoaderService> service_;
// Receives `network::mojom::URLLoaderFactory` requests from renderers.
mojo::ReceiverSet<network::mojom::URLLoaderFactory,
std::unique_ptr<BindContext>>
loader_factory_receivers_;
};
void KeepAliveURLLoaderService::KeepAliveURLLoaderFactory::BindFactory(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT0("loading", "KeepAliveURLLoaderFactory::BindFactory");
auto factory_bundle =
network::SharedURLLoaderFactory::Create(std::move(pending_factory));
loader_factory_receivers_.Add(this, std::move(receiver),
std::make_unique<BindContext>(factory_bundle));
}
void KeepAliveURLLoaderService::KeepAliveURLLoaderFactory::CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> receiver,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& resource_request,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT1("loading", "KeepAliveURLLoaderFactory::CreateLoaderAndStart",
"request_id", request_id);
if (!resource_request.keepalive) {
loader_factory_receivers_.ReportBadMessage(
"Unexpected `resource_request` in "
"KeepAliveURLLoaderService::CreateLoaderAndStart(): "
"resource_request.keepalive must be true");
return;
}
if (resource_request.trusted_params) {
// Must use untrusted URLLoaderFactory. If not, the requesting renderer
// should be aborted.
loader_factory_receivers_.ReportBadMessage(
"Unexpected `resource_request` in "
"KeepAliveURLLoaderService::CreateLoaderAndStart(): "
"resource_request.trusted_params must not be set");
return;
}
// Creates a new KeepAliveURLLoader from the current context.
const std::unique_ptr<BindContext>& current_context =
loader_factory_receivers_.current_context();
// Passes in the pending remote of `client` from a renderer so that `loader`
// can forward response back to the renderer.
auto loader = std::make_unique<KeepAliveURLLoader>(
request_id, options, resource_request, std::move(client),
traffic_annotation, current_context->factory,
base::PassKey<KeepAliveURLLoaderService>());
// Binds `loader` with the pending `receiver` from a renderer to handle URL
// requests.
auto* raw_loader = loader.get();
auto receiver_id = service_->loader_receivers_.Add(
raw_loader, std::move(receiver), std::move(loader));
raw_loader->set_on_delete_callback(
base::BindOnce(&KeepAliveURLLoaderService::RemoveLoader,
base::Unretained(service_), receiver_id));
}
void KeepAliveURLLoaderService::KeepAliveURLLoaderFactory::Clone(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
loader_factory_receivers_.Add(
this, std::move(receiver),
std::make_unique<BindContext>(
loader_factory_receivers_.current_context()));
}
KeepAliveURLLoaderService::KeepAliveURLLoaderService() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
factory_ =
std::make_unique<KeepAliveURLLoaderService::KeepAliveURLLoaderFactory>(
this);
}
KeepAliveURLLoaderService::~KeepAliveURLLoaderService() = default;
void KeepAliveURLLoaderService::BindFactory(
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
factory_->BindFactory(std::move(receiver), std::move(pending_factory));
}
void KeepAliveURLLoaderService::RemoveLoader(
mojo::ReceiverId loader_receiver_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT1("loading", "KeepAliveURLLoaderService::RemoveLoader",
"loader_id", loader_receiver_id);
loader_receivers_.Remove(loader_receiver_id);
}
size_t KeepAliveURLLoaderService::NumLoadersForTesting() const {
return loader_receivers_.size();
}
} // namespace content