blob: 4b99c54978393842d5edc0c62e8b5ab8df2072ce [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/url_loader_factory.h"
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "components/content_settings/core/common/content_settings.h"
#include "mojo/public/cpp/bindings/message.h"
#include "net/base/isolation_info.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_setting_override.h"
#include "net/url_request/url_request_context.h"
#include "services/network/attribution/attribution_request_helper.h"
#include "services/network/cookie_manager.h"
#include "services/network/cookie_settings.h"
#include "services/network/cors/cors_url_loader_factory.h"
#include "services/network/network_context.h"
#include "services/network/network_service.h"
#include "services/network/observer_wrapper.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/devtools_observer.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/resource_scheduler/resource_scheduler_client.h"
#include "services/network/shared_dictionary/shared_dictionary_access_checker.h"
#include "services/network/trust_tokens/trust_token_request_helper_factory.h"
#include "services/network/url_loader.h"
#include "services/network/web_bundle/web_bundle_url_loader_factory.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace network {
namespace {
// Helper function template to create ObserverWrapper instances.
// Encapsulates the logic of potentially moving a PendingRemote from
// TrustedParams based on a member pointer, or using a fallback pointer.
template <typename T>
ObserverWrapper<T> CreateObserverWrapper(
const std::optional<ResourceRequest::TrustedParams>& trusted_params,
mojo::PendingRemote<T> ResourceRequest::TrustedParams::* remote_member_ptr,
T* fallback_ptr) {
mojo::PendingRemote<T> remote_to_pass;
if (trusted_params) {
auto& remote_member =
const_cast<ResourceRequest::TrustedParams*>(&trusted_params.value())
->*remote_member_ptr;
if (remote_member.is_valid()) {
remote_to_pass = std::move(remote_member);
}
}
return ObserverWrapper<T>(std::move(remote_to_pass), fallback_ptr);
}
// Overload of CreateObserverWrapper that takes a mojo::Remote reference
// for the fallback, simplifying calls where the fallback is held in a Remote.
template <typename T>
ObserverWrapper<T> CreateObserverWrapper(
const std::optional<ResourceRequest::TrustedParams>& trusted_params,
mojo::PendingRemote<T> ResourceRequest::TrustedParams::* remote_member_ptr,
mojo::Remote<T>& remote_for_fallback_ptr) {
return CreateObserverWrapper<T>(
trusted_params, remote_member_ptr,
remote_for_fallback_ptr ? remote_for_fallback_ptr.get() : nullptr);
}
} // namespace
constexpr int URLLoaderFactory::kMaxKeepaliveConnections;
constexpr int URLLoaderFactory::kMaxKeepaliveConnectionsPerTopLevelFrame;
constexpr int URLLoaderFactory::kMaxTotalKeepaliveRequestSize;
URLLoaderFactory::URLLoaderFactory(
NetworkContext* context,
mojom::URLLoaderFactoryParamsPtr params,
scoped_refptr<ResourceSchedulerClient> resource_scheduler_client,
cors::CorsURLLoaderFactory* cors_url_loader_factory)
: context_(context),
params_(std::move(params)),
resource_scheduler_client_(std::move(resource_scheduler_client)),
header_client_(std::move(params_->header_client)),
cors_url_loader_factory_(cors_url_loader_factory),
cookie_observer_(std::move(params_->cookie_observer)),
trust_token_observer_(std::move(params_->trust_token_observer)),
devtools_observer_(std::move(params_->devtools_observer)),
device_bound_session_observer_(
params_->device_bound_session_observer
? base::MakeRefCounted<
RefCountedDeviceBoundSessionAccessObserverRemote>(
mojo::Remote<mojom::DeviceBoundSessionAccessObserver>(
std::move(params_->device_bound_session_observer)))
: nullptr) {
DCHECK(context);
DCHECK_NE(mojom::kInvalidProcessId, params_->process_id);
DCHECK(!params_->factory_override);
// Only non-navigation IsolationInfos should be bound to URLLoaderFactories.
DCHECK_EQ(net::IsolationInfo::RequestType::kOther,
params_->isolation_info.request_type());
DCHECK(!params_->automatically_assign_isolation_info ||
params_->isolation_info.IsEmpty());
DCHECK(cors_url_loader_factory_);
if (!params_->top_frame_id) {
params_->top_frame_id = base::UnguessableToken::Create();
}
if (context_->network_service()) {
context_->network_service()->keepalive_statistics_recorder()->Register(
*params_->top_frame_id);
}
}
URLLoaderFactory::~URLLoaderFactory() {
if (context_->network_service()) {
context_->network_service()->keepalive_statistics_recorder()->Unregister(
*params_->top_frame_id);
}
}
void URLLoaderFactory::CreateLoaderAndStart(
mojo::PendingReceiver<mojom::URLLoader> receiver,
int32_t request_id,
uint32_t options,
const ResourceRequest& resource_request,
mojo::PendingRemote<mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
CreateLoaderAndStartWithSyncClient(std::move(receiver), request_id, options,
resource_request, std::move(client),
/* sync_client= */ nullptr,
traffic_annotation);
}
void URLLoaderFactory::Clone(
mojo::PendingReceiver<mojom::URLLoaderFactory> receiver) {
NOTREACHED();
}
net::URLRequestContext* URLLoaderFactory::GetUrlRequestContext() const {
return context_->url_request_context();
}
mojom::NetworkContextClient* URLLoaderFactory::GetNetworkContextClient() const {
return context_->client();
}
const mojom::URLLoaderFactoryParams& URLLoaderFactory::GetFactoryParams()
const {
return *params_;
}
mojom::CrossOriginEmbedderPolicyReporter* URLLoaderFactory::GetCoepReporter()
const {
return cors_url_loader_factory_->coep_reporter();
}
mojom::DocumentIsolationPolicyReporter* URLLoaderFactory::GetDipReporter()
const {
return cors_url_loader_factory_->dip_reporter();
}
bool URLLoaderFactory::ShouldRequireIsolationInfo() const {
return context_->require_network_anonymization_key();
}
scoped_refptr<ResourceSchedulerClient>
URLLoaderFactory::GetResourceSchedulerClient() const {
return resource_scheduler_client_;
}
mojom::TrustedURLLoaderHeaderClient*
URLLoaderFactory::GetUrlLoaderHeaderClient() const {
return header_client_.is_bound() ? header_client_.get() : nullptr;
}
const cors::OriginAccessList& URLLoaderFactory::GetOriginAccessList() const {
return context_->cors_origin_access_list();
}
orb::PerFactoryState& URLLoaderFactory::GetMutableOrbState() {
return orb_state_;
}
bool URLLoaderFactory::DataUseUpdatesEnabled() {
return context_->network_service() &&
context_->network_service()->data_use_updates_enabled();
}
void URLLoaderFactory::CreateLoaderAndStartWithSyncClient(
mojo::PendingReceiver<mojom::URLLoader> receiver,
int32_t request_id,
uint32_t options,
const ResourceRequest& resource_request,
mojo::PendingRemote<mojom::URLLoaderClient> client,
base::WeakPtr<mojom::URLLoaderClient> sync_client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
// Requests with |trusted_params| when params_->is_trusted is not set should
// have been rejected at the CorsURLLoader layer.
DCHECK(!resource_request.trusted_params || params_->is_trusted);
if (resource_request.web_bundle_token_params.has_value() &&
resource_request.destination !=
network::mojom::RequestDestination::kWebBundle) {
mojo::Remote<mojom::TrustedHeaderClient> trusted_header_client;
if (header_client_ && (options & mojom::kURLLoadOptionUseHeaderClient)) {
// CORS preflight request must not come here.
DCHECK(!(options & mojom::kURLLoadOptionAsCorsPreflight));
header_client_->OnLoaderCreated(
request_id, trusted_header_client.BindNewPipeAndPassReceiver());
}
// Load a subresource from a WebBundle.
context_->GetWebBundleManager().StartSubresourceRequest(
std::move(receiver), resource_request, std::move(client),
params_->process_id, std::move(trusted_header_client));
return;
}
base::WeakPtr<KeepaliveStatisticsRecorder> keepalive_statistics_recorder;
if (context_->network_service()) {
keepalive_statistics_recorder = context_->network_service()
->keepalive_statistics_recorder()
->AsWeakPtr();
}
bool exhausted = false;
if (!context_->CanCreateLoader(params_->process_id)) {
exhausted = true;
}
int keepalive_request_size = 0;
if (resource_request.keepalive) {
base::UmaHistogramEnumeration(
"FetchKeepAlive.Requests2.Network",
internal::FetchKeepAliveRequestNetworkMetricType::kOnCreate);
}
if (resource_request.keepalive && keepalive_statistics_recorder) {
const size_t url_size = resource_request.url.spec().size();
size_t headers_size = 0;
net::HttpRequestHeaders merged_headers = resource_request.headers;
merged_headers.MergeFrom(resource_request.cors_exempt_headers);
for (const auto& pair : merged_headers.GetHeaderVector()) {
headers_size += (pair.key.size() + pair.value.size());
}
keepalive_request_size = url_size + headers_size;
const auto& top_frame_id = *params_->top_frame_id;
const auto& recorder = *keepalive_statistics_recorder;
if (!exhausted) {
if (recorder.num_inflight_requests() >= kMaxKeepaliveConnections ||
recorder.NumInflightRequestsPerTopLevelFrame(top_frame_id) >=
kMaxKeepaliveConnectionsPerTopLevelFrame ||
recorder.GetTotalRequestSizePerTopLevelFrame(top_frame_id) +
keepalive_request_size >
kMaxTotalKeepaliveRequestSize) {
exhausted = true;
}
}
}
if (exhausted) {
URLLoaderCompletionStatus status;
status.error_code = net::ERR_INSUFFICIENT_RESOURCES;
status.exists_in_cache = false;
status.completion_time = base::TimeTicks::Now();
mojo::Remote<mojom::URLLoaderClient>(std::move(client))->OnComplete(status);
return;
}
std::unique_ptr<TrustTokenRequestHelperFactory> trust_token_factory;
if (resource_request.trust_token_params) {
trust_token_factory = std::make_unique<TrustTokenRequestHelperFactory>(
context_->trust_token_store(),
context_->network_service()->trust_token_key_commitments(),
// It's safe to use Unretained because |context_| is guaranteed to
// outlive the URLLoader that will own this
// TrustTokenRequestHelperFactory.
base::BindRepeating(&NetworkContext::client,
base::Unretained(context_)),
// It's safe to access cookie manager for |context_| here because
// NetworkContext::CookieManager outlives the URLLoaders associated with
// the NetworkContext.
base::BindRepeating(
[](NetworkContext* context, const GURL& resource_request_url,
const GURL& top_frame_origin) {
// Private state tokens will be blocked if the user has either
// disabled the anti-abuse content setting or blocked the top
// level site or issuer from storing data through the cookie
// content settings.
return (
// PST is not disabled through settings.
!context->are_trust_tokens_blocked() &&
// and top frame is not blocked.
context->cookie_manager()
->cookie_settings()
.ArePrivateStateTokensAllowed(top_frame_origin) &&
// and issuer is not blocked.
context->cookie_manager()
->cookie_settings()
.ArePrivateStateTokensAllowed(resource_request_url));
},
base::Unretained(context_), resource_request.url,
params_->isolation_info.top_frame_origin()
.value_or(url::Origin())
.GetURL()));
}
std::unique_ptr<SharedDictionaryAccessChecker> shared_dictionary_checker;
if (context_->GetSharedDictionaryManager()) {
if (resource_request.trusted_params &&
resource_request.trusted_params->shared_dictionary_observer) {
shared_dictionary_checker =
std::make_unique<SharedDictionaryAccessChecker>(
*context_, std::move(const_cast<mojo::PendingRemote<
mojom::SharedDictionaryAccessObserver>&>(
resource_request.trusted_params
->shared_dictionary_observer)));
} else {
shared_dictionary_checker =
std::make_unique<SharedDictionaryAccessChecker>(
*context_,
cors_url_loader_factory_->GetSharedDictionaryAccessObserver());
}
}
auto cookie_observer = CreateObserverWrapper<mojom::CookieAccessObserver>(
resource_request.trusted_params,
&ResourceRequest::TrustedParams::cookie_observer, cookie_observer_);
auto trust_token_observer =
CreateObserverWrapper<mojom::TrustTokenAccessObserver>(
resource_request.trusted_params,
&ResourceRequest::TrustedParams::trust_token_observer,
trust_token_observer_);
auto url_loader_network_observer =
CreateObserverWrapper<mojom::URLLoaderNetworkServiceObserver>(
resource_request.trusted_params,
&ResourceRequest::TrustedParams::url_loader_network_observer,
GetURLLoaderNetworkServiceObserver());
auto devtools_observer = CreateObserverWrapper<mojom::DevToolsObserver>(
resource_request.trusted_params,
&ResourceRequest::TrustedParams::devtools_observer, devtools_observer_);
auto device_bound_session_observer =
CreateObserverWrapper<mojom::DeviceBoundSessionAccessObserver>(
resource_request.trusted_params,
&ResourceRequest::TrustedParams::device_bound_session_observer,
device_bound_session_observer_
? device_bound_session_observer_->data.get()
: nullptr);
mojo::PendingRemote<mojom::AcceptCHFrameObserver> accept_ch_frame_observer;
if (resource_request.trusted_params &&
resource_request.trusted_params->accept_ch_frame_observer) {
accept_ch_frame_observer = std::move(
const_cast<mojo::PendingRemote<mojom::AcceptCHFrameObserver>&>(
resource_request.trusted_params->accept_ch_frame_observer));
}
auto loader = std::make_unique<URLLoader>(
*this,
base::BindOnce(&cors::CorsURLLoaderFactory::DestroyURLLoader,
base::Unretained(cors_url_loader_factory_)),
std::move(receiver), options, resource_request, std::move(client),
std::move(sync_client),
static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation),
request_id, keepalive_request_size,
std::move(keepalive_statistics_recorder), std::move(trust_token_factory),
context_->GetSharedDictionaryManager(),
std::move(shared_dictionary_checker), std::move(cookie_observer),
std::move(trust_token_observer), std::move(url_loader_network_observer),
std::move(devtools_observer), std::move(device_bound_session_observer),
std::move(accept_ch_frame_observer),
resource_request.shared_storage_writable_eligible,
*context_->GetSharedResourceChecker(),
context_->MaybeCreateDurableMessage(
resource_request.throttling_profile_id,
resource_request.devtools_request_id));
cors_url_loader_factory_->OnURLLoaderCreated(std::move(loader));
}
net::handles::NetworkHandle URLLoaderFactory::GetBoundNetworkForTesting()
const {
return context_->url_request_context()->bound_network();
}
mojom::DevToolsObserver* URLLoaderFactory::GetDevToolsObserver() const {
if (devtools_observer_) {
return devtools_observer_.get();
}
return nullptr;
}
scoped_refptr<RefCountedDeviceBoundSessionAccessObserverRemote>
URLLoaderFactory::GetDeviceBoundSessionAccessObserverSharedRemote() const {
return device_bound_session_observer_;
}
mojom::URLLoaderNetworkServiceObserver*
URLLoaderFactory::GetURLLoaderNetworkServiceObserver() const {
if (cors_url_loader_factory_->url_loader_network_service_observer()) {
return cors_url_loader_factory_->url_loader_network_service_observer();
}
if (!context_->network_service()) {
return nullptr;
}
return context_->network_service()
->GetDefaultURLLoaderNetworkServiceObserver();
}
} // namespace network