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:

committed by
Chromium LUCI CQ

parent
9887e1e336
commit
bed536bbad
@ -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_
|
||||
|
Reference in New Issue
Block a user