0

network: Move URLLoader utility functions to url_loader_util

This change continues the refactoring of `url_loader.cc` by moving
several static methods and helper functions into the dedicated utility
files `url_loader_util.h` and `url_loader_util.cc`.

Functions moved include:
- `CalculateCookieSettingOverrides` (formerly static `URLLoader::`)
- `GetIsolationInfo` (formerly static `URLLoader::`)
- `GetCookiesFromHeaders` (formerly in anonymous namespace)
- `RecordURLLoaderRequestMetrics` (formerly
  `URLLoader::RecordRequestMetrics`)
- `MaybeRecordSharedDictionaryUsedResponseMetrics` (formerly in
  anonymous namespace)

Associated constants (e.g., `kAllowedDevToolsCookieSettingOverrides`)
and helper functions used solely by these utilities (e.g., metric string
builders like `GetDestinationTypePartString`) were also moved.

Call sites within `URLLoader`, `CorsURLLoader`, `CorsURLLoaderFactory`,
and related unit tests have been updated to call these functions from
their new location.

This further reduces the size and complexity of `url_loader.cc`,
improves code organization by grouping related utility logic.

This is purely a code movement refactoring with no intended functional
changes.

Bug: 408106280
Change-Id: Iff5a354f5a20b67eda0ad3e5dd0faa63c7468211
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6433387
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
Commit-Queue: Tsuyoshi Horo <horo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1442624}
This commit is contained in:
Tsuyoshi Horo
2025-04-04 01:40:15 -07:00
committed by Chromium LUCI CQ
parent 9887e1e336
commit bed536bbad
7 changed files with 319 additions and 266 deletions

