blob: c765b468098c5bb358f3e61d6a6616bd2d0aff77 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/network_service_network_delegate.h"
#include <string>
#include "base/bind.h"
#include "base/debug/dump_without_crashing.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/domain_reliability/monitor.h"
#include "net/base/features.h"
#include "net/base/isolation_info.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/cookies/same_party_context.h"
#include "net/url_request/referrer_policy.h"
#include "net/url_request/url_request.h"
#include "services/network/cookie_manager.h"
#include "services/network/network_context.h"
#include "services/network/network_service.h"
#include "services/network/network_service_proxy_delegate.h"
#include "services/network/pending_callback_chain.h"
#include "services/network/url_loader.h"
#include "url/gurl.h"
#if !BUILDFLAG(IS_IOS)
#include "services/network/websocket.h"
#endif
namespace network {
namespace {
const char kClearSiteDataHeader[] = "Clear-Site-Data";
} // anonymous namespace
NetworkServiceNetworkDelegate::NetworkServiceNetworkDelegate(
bool enable_referrers,
bool validate_referrer_policy_on_initial_request,
mojo::PendingRemote<mojom::ProxyErrorClient> proxy_error_client_remote,
NetworkContext* network_context)
: enable_referrers_(enable_referrers),
validate_referrer_policy_on_initial_request_(
validate_referrer_policy_on_initial_request),
network_context_(network_context) {
if (proxy_error_client_remote)
proxy_error_client_.Bind(std::move(proxy_error_client_remote));
}
NetworkServiceNetworkDelegate::~NetworkServiceNetworkDelegate() = default;
void NetworkServiceNetworkDelegate::MaybeTruncateReferrer(
net::URLRequest* const request,
const GURL& effective_url) {
if (!enable_referrers_) {
request->SetReferrer(std::string());
request->set_referrer_policy(net::ReferrerPolicy::NO_REFERRER);
return;
}
if (base::FeatureList::IsEnabled(
net::features::kCapReferrerToOriginOnCrossOrigin)) {
if (!url::IsSameOriginWith(effective_url, GURL(request->referrer()))) {
auto capped_referrer = url::Origin::Create(GURL(request->referrer()));
request->SetReferrer(capped_referrer.GetURL().spec());
}
}
}
int NetworkServiceNetworkDelegate::OnBeforeURLRequest(
net::URLRequest* request,
net::CompletionOnceCallback callback,
GURL* new_url) {
DCHECK(request);
auto* const loader = URLLoader::ForRequest(*request);
const GURL* effective_url = nullptr;
if (loader && loader->new_redirect_url()) {
DCHECK(new_url);
*new_url = loader->new_redirect_url().value();
effective_url = new_url;
} else {
effective_url = &request->url();
}
MaybeTruncateReferrer(request, *effective_url);
if (!loader)
return net::OK;
loader->OnBeforeURLRequest();
NetworkService* network_service = network_context_->network_service();
if (network_service) {
loader->SetEnableReportingRawHeaders(network_service->HasRawHeadersAccess(
loader->GetProcessId(), *effective_url));
}
return net::OK;
}
int NetworkServiceNetworkDelegate::OnBeforeStartTransaction(
net::URLRequest* request,
const net::HttpRequestHeaders& headers,
OnBeforeStartTransactionCallback callback) {
URLLoader* url_loader = URLLoader::ForRequest(*request);
if (url_loader)
return url_loader->OnBeforeStartTransaction(headers, std::move(callback));
#if !BUILDFLAG(IS_IOS)
WebSocket* web_socket = WebSocket::ForRequest(*request);
if (web_socket)
return web_socket->OnBeforeStartTransaction(headers, std::move(callback));
#endif // !BUILDFLAG(IS_IOS)
return net::OK;
}
int NetworkServiceNetworkDelegate::OnHeadersReceived(
net::URLRequest* request,
net::CompletionOnceCallback callback,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
const net::IPEndPoint& endpoint,
absl::optional<GURL>* preserve_fragment_on_redirect_url) {
auto chain = base::MakeRefCounted<PendingCallbackChain>(std::move(callback));
URLLoader* url_loader = URLLoader::ForRequest(*request);
if (url_loader) {
chain->AddResult(url_loader->OnHeadersReceived(
chain->CreateCallback(), original_response_headers,
override_response_headers, endpoint,
preserve_fragment_on_redirect_url));
}
#if !BUILDFLAG(IS_IOS)
WebSocket* web_socket = WebSocket::ForRequest(*request);
if (web_socket) {
chain->AddResult(web_socket->OnHeadersReceived(
chain->CreateCallback(), original_response_headers,
override_response_headers, preserve_fragment_on_redirect_url));
}
#endif // !BUILDFLAG(IS_IOS)
chain->AddResult(HandleClearSiteDataHeader(request, chain->CreateCallback(),
original_response_headers));
return chain->GetResult();
}
void NetworkServiceNetworkDelegate::OnBeforeRedirect(net::URLRequest* request,
const GURL& new_location) {
if (network_context_->domain_reliability_monitor())
network_context_->domain_reliability_monitor()->OnBeforeRedirect(request);
}
void NetworkServiceNetworkDelegate::OnResponseStarted(net::URLRequest* request,
int net_error) {
ForwardProxyErrors(net_error);
}
void NetworkServiceNetworkDelegate::OnCompleted(net::URLRequest* request,
bool started,
int net_error) {
// TODO(mmenke): Once the network service ships on all platforms, can move
// this logic into URLLoader's completion method.
DCHECK_NE(net::ERR_IO_PENDING, net_error);
if (network_context_->domain_reliability_monitor()) {
network_context_->domain_reliability_monitor()->OnCompleted(
request, started, net_error);
}
ForwardProxyErrors(net_error);
}
void NetworkServiceNetworkDelegate::OnPACScriptError(
int line_number,
const std::u16string& error) {
if (!proxy_error_client_)
return;
proxy_error_client_->OnPACScriptError(line_number, base::UTF16ToUTF8(error));
}
bool NetworkServiceNetworkDelegate::OnAnnotateAndMoveUserBlockedCookies(
const net::URLRequest& request,
net::CookieAccessResultList& maybe_included_cookies,
net::CookieAccessResultList& excluded_cookies,
bool allowed_from_caller) {
if (!allowed_from_caller) {
ExcludeAllCookies(net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES,
maybe_included_cookies, excluded_cookies);
return false;
}
if (!network_context_->cookie_manager()
->cookie_settings()
.AnnotateAndMoveUserBlockedCookies(
request.url(), request.site_for_cookies(),
request.isolation_info().top_frame_origin().has_value()
? &request.isolation_info().top_frame_origin().value()
: nullptr,
maybe_included_cookies, excluded_cookies)) {
// CookieSettings has already moved and annotated the cookies.
return false;
}
bool allowed = true;
URLLoader* url_loader = URLLoader::ForRequest(request);
if (url_loader) {
allowed =
url_loader->AllowCookies(request.url(), request.site_for_cookies());
#if !BUILDFLAG(IS_IOS)
} else {
WebSocket* web_socket = WebSocket::ForRequest(request);
if (web_socket) {
allowed = web_socket->AllowCookies(request.url());
}
#endif // !BUILDFLAG(IS_IOS)
}
if (!allowed)
ExcludeAllCookies(net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES,
maybe_included_cookies, excluded_cookies);
return allowed;
}
bool NetworkServiceNetworkDelegate::OnCanSetCookie(
const net::URLRequest& request,
const net::CanonicalCookie& cookie,
net::CookieOptions* options,
bool allowed_from_caller) {
bool allowed =
allowed_from_caller &&
network_context_->cookie_manager()->cookie_settings().IsCookieAccessible(
cookie, request.url(), request.site_for_cookies(),
request.isolation_info().top_frame_origin());
if (!allowed)
return false;
URLLoader* url_loader = URLLoader::ForRequest(request);
if (url_loader)
return url_loader->AllowCookies(request.url(), request.site_for_cookies());
#if !BUILDFLAG(IS_IOS)
WebSocket* web_socket = WebSocket::ForRequest(request);
if (web_socket)
return web_socket->AllowCookies(request.url());
#endif // !BUILDFLAG(IS_IOS)
return true;
}
bool NetworkServiceNetworkDelegate::OnForcePrivacyMode(
const GURL& url,
const net::SiteForCookies& site_for_cookies,
const absl::optional<url::Origin>& top_frame_origin,
net::SamePartyContext::Type same_party_context_type) const {
return network_context_->cookie_manager()
->cookie_settings()
.IsPrivacyModeEnabled(url, site_for_cookies, top_frame_origin,
same_party_context_type);
}
bool NetworkServiceNetworkDelegate::
OnCancelURLRequestWithPolicyViolatingReferrerHeader(
const net::URLRequest& request,
const GURL& target_url,
const GURL& referrer_url) const {
// TODO(mmenke): Once the network service has shipped on all platforms,
// consider moving this logic into URLLoader, and removing this method from
// NetworkDelegate. Can just have a DCHECK in URLRequest instead.
if (!validate_referrer_policy_on_initial_request_)
return false;
LOG(ERROR) << "Cancelling request to " << target_url
<< " with invalid referrer " << referrer_url;
// Record information to help debug issues like http://crbug.com/422871.
if (target_url.SchemeIsHTTPOrHTTPS()) {
auto referrer_policy = request.referrer_policy();
base::debug::Alias(&referrer_policy);
DEBUG_ALIAS_FOR_GURL(target_buf, target_url);
DEBUG_ALIAS_FOR_GURL(referrer_buf, referrer_url);
base::debug::DumpWithoutCrashing();
}
return true;
}
bool NetworkServiceNetworkDelegate::OnCanQueueReportingReport(
const url::Origin& origin) const {
return network_context_->cookie_manager()
->cookie_settings()
.IsFullCookieAccessAllowed(origin.GetURL(), origin.GetURL());
}
void NetworkServiceNetworkDelegate::OnCanSendReportingReports(
std::set<url::Origin> origins,
base::OnceCallback<void(std::set<url::Origin>)> result_callback) const {
auto* client = network_context_->client();
if (!client) {
origins.clear();
std::move(result_callback).Run(std::move(origins));
return;
}
if (network_context_->SkipReportingPermissionCheck()) {
std::move(result_callback).Run(std::move(origins));
return;
}
std::vector<url::Origin> origin_vector;
std::copy(origins.begin(), origins.end(), std::back_inserter(origin_vector));
client->OnCanSendReportingReports(
origin_vector,
base::BindOnce(
&NetworkServiceNetworkDelegate::FinishedCanSendReportingReports,
weak_ptr_factory_.GetWeakPtr(), std::move(result_callback)));
}
bool NetworkServiceNetworkDelegate::OnCanSetReportingClient(
const url::Origin& origin,
const GURL& endpoint) const {
return network_context_->cookie_manager()
->cookie_settings()
.IsFullCookieAccessAllowed(origin.GetURL(), origin.GetURL());
}
bool NetworkServiceNetworkDelegate::OnCanUseReportingClient(
const url::Origin& origin,
const GURL& endpoint) const {
return network_context_->cookie_manager()
->cookie_settings()
.IsFullCookieAccessAllowed(origin.GetURL(), origin.GetURL());
}
int NetworkServiceNetworkDelegate::HandleClearSiteDataHeader(
net::URLRequest* request,
net::CompletionOnceCallback callback,
const net::HttpResponseHeaders* original_response_headers) {
DCHECK(request);
if (!original_response_headers)
return net::OK;
URLLoader* url_loader = URLLoader::ForRequest(*request);
if (!url_loader)
return net::OK;
auto* url_loader_network_observer =
url_loader->GetURLLoaderNetworkServiceObserver();
if (!url_loader_network_observer)
return net::OK;
std::string header_value;
if (!original_response_headers->GetNormalizedHeader(kClearSiteDataHeader,
&header_value)) {
return net::OK;
}
url_loader_network_observer->OnClearSiteData(
request->url(), header_value, request->load_flags(),
net::CookiePartitionKey::FromNetworkIsolationKey(
request->isolation_info().network_isolation_key()),
base::BindOnce(&NetworkServiceNetworkDelegate::FinishedClearSiteData,
weak_ptr_factory_.GetWeakPtr(), request->GetWeakPtr(),
std::move(callback)));
return net::ERR_IO_PENDING;
}
void NetworkServiceNetworkDelegate::FinishedClearSiteData(
base::WeakPtr<net::URLRequest> request,
net::CompletionOnceCallback callback) {
if (request)
std::move(callback).Run(net::OK);
}
void NetworkServiceNetworkDelegate::FinishedCanSendReportingReports(
base::OnceCallback<void(std::set<url::Origin>)> result_callback,
const std::vector<url::Origin>& origins) {
std::set<url::Origin> origin_set(origins.begin(), origins.end());
std::move(result_callback).Run(origin_set);
}
void NetworkServiceNetworkDelegate::ForwardProxyErrors(int net_error) {
if (!proxy_error_client_)
return;
// TODO(https://crbug.com/876848): Provide justification for the currently
// enumerated errors.
switch (net_error) {
case net::ERR_PROXY_AUTH_UNSUPPORTED:
case net::ERR_PROXY_CONNECTION_FAILED:
case net::ERR_TUNNEL_CONNECTION_FAILED:
proxy_error_client_->OnRequestMaybeFailedDueToProxySettings(net_error);
break;
}
}
} // namespace network