blob: 47dae6851d3de515e25c792f6738c58736697a69 [file] [log] [blame]
// Copyright 2020 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 "third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h"
#include "media/media_buildflags.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "net/base/load_flags.h"
#include "net/base/request_priority.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_util.h"
#include "services/network/public/cpp/constants.h"
#include "services/network/public/cpp/optional_trust_token_params.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/resource_request_body.h"
#include "services/network/public/mojom/data_pipe_getter.mojom-blink.h"
#include "services/network/public/mojom/data_pipe_getter.mojom.h"
#include "services/network/public/mojom/trust_tokens.mojom-blink.h"
#include "services/network/public/mojom/trust_tokens.mojom.h"
#include "third_party/blink/public/mojom/blob/blob.mojom.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
#include "third_party/blink/public/platform/cross_variant_mojo_util.h"
#include "third_party/blink/public/platform/file_path_conversion.h"
#include "third_party/blink/public/platform/url_conversion.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/renderer/platform/blob/blob_data.h"
#include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
#include "third_party/blink/renderer/platform/loader/fetch/trust_token_params_conversion.h"
#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
#include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h"
namespace blink {
#if BUILDFLAG(ENABLE_AV1_DECODER)
constexpr char kImageAcceptHeader[] =
"image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
#else
constexpr char kImageAcceptHeader[] =
"image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
#endif
namespace {
constexpr char kStylesheetAcceptHeader[] = "text/css,*/*;q=0.1";
// TODO(yhirano): Unify these with variables in
// content/public/common/content_constants.h.
constexpr char kCorsExemptPurposeHeaderName[] = "Purpose";
constexpr char kCorsExemptRequestedWithHeaderName[] = "X-Requested-With";
// This is complementary to ConvertNetPriorityToWebKitPriority, defined in
// service_worker_context_client.cc.
net::RequestPriority ConvertWebKitPriorityToNetPriority(
WebURLRequest::Priority priority) {
switch (priority) {
case WebURLRequest::Priority::kVeryHigh:
return net::HIGHEST;
case WebURLRequest::Priority::kHigh:
return net::MEDIUM;
case WebURLRequest::Priority::kMedium:
return net::LOW;
case WebURLRequest::Priority::kLow:
return net::LOWEST;
case WebURLRequest::Priority::kVeryLow:
return net::IDLE;
case WebURLRequest::Priority::kUnresolved:
default:
NOTREACHED();
return net::LOW;
}
}
// TODO(yhirano) Dedupe this and the same-name function in
// web_url_request_util.cc.
std::string TrimLWSAndCRLF(const base::StringPiece& input) {
base::StringPiece string = net::HttpUtil::TrimLWS(input);
const char* begin = string.data();
const char* end = string.data() + string.size();
while (begin < end && (end[-1] == '\r' || end[-1] == '\n'))
--end;
return std::string(base::StringPiece(begin, end - begin));
}
mojom::ResourceType RequestContextToResourceType(
mojom::blink::RequestContextType request_context) {
switch (request_context) {
// CSP report
case mojom::blink::RequestContextType::CSP_REPORT:
return mojom::ResourceType::kCspReport;
// Favicon
case mojom::blink::RequestContextType::FAVICON:
return mojom::ResourceType::kFavicon;
// Font
case mojom::blink::RequestContextType::FONT:
return mojom::ResourceType::kFontResource;
// Image
case mojom::blink::RequestContextType::IMAGE:
case mojom::blink::RequestContextType::IMAGE_SET:
return mojom::ResourceType::kImage;
// Media
case mojom::blink::RequestContextType::AUDIO:
case mojom::blink::RequestContextType::VIDEO:
return mojom::ResourceType::kMedia;
// Object
case mojom::blink::RequestContextType::EMBED:
case mojom::blink::RequestContextType::OBJECT:
return mojom::ResourceType::kObject;
// Ping
case mojom::blink::RequestContextType::BEACON:
case mojom::blink::RequestContextType::PING:
return mojom::ResourceType::kPing;
// Subresource of plugins
case mojom::blink::RequestContextType::PLUGIN:
return mojom::ResourceType::kPluginResource;
// Prefetch
case mojom::blink::RequestContextType::PREFETCH:
return mojom::ResourceType::kPrefetch;
// Script
case mojom::blink::RequestContextType::IMPORT:
case mojom::blink::RequestContextType::SCRIPT:
return mojom::ResourceType::kScript;
// Style
case mojom::blink::RequestContextType::XSLT:
case mojom::blink::RequestContextType::STYLE:
return mojom::ResourceType::kStylesheet;
// Subresource
case mojom::blink::RequestContextType::DOWNLOAD:
case mojom::blink::RequestContextType::MANIFEST:
case mojom::blink::RequestContextType::SUBRESOURCE:
case mojom::blink::RequestContextType::SUBRESOURCE_WEBBUNDLE:
return mojom::ResourceType::kSubResource;
// TextTrack
case mojom::blink::RequestContextType::TRACK:
return mojom::ResourceType::kMedia;
// Workers
case mojom::blink::RequestContextType::SERVICE_WORKER:
return mojom::ResourceType::kServiceWorker;
case mojom::blink::RequestContextType::SHARED_WORKER:
return mojom::ResourceType::kSharedWorker;
case mojom::blink::RequestContextType::WORKER:
return mojom::ResourceType::kWorker;
// Unspecified
case mojom::blink::RequestContextType::INTERNAL:
case mojom::blink::RequestContextType::UNSPECIFIED:
return mojom::ResourceType::kSubResource;
// XHR
case mojom::blink::RequestContextType::EVENT_SOURCE:
case mojom::blink::RequestContextType::FETCH:
case mojom::blink::RequestContextType::XML_HTTP_REQUEST:
return mojom::ResourceType::kXhr;
// Navigation requests should not go through WebURLLoader.
case mojom::blink::RequestContextType::FORM:
case mojom::blink::RequestContextType::HYPERLINK:
case mojom::blink::RequestContextType::LOCATION:
case mojom::blink::RequestContextType::FRAME:
case mojom::blink::RequestContextType::IFRAME:
NOTREACHED();
return mojom::ResourceType::kSubResource;
default:
NOTREACHED();
return mojom::ResourceType::kSubResource;
}
}
void PopulateResourceRequestBody(const EncodedFormData& src,
network::ResourceRequestBody* dest) {
for (const auto& element : src.Elements()) {
switch (element.type_) {
case FormDataElement::kData:
dest->AppendBytes(element.data_.data(), element.data_.size());
break;
case FormDataElement::kEncodedFile:
if (element.file_length_ == -1) {
dest->AppendFileRange(
WebStringToFilePath(element.filename_), 0,
std::numeric_limits<uint64_t>::max(),
element.expected_file_modification_time_.value_or(base::Time()));
} else {
dest->AppendFileRange(
WebStringToFilePath(element.filename_),
static_cast<uint64_t>(element.file_start_),
static_cast<uint64_t>(element.file_length_),
element.expected_file_modification_time_.value_or(base::Time()));
}
break;
case FormDataElement::kEncodedBlob: {
DCHECK(element.optional_blob_data_handle_);
mojo::Remote<mojom::blink::Blob> blob_remote(
element.optional_blob_data_handle_->CloneBlobRemote());
mojo::PendingRemote<network::mojom::blink::DataPipeGetter>
data_pipe_getter_remote;
blob_remote->AsDataPipeGetter(
data_pipe_getter_remote.InitWithNewPipeAndPassReceiver());
dest->AppendDataPipe(
ToCrossVariantMojoType(std::move(data_pipe_getter_remote)));
break;
}
case FormDataElement::kDataPipe: {
mojo::PendingRemote<network::mojom::blink::DataPipeGetter>
pending_data_pipe_getter;
element.data_pipe_getter_->GetDataPipeGetter()->Clone(
pending_data_pipe_getter.InitWithNewPipeAndPassReceiver());
dest->AppendDataPipe(
ToCrossVariantMojoType(std::move(pending_data_pipe_getter)));
break;
}
}
}
}
} // namespace
scoped_refptr<network::ResourceRequestBody> NetworkResourceRequestBodyFor(
ResourceRequestBody src_body,
bool allow_http1_for_streaming_upload) {
scoped_refptr<network::ResourceRequestBody> dest_body;
if (const EncodedFormData* form_body = src_body.FormBody().get()) {
dest_body = base::MakeRefCounted<network::ResourceRequestBody>();
PopulateResourceRequestBody(*form_body, dest_body.get());
} else if (src_body.StreamBody().is_valid()) {
mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>
stream_body = src_body.TakeStreamBody();
dest_body = base::MakeRefCounted<network::ResourceRequestBody>();
dest_body->SetToChunkedDataPipe(
ToCrossVariantMojoType(std::move(stream_body)),
network::ResourceRequestBody::ReadOnlyOnce(true));
dest_body->SetAllowHTTP1ForStreamingUpload(
allow_http1_for_streaming_upload);
}
return dest_body;
}
void PopulateResourceRequest(const ResourceRequestHead& src,
ResourceRequestBody src_body,
network::ResourceRequest* dest) {
dest->method = src.HttpMethod().Latin1();
dest->url = src.Url();
dest->site_for_cookies = src.SiteForCookies();
dest->upgrade_if_insecure = src.UpgradeIfInsecure();
dest->is_revalidating = src.IsRevalidating();
if (src.RequestorOrigin()->ToString() == "null") {
// "file:" origin is treated like an opaque unique origin when
// allow-file-access-from-files is not specified. Such origin is not opaque
// (i.e., IsOpaque() returns false) but still serializes to "null". Derive a
// new opaque origin so that downstream consumers can make use of the
// origin's precursor.
dest->request_initiator =
src.RequestorOrigin()->DeriveNewOpaqueOrigin()->ToUrlOrigin();
} else {
dest->request_initiator = src.RequestorOrigin()->ToUrlOrigin();
}
if (src.IsolatedWorldOrigin()) {
dest->isolated_world_origin = src.IsolatedWorldOrigin()->ToUrlOrigin();
}
dest->referrer = WebStringToGURL(src.ReferrerString());
// "default" referrer policy has already been resolved.
DCHECK_NE(src.GetReferrerPolicy(), network::mojom::ReferrerPolicy::kDefault);
dest->referrer_policy =
network::ReferrerPolicyForUrlRequest(src.GetReferrerPolicy());
for (const auto& item : src.HttpHeaderFields()) {
const std::string name = item.key.Latin1();
const std::string value = TrimLWSAndCRLF(item.value.Latin1());
dest->headers.SetHeader(name, value);
}
// Set X-Requested-With header to cors_exempt_headers rather than headers to
// be exempted from CORS checks.
if (!src.GetRequestedWithHeader().IsEmpty()) {
dest->cors_exempt_headers.SetHeader(kCorsExemptRequestedWithHeaderName,
src.GetRequestedWithHeader().Utf8());
}
// Set Purpose header to cors_exempt_headers rather than headers to be
// exempted from CORS checks.
if (!src.GetPurposeHeader().IsEmpty()) {
dest->cors_exempt_headers.SetHeader(kCorsExemptPurposeHeaderName,
src.GetPurposeHeader().Utf8());
}
// TODO(yhirano): Remove this WrappedResourceRequest.
dest->load_flags = WrappedResourceRequest(ResourceRequest(src))
.GetLoadFlagsForWebUrlRequest();
dest->recursive_prefetch_token = src.RecursivePrefetchToken();
dest->priority = ConvertWebKitPriorityToNetPriority(src.Priority());
dest->should_reset_appcache = src.ShouldResetAppCache();
dest->is_external_request = src.IsExternalRequest();
dest->cors_preflight_policy = src.CorsPreflightPolicy();
dest->skip_service_worker = src.GetSkipServiceWorker();
dest->mode = src.GetMode();
dest->destination = src.GetRequestDestination();
dest->credentials_mode = src.GetCredentialsMode();
dest->redirect_mode = src.GetRedirectMode();
dest->fetch_integrity = src.GetFetchIntegrity().Utf8();
if (src.GetWebBundleTokenParams().has_value()) {
dest->web_bundle_token_params =
base::make_optional(network::ResourceRequest::WebBundleTokenParams(
src.GetWebBundleTokenParams()->bundle_url,
src.GetWebBundleTokenParams()->token,
src.GetWebBundleTokenParams()->CloneHandle()));
}
// TODO(kinuko): Deprecate this.
dest->resource_type =
static_cast<int>(RequestContextToResourceType(src.GetRequestContext()));
if (src.IsFetchLikeAPI() &&
(dest->url.has_username() || dest->url.has_password())) {
dest->do_not_prompt_for_login = true;
}
if (src.GetRequestContext() == mojom::blink::RequestContextType::PREFETCH ||
src.IsFavicon()) {
dest->do_not_prompt_for_login = true;
}
dest->keepalive = src.GetKeepalive();
dest->has_user_gesture = src.HasUserGesture();
dest->enable_load_timing = true;
dest->enable_upload_progress = src.ReportUploadProgress();
dest->report_raw_headers = src.ReportRawHeaders();
// TODO(ryansturm): Remove dest->previews_state once it is no
// longer used in a network delegate. https://crbug.com/842233
dest->previews_state = static_cast<int>(src.GetPreviewsState());
dest->throttling_profile_id = src.GetDevToolsToken();
dest->trust_token_params = ConvertTrustTokenParams(src.TrustTokenParams());
if (base::UnguessableToken window_id = src.GetFetchWindowId())
dest->fetch_window_id = base::make_optional(window_id);
if (src.GetDevToolsId().has_value()) {
dest->devtools_request_id = src.GetDevToolsId().value().Ascii();
}
if (src.GetDevToolsStackId().has_value()) {
dest->devtools_stack_id = src.GetDevToolsStackId().value().Ascii();
}
if (src.IsSignedExchangePrefetchCacheEnabled()) {
DCHECK_EQ(src.GetRequestContext(),
mojom::blink::RequestContextType::PREFETCH);
dest->is_signed_exchange_prefetch_cache_enabled = true;
}
dest->is_fetch_like_api = src.IsFetchLikeAPI();
dest->is_favicon = src.IsFavicon();
dest->request_body = NetworkResourceRequestBodyFor(
std::move(src_body), src.AllowHTTP1ForStreamingUpload());
if (dest->request_body) {
DCHECK_NE(dest->method, net::HttpRequestHeaders::kGetMethod);
DCHECK_NE(dest->method, net::HttpRequestHeaders::kHeadMethod);
}
network::mojom::RequestDestination request_destination =
src.GetRequestDestination();
if (request_destination == network::mojom::RequestDestination::kStyle ||
request_destination == network::mojom::RequestDestination::kXslt) {
dest->headers.SetHeader(net::HttpRequestHeaders::kAccept,
kStylesheetAcceptHeader);
} else if (request_destination ==
network::mojom::RequestDestination::kImage) {
dest->headers.SetHeaderIfMissing(net::HttpRequestHeaders::kAccept,
kImageAcceptHeader);
} else {
// Calling SetHeaderIfMissing() instead of SetHeader() because JS can
// manually set an accept header on an XHR.
dest->headers.SetHeaderIfMissing(net::HttpRequestHeaders::kAccept,
network::kDefaultAcceptHeaderValue);
}
}
} // namespace blink