@ -58,6 +58,7 @@
#include "services/network/trust_tokens/trust_token_operation_metrics_recorder.h"
#include "services/network/url_loader.h"
#include "services/network/url_loader_factory.h"
#include "services/network/url_loader_util.h"
#include "url/scheme_host_port.h"
#include "url/url_util.h"
@ -894,7 +895,7 @@ void CorsURLLoader::StartRequest() {
context_->cookie_manager()->cookie_settings().GetStorageAccessStatus(
request_.url, request_.site_for_cookies,
isolation_info_.top_frame_origin(),
network::URLLoader::CalculateCookieSettingOverrides(
url_loader_util::CalculateCookieSettingOverrides(
factory_cookie_setting_overrides_,
devtools_cookie_setting_overrides_, request_,
/*emit_metrics=*/false),

@ -41,6 +41,7 @@
#include "services/network/shared_dictionary/shared_dictionary_storage.h"
#include "services/network/url_loader.h"
#include "services/network/url_loader_factory.h"
#include "services/network/url_loader_util.h"
#include "services/network/web_bundle/web_bundle_url_loader_factory.h"
#include "url/gurl.h"
#include "url/origin.h"
@ -418,7 +419,7 @@ void CorsURLLoaderFactory::CreateLoaderAndStart(
DCHECK(inner_url_loader_factory);
const net::IsolationInfo* isolation_info_ptr = &isolation_info_;
auto isolation_info = URLLoader::GetIsolationInfo(
auto isolation_info = url_loader_util::GetIsolationInfo(
isolation_info_, automatically_assign_isolation_info_, resource_request);
if (isolation_info.has_value()) {
isolation_info_ptr = &isolation_info.value();

@ -34,6 +34,7 @@
#include "services/network/test/mock_devtools_observer.h"
#include "services/network/test/test_url_loader_client.h"
#include "services/network/url_loader.h"
#include "services/network/url_loader_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/origin.h"
@ -2441,7 +2442,7 @@ class StorageAccessHeadersCorsURLLoaderTest : public CorsURLLoaderTest {
.GetStorageAccessStatus(
request.url, request.site_for_cookies,
request.trusted_params->isolation_info.top_frame_origin(),
URLLoader::CalculateCookieSettingOverrides(
url_loader_util::CalculateCookieSettingOverrides(
/*factory_overrides=*/net::CookieSettingOverrides(),
/*devtools_overrides=*/net::CookieSettingOverrides(), request,
/*emit_metrics=*/false),

@ -136,17 +136,6 @@ constexpr size_t kBlockedBodyAllocationSize = 1;
// Size to allocate for `discard_buffer_`.
constexpr size_t kDiscardBufferSize = 128 * 1024;
// TODO(https://crbug.com/375352611): add the check for enabling third-party
// cookies.
constexpr uint64_t kAllowedDevToolsCookieSettingOverrides =
1u << static_cast<int>(
net::CookieSettingOverride::kForceDisableThirdPartyCookies) |
1u << static_cast<int>(
net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations) |
1u << static_cast<int>(net::CookieSettingOverride::kSkipTPCDMetadataGrant) |
1u << static_cast<int>(
net::CookieSettingOverride::kSkipTPCDHeuristicsGrant);
constexpr char kActivateStorageAccessHeader[] = "activate-storage-access";
// These values are persisted to logs. Entries should not be renumbered and
@ -281,18 +270,6 @@ MaybeInitializeDeviceBoundSessionAccessObserverSharedRemote(
return context.GetDeviceBoundSessionAccessObserverSharedRemote();
}
// Retrieves the Cookie header from either `cors_exempt_headers` or `headers`.
std::string GetCookiesFromHeaders(
const net::HttpRequestHeaders& headers,
const net::HttpRequestHeaders& cors_exempt_headers) {
std::optional<std::string> cookies =
cors_exempt_headers.GetHeader(net::HttpRequestHeaders::kCookie);
if (!cookies) {
cookies = headers.GetHeader(net::HttpRequestHeaders::kCookie);
}
return std::move(cookies).value_or(std::string());
}
net::HttpRequestHeaders AttachCookies(const net::HttpRequestHeaders& headers,
const std::string& cookies_from_browser) {
DCHECK(!cookies_from_browser.empty());
@ -331,53 +308,6 @@ net::HttpRequestHeaders AttachCookies(const net::HttpRequestHeaders& headers,
return updated_headers;
}
const char* GetDestinationTypePartString(
network::mojom::RequestDestination destination) {
if (destination == network::mojom::RequestDestination::kDocument) {
return "MainFrame";
} else if (destination == network::mojom::RequestDestination::kFrame ||
destination == network::mojom::RequestDestination::kIframe) {
return "SubFrame";
}
return "Subresource";
}
const char* GetCertStatePartString(const net::SSLInfo& ssl_info) {
if (!ssl_info.cert.get()) {
return "NoCert";
}
return ssl_info.is_issued_by_known_root ? "KnownRootCert" : "UnknownRootCert";
}
void MaybeRecordSharedDictionaryUsedResponseMetrics(
int error_code,
network::mojom::RequestDestination destination,
const net::HttpResponseInfo& response_info,
bool shared_dictionary_allowed_check_passed) {
if (response_info.was_cached) {
return;
}
if (response_info.did_use_shared_dictionary) {
base::UmaHistogramSparse(
base::StrCat({"Net.SharedDictionaryUsedResponseErrorCodes2.",
GetDestinationTypePartString(destination), ".",
GetCertStatePartString(response_info.ssl_info)}),
-error_code);
}
if (shared_dictionary_allowed_check_passed &&
destination == network::mojom::RequestDestination::kDocument) {
base::UmaHistogramBoolean(
base::StrCat(
{"Net.SharedDictionaryUsedByResponseWhenAvailable2.MainFrame.",
net::HttpConnectionInfoCoarseToString(
net::HttpConnectionInfoToCoarse(
response_info.connection_info)),
".", GetCertStatePartString(response_info.ssl_info)}),
response_info.did_use_shared_dictionary);
}
}
std::vector<network::mojom::HttpRawHeaderPairPtr>
ResponseHeaderToRawHeaderPairs(
const net::HttpResponseHeaders& response_headers) {
@ -390,18 +320,6 @@ ResponseHeaderToRawHeaderPairs(
return header_array;
}
bool IsMultiplexedConnection(const net::HttpResponseInfo& response_info) {
switch (net::HttpConnectionInfoToCoarse(response_info.connection_info)) {
case net::HttpConnectionInfoCoarse::kHTTP1:
return false;
case net::HttpConnectionInfoCoarse::kHTTP2:
case net::HttpConnectionInfoCoarse::kQUIC:
return true;
case net::HttpConnectionInfoCoarse::kOTHER:
return false;
}
}
bool IncludesValidLoadField(const net::HttpResponseHeaders* headers) {
if (!headers) {
return false;
@ -590,11 +508,11 @@ URLLoader::URLLoader(
allow_cookies_from_browser_(
request.trusted_params &&
request.trusted_params->allow_cookies_from_browser),
cookies_from_browser_(
allow_cookies_from_browser_
? GetCookiesFromHeaders(request.headers,
cookies_from_browser_(allow_cookies_from_browser_
? url_loader_util::GetCookiesFromHeaders(
request.headers,
request.cors_exempt_headers)
: std::string()),
: std::string()),
include_request_cookies_with_response_(
request.trusted_params &&
request.trusted_params->include_request_cookies_with_response),
@ -707,9 +625,9 @@ URLLoader::URLLoader(
/*is_ad_tagged=*/request.is_ad_tagged,
request.client_side_content_decoding_enabled,
/*isolation_info=*/
GetIsolationInfo(factory_params_->isolation_info,
factory_params_->automatically_assign_isolation_info,
request),
url_loader_util::GetIsolationInfo(
factory_params_->isolation_info,
factory_params_->automatically_assign_isolation_info, request),
/*force_main_frame_for_same_site_cookies=*/
force_main_frame_for_same_site_cookies, secure_dns_policy,
std::move(merged_headers), request.devtools_accepted_stream_types,
@ -717,7 +635,7 @@ URLLoader::URLLoader(
/*request_load_flags=*/request.load_flags,
/*priority_incremental=*/request.priority_incremental,
/*cookie_setting_overrides=*/
CalculateCookieSettingOverrides(
url_loader_util::CalculateCookieSettingOverrides(
factory_params_->cookie_setting_overrides,
factory_params_->devtools_cookie_setting_overrides, request,
/*emit_metrics=*/true),
@ -1034,7 +952,7 @@ void URLLoader::SetUpUpload(const ResourceRequest& request,
scoped_refptr<base::SequencedTaskRunner> task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE});
url_request_->set_upload(CreateUploadDataStream(
url_request_->set_upload(url_loader_util::CreateUploadDataStream(
request.request_body.get(), opened_files, task_runner.get()));
if (request.enable_upload_progress) {
@ -1359,8 +1277,8 @@ void URLLoader::FollowRedirect(
// Store any cookies passed from the browser process to later attach them to
// the request.
if (allow_cookies_from_browser_) {
cookies_from_browser_ =
GetCookiesFromHeaders(modified_headers, modified_cors_exempt_headers);
cookies_from_browser_ = url_loader_util::GetCookiesFromHeaders(
modified_headers, modified_cors_exempt_headers);
}
// Reset the state of the PNA checker - redirects should be treated like new
@ -1817,79 +1735,6 @@ bool URLLoader::HasFetchStreamingUploadBody(const ResourceRequest* request) {
element.As<network::DataElementChunkedDataPipe>().read_only_once();
}
// static
std::optional<net::IsolationInfo> URLLoader::GetIsolationInfo(
const net::IsolationInfo& factory_isolation_info,
bool automatically_assign_isolation_info,
const ResourceRequest& request) {
if (!factory_isolation_info.IsEmpty())
return factory_isolation_info;
if (request.trusted_params &&
!request.trusted_params->isolation_info.IsEmpty()) {
if (request.credentials_mode != network::mojom::CredentialsMode::kOmit) {
DCHECK(request.trusted_params->isolation_info.site_for_cookies()
.IsEquivalent(request.site_for_cookies));
}
return request.trusted_params->isolation_info;
}
if (automatically_assign_isolation_info) {
url::Origin origin = url::Origin::Create(request.url);
return net::IsolationInfo::Create(net::IsolationInfo::RequestType::kOther,
origin, origin, net::SiteForCookies());
}
return std::nullopt;
}
// static
net::CookieSettingOverrides URLLoader::CalculateCookieSettingOverrides(
net::CookieSettingOverrides factory_overrides,
net::CookieSettingOverrides devtools_overrides,
const ResourceRequest& request,
bool emit_metrics) {
net::CookieSettingOverrides overrides(factory_overrides);
if (request.is_outermost_main_frame &&
network::cors::IsCorsEnabledRequestMode(request.mode)) {
overrides.Put(
net::CookieSettingOverride::kTopLevelStorageAccessGrantEligible);
}
AddAdsHeuristicCookieSettingOverrides(request.is_ad_tagged, overrides,
emit_metrics);
// Only apply the DevTools overrides if the request is from devtools enabled
// context.
if (request.devtools_request_id.has_value()) {
CHECK_EQ(devtools_overrides.ToEnumBitmask() &
~kAllowedDevToolsCookieSettingOverrides,
0u);
overrides = base::Union(overrides, devtools_overrides);
}
// The `kStorageAccessGrantEligible` override should not be present in
// factory_overrides.
CHECK(
!overrides.Has(net::CookieSettingOverride::kStorageAccessGrantEligible));
// Add the Storage Access override enum based on whether the request's url and
// initiator are same-site, to prevent cross-site sibling iframes benefit from
// each other's storage access API grants. This must be updated on redirects.
if (net::cookie_util::ShouldAddInitialStorageAccessApiOverride(
request.url, request.storage_access_api_status,
request.request_initiator, emit_metrics,
request.credentials_mode == mojom::CredentialsMode::kInclude)) {
overrides.Put(net::CookieSettingOverride::kStorageAccessGrantEligible);
}
// The `kStorageAccessGrantEligibleViaHeader` override will be applied
// (in-place) by individual request jobs as appropriate, but should not be
// present initially.
CHECK(!overrides.Has(
net::CookieSettingOverride::kStorageAccessGrantEligibleViaHeader));
return overrides;
}
void URLLoader::OnAuthRequired(net::URLRequest* url_request,
const net::AuthChallengeInfo& auth_info) {
if (has_fetch_streaming_upload_body_) {
@ -2781,7 +2626,7 @@ void URLLoader::NotifyCompleted(int error_code) {
UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesSent3.Delegate", total_sent);
}
MaybeRecordSharedDictionaryUsedResponseMetrics(
url_loader_util::MaybeRecordSharedDictionaryUsedResponseMetrics(
error_code, request_destination_, url_request_->response_info(),
shared_dictionary_allowed_check_passed_);
@ -3027,7 +2872,8 @@ bool URLLoader::DispatchOnRawResponse() {
if (url_request_->response_headers() && !seen_raw_request_headers_) {
// Record request metrics here instead of in NotifyCompleted to account for
// redirects.
RecordRequestMetrics();
url_loader_util::RecordURLLoaderRequestMetrics(
*url_request_, raw_request_line_size_, raw_request_headers_size_);
}
if (!devtools_observer_ || !devtools_request_id() ||
@ -3510,81 +3356,4 @@ bool URLLoader::ShouldSetLoadWithStorageAccess() const {
net::cookie_util::ActivateStorageAccessLoadOutcome::kSuccess;
}
void URLLoader::RecordRequestMetrics() {
// All histograms recorded here are of the form:
// "NetworkService.Requests.{Multiplexed}.{RequestType}.{Method}.{Result}.{Metric}".
// For example:
// "NetworkService.Requests.Simple.MainFrame.Get.Success.TotalRequestSize".
absl::InlinedVector<std::string_view, 10> histogram_prefix_pieces = {
"NetworkService", "Requests"};
const net::HttpResponseInfo& response_info = url_request_->response_info();
if (IsMultiplexedConnection(response_info)) {
histogram_prefix_pieces.push_back("Multiplexed");
} else {
histogram_prefix_pieces.push_back("Simple");
}
switch (url_request_->isolation_info().request_type()) {
case net::IsolationInfo::RequestType::kMainFrame:
histogram_prefix_pieces.push_back("MainFrame");
break;
case net::IsolationInfo::RequestType::kSubFrame:
case net::IsolationInfo::RequestType::kOther:
// TODO(crbug.com/362787712): Add metrics for other types of requests.
return;
}
if (url_request_->method() == "GET") {
histogram_prefix_pieces.push_back("Get");
} else {
// Other types of requests need to be handled differently e.g. the total
// request size of a POST request needs to include the body.
// TODO(crbug.com/362787712): Add metrics for other types of requests.
return;
}
const int response_code = response_info.headers->response_code();
if (response_code < 199) {
// Ignore information responses because they are not complete requests.
return;
} else if (response_code < 299 || response_code < 399) {
// We consider redirects a success.
histogram_prefix_pieces.push_back("Success");
} else if (response_code < 499) {
histogram_prefix_pieces.push_back("ClientError");
} else if (response_code < 599) {
histogram_prefix_pieces.push_back("ServerError");
} else {
// Ignore unexpected server response codes.
return;
}
auto make_histogram_name =
[&histogram_prefix_pieces](std::string_view metric) {
histogram_prefix_pieces.push_back(metric);
std::string name = base::JoinString(histogram_prefix_pieces, ".");
histogram_prefix_pieces.pop_back();
return name;
};
base::UmaHistogramCounts100000(make_histogram_name("TotalUrlSize"),
url_request_->url().spec().size());
// HTTP/2 and HTTP/3 requests don't separate request line from headers so no
// need to record header metrics separately.
if (!IsMultiplexedConnection(response_info)) {
base::UmaHistogramCounts100000(make_histogram_name("TotalHeadersSize"),
raw_request_headers_size_);
}
// For HTTP/2 and HTTP/3 the request line is included in the headers, but
// `raw_request_line_size_` is 0 for these requests, so we can add it
// unconditionally for all requests.
size_t total_request_size =
raw_request_headers_size_ + raw_request_line_size_;
base::UmaHistogramCounts100000(make_histogram_name("TotalRequestSize"),
total_request_size);
}
} // namespace network

@ -310,19 +310,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
static bool HasFetchStreamingUploadBody(const ResourceRequest*);
static std::optional<net::IsolationInfo> GetIsolationInfo(
const net::IsolationInfo& factory_isolation_info,
bool automatically_assign_isolation_info,
const ResourceRequest& request);
// Computes the CookieSettingOverrides to use for a given `ResourceRequest`.
// May also emit to histograms.
static net::CookieSettingOverrides CalculateCookieSettingOverrides(
net::CookieSettingOverrides factory_overrides,
net::CookieSettingOverrides devtools_overrides,
const ResourceRequest& request,
bool emit_metrics);
// Returns an optional reference to a constant permissions policy that belongs
// to the request. `this` must outlive the caller of this method.
base::optional_ref<const network::PermissionsPolicy> GetPermissionsPolicy()
@ -651,9 +638,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
// `partial_decoder_result_` is set unless an error occurred during decoding.
void CheckPartialDecoderResult(int result);
// Records metrics about GET requests.
void RecordRequestMetrics();
const raw_ptr<net::URLRequestContext> url_request_context_;
const raw_ptr<mojom::NetworkContextClient> network_context_client_;

@ -4,20 +4,71 @@
#include "services/network/url_loader_util.h"
#include "base/containers/enum_set.h"
#include "base/metrics/histogram_functions.h"
#include "net/base/elements_upload_data_stream.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/base/upload_data_stream.h"
#include "net/base/upload_file_element_reader.h"
#include "net/cookies/cookie_util.h"
#include "net/http/http_connection_info.h"
#include "net/http/http_response_info.h"
#include "net/url_request/url_request.h"
#include "services/network/ad_heuristic_cookie_overrides.h"
#include "services/network/chunked_data_pipe_upload_data_stream.h"
#include "services/network/data_pipe_element_reader.h"
#include "services/network/public/cpp/cors/cors.h"
#include "services/network/public/cpp/data_element.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/resource_request_body.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "services/network/public/mojom/url_request.mojom-shared.h"
namespace network {
namespace network::url_loader_util {
namespace {
// TODO(https://crbug.com/375352611): add the check for enabling third-party
// cookies.
constexpr uint64_t kAllowedDevToolsCookieSettingOverrides =
1u << static_cast<int>(
net::CookieSettingOverride::kForceDisableThirdPartyCookies) |
1u << static_cast<int>(
net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations) |
1u << static_cast<int>(net::CookieSettingOverride::kSkipTPCDMetadataGrant) |
1u << static_cast<int>(
net::CookieSettingOverride::kSkipTPCDHeuristicsGrant);
bool IsMultiplexedConnection(const net::HttpResponseInfo& response_info) {
switch (net::HttpConnectionInfoToCoarse(response_info.connection_info)) {
case net::HttpConnectionInfoCoarse::kHTTP1:
return false;
case net::HttpConnectionInfoCoarse::kHTTP2:
case net::HttpConnectionInfoCoarse::kQUIC:
return true;
case net::HttpConnectionInfoCoarse::kOTHER:
return false;
}
}
const char* GetDestinationTypePartString(
network::mojom::RequestDestination destination) {
if (destination == network::mojom::RequestDestination::kDocument) {
return "MainFrame";
} else if (destination == network::mojom::RequestDestination::kFrame ||
destination == network::mojom::RequestDestination::kIframe) {
return "SubFrame";
}
return "Subresource";
}
const char* GetCertStatePartString(const net::SSLInfo& ssl_info) {
if (!ssl_info.cert.get()) {
return "NoCert";
}
return ssl_info.is_issued_by_known_root ? "KnownRootCert" : "UnknownRootCert";
}
// A subclass of net::UploadBytesElementReader which owns
// ResourceRequestBody.
class BytesElementReader : public net::UploadBytesElementReader {
@ -118,4 +169,196 @@ std::unique_ptr<net::UploadDataStream> CreateUploadDataStream(
std::move(element_readers), body->identifier());
}
} // namespace network
net::CookieSettingOverrides CalculateCookieSettingOverrides(
net::CookieSettingOverrides factory_overrides,
net::CookieSettingOverrides devtools_overrides,
const ResourceRequest& request,
bool emit_metrics) {
net::CookieSettingOverrides overrides(factory_overrides);
if (request.is_outermost_main_frame &&
network::cors::IsCorsEnabledRequestMode(request.mode)) {
overrides.Put(
net::CookieSettingOverride::kTopLevelStorageAccessGrantEligible);
}
AddAdsHeuristicCookieSettingOverrides(request.is_ad_tagged, overrides,
emit_metrics);
// Only apply the DevTools overrides if the request is from devtools enabled
// context.
if (request.devtools_request_id.has_value()) {
CHECK_EQ(devtools_overrides.ToEnumBitmask() &
~kAllowedDevToolsCookieSettingOverrides,
0u);
overrides = base::Union(overrides, devtools_overrides);
}
// The `kStorageAccessGrantEligible` override should not be present in
// factory_overrides.
CHECK(
!overrides.Has(net::CookieSettingOverride::kStorageAccessGrantEligible));
// Add the Storage Access override enum based on whether the request's url and
// initiator are same-site, to prevent cross-site sibling iframes benefit from
// each other's storage access API grants. This must be updated on redirects.
if (net::cookie_util::ShouldAddInitialStorageAccessApiOverride(
request.url, request.storage_access_api_status,
request.request_initiator, emit_metrics,
request.credentials_mode == mojom::CredentialsMode::kInclude)) {
overrides.Put(net::CookieSettingOverride::kStorageAccessGrantEligible);
}
// The `kStorageAccessGrantEligibleViaHeader` override will be applied
// (in-place) by individual request jobs as appropriate, but should not be
// present initially.
CHECK(!overrides.Has(
net::CookieSettingOverride::kStorageAccessGrantEligibleViaHeader));
return overrides;
}
std::optional<net::IsolationInfo> GetIsolationInfo(
const net::IsolationInfo& factory_isolation_info,
bool automatically_assign_isolation_info,
const ResourceRequest& request) {
if (!factory_isolation_info.IsEmpty()) {
return factory_isolation_info;
}
if (request.trusted_params &&
!request.trusted_params->isolation_info.IsEmpty()) {
if (request.credentials_mode != network::mojom::CredentialsMode::kOmit) {
DCHECK(request.trusted_params->isolation_info.site_for_cookies()
.IsEquivalent(request.site_for_cookies));
}
return request.trusted_params->isolation_info;
}
if (automatically_assign_isolation_info) {
url::Origin origin = url::Origin::Create(request.url);
return net::IsolationInfo::Create(net::IsolationInfo::RequestType::kOther,
origin, origin, net::SiteForCookies());
}
return std::nullopt;
}
// Retrieves the Cookie header from either `cors_exempt_headers` or `headers`.
std::string GetCookiesFromHeaders(
const net::HttpRequestHeaders& headers,
const net::HttpRequestHeaders& cors_exempt_headers) {
std::optional<std::string> cookies =
cors_exempt_headers.GetHeader(net::HttpRequestHeaders::kCookie);
if (!cookies) {
cookies = headers.GetHeader(net::HttpRequestHeaders::kCookie);
}
return std::move(cookies).value_or(std::string());
}
void RecordURLLoaderRequestMetrics(const net::URLRequest& url_request,
size_t raw_request_line_size,
size_t raw_request_headers_size) {
// All histograms recorded here are of the form:
// "NetworkService.Requests.{Multiplexed}.{RequestType}.{Method}.{Result}
// .{Metric}".
// For example:
// "NetworkService.Requests.Simple.MainFrame.Get.Success.TotalRequestSize".
absl::InlinedVector<std::string_view, 10> histogram_prefix_pieces = {
"NetworkService", "Requests"};
const net::HttpResponseInfo& response_info = url_request.response_info();
if (IsMultiplexedConnection(response_info)) {
histogram_prefix_pieces.push_back("Multiplexed");
} else {
histogram_prefix_pieces.push_back("Simple");
}
switch (url_request.isolation_info().request_type()) {
case net::IsolationInfo::RequestType::kMainFrame:
histogram_prefix_pieces.push_back("MainFrame");
break;
case net::IsolationInfo::RequestType::kSubFrame:
case net::IsolationInfo::RequestType::kOther:
// TODO(crbug.com/362787712): Add metrics for other types of requests.
return;
}
if (url_request.method() == "GET") {
histogram_prefix_pieces.push_back("Get");
} else {
// Other types of requests need to be handled differently e.g. the total
// request size of a POST request needs to include the body.
// TODO(crbug.com/362787712): Add metrics for other types of requests.
return;
}
const int response_code = response_info.headers->response_code();
if (response_code < 199) {
// Ignore information responses because they are not complete requests.
return;
} else if (response_code < 299 || response_code < 399) {
// We consider redirects a success.
histogram_prefix_pieces.push_back("Success");
} else if (response_code < 499) {
histogram_prefix_pieces.push_back("ClientError");
} else if (response_code < 599) {
histogram_prefix_pieces.push_back("ServerError");
} else {
// Ignore unexpected server response codes.
return;
}
auto make_histogram_name =
[&histogram_prefix_pieces](std::string_view metric) {
histogram_prefix_pieces.push_back(metric);
std::string name = base::JoinString(histogram_prefix_pieces, ".");
histogram_prefix_pieces.pop_back();
return name;
};
base::UmaHistogramCounts100000(make_histogram_name("TotalUrlSize"),
url_request.url().spec().size());
// HTTP/2 and HTTP/3 requests don't separate request line from headers so no
// need to record header metrics separately.
if (!IsMultiplexedConnection(response_info)) {
base::UmaHistogramCounts100000(make_histogram_name("TotalHeadersSize"),
raw_request_headers_size);
}
// For HTTP/2 and HTTP/3 the request line is included in the headers, but
// `raw_request_line_size_` is 0 for these requests, so we can add it
// unconditionally for all requests.
size_t total_request_size = raw_request_headers_size + raw_request_line_size;
base::UmaHistogramCounts100000(make_histogram_name("TotalRequestSize"),
total_request_size);
}
void MaybeRecordSharedDictionaryUsedResponseMetrics(
int error_code,
network::mojom::RequestDestination destination,
const net::HttpResponseInfo& response_info,
bool shared_dictionary_allowed_check_passed) {
if (response_info.was_cached) {
return;
}
if (response_info.did_use_shared_dictionary) {
base::UmaHistogramSparse(
base::StrCat({"Net.SharedDictionaryUsedResponseErrorCodes2.",
GetDestinationTypePartString(destination), ".",
GetCertStatePartString(response_info.ssl_info)}),
-error_code);
}
if (shared_dictionary_allowed_check_passed &&
destination == network::mojom::RequestDestination::kDocument) {
base::UmaHistogramBoolean(
base::StrCat(
{"Net.SharedDictionaryUsedByResponseWhenAvailable2.MainFrame.",
net::HttpConnectionInfoCoarseToString(
net::HttpConnectionInfoToCoarse(
response_info.connection_info)),
".", GetCertStatePartString(response_info.ssl_info)}),
response_info.did_use_shared_dictionary);
}
}
} // namespace network::url_loader_util

@ -6,22 +6,37 @@
#define SERVICES_NETWORK_URL_LOADER_UTIL_H_
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/files/file.h"
#include "net/base/isolation_info.h"
#include "net/cookies/cookie_setting_override.h"
namespace base {
class SequencedTaskRunner;
} // namespace base
namespace net {
class HttpRequestHeaders;
class HttpResponseInfo;
class UploadDataStream;
class URLRequest;
} // namespace net
namespace network {
namespace mojom {
enum class RequestDestination;
} // namespace mojom
struct ResourceRequest;
class ResourceRequestBody;
namespace url_loader_util {
// Creates a net::UploadDataStream from the passed `body` and `opened_files`.
// `file_task_runner` will be used for reading file elements in the `body`.
std::unique_ptr<net::UploadDataStream> CreateUploadDataStream(
@ -29,6 +44,45 @@ std::unique_ptr<net::UploadDataStream> CreateUploadDataStream(
std::vector<base::File>& opened_files,
base::SequencedTaskRunner* file_task_runner);
// Computes the CookieSettingOverrides to use for a given `ResourceRequest`.
// May also emit to histograms.
COMPONENT_EXPORT(NETWORK_SERVICE)
net::CookieSettingOverrides CalculateCookieSettingOverrides(
net::CookieSettingOverrides factory_overrides,
net::CookieSettingOverrides devtools_overrides,
const ResourceRequest& request,
bool emit_metrics);
// Determines the IsolationInfo for a request, checking sources in priority:
// 1. `factory_isolation_info` (if non-empty).
// 2. `request.trusted_params->isolation_info` (if non-empty).
// 3. Auto-created based on `request.url` origin if
// `automatically_assign_isolation_info` is true.
// Returns `std::nullopt` if none of the above apply.
std::optional<net::IsolationInfo> GetIsolationInfo(
const net::IsolationInfo& factory_isolation_info,
bool automatically_assign_isolation_info,
const ResourceRequest& request);
// Retrieves the Cookie header from either `cors_exempt_headers` or `headers`.
std::string GetCookiesFromHeaders(
const net::HttpRequestHeaders& headers,
const net::HttpRequestHeaders& cors_exempt_headers);
// Records UMA histograms for request sizes and categorizes them.
void RecordURLLoaderRequestMetrics(const net::URLRequest& url_request,
size_t raw_request_line_size,
size_t raw_request_headers_size);
// Records UMA metrics related to shared dictionary usage for non-cached
// responses.
void MaybeRecordSharedDictionaryUsedResponseMetrics(
int error_code,
network::mojom::RequestDestination destination,
const net::HttpResponseInfo& response_info,
bool shared_dictionary_allowed_check_passed);
} // namespace url_loader_util
} // namespace network
#endif // SERVICES_NETWORK_URL_LOADER_UTIL_H_