blob: 36d7cd70797e94ae881e2e2f6a9aff3378ffcb2a [file] [log] [blame]
/*
* Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
* Copyright (C) 2009, 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
#include <memory>
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/renderer/platform/network/http_names.h"
#include "third_party/blink/renderer/platform/network/network_utils.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
namespace blink {
base::TimeDelta ResourceRequest::default_timeout_interval_ =
base::TimeDelta::Max();
ResourceRequest::ResourceRequest() : ResourceRequest(NullURL()) {}
ResourceRequest::ResourceRequest(const String& url_string)
: ResourceRequest(KURL(url_string)) {}
ResourceRequest::ResourceRequest(const KURL& url)
: url_(url),
timeout_interval_(default_timeout_interval_),
requestor_origin_(nullptr),
http_method_(HTTPNames::GET),
allow_stored_credentials_(true),
report_upload_progress_(false),
report_raw_headers_(false),
has_user_gesture_(false),
download_to_blob_(false),
use_stream_on_response_(false),
keepalive_(false),
should_reset_app_cache_(false),
allow_stale_response_(false),
cache_mode_(mojom::FetchCacheMode::kDefault),
skip_service_worker_(false),
priority_(ResourceLoadPriority::kLowest),
intra_priority_value_(0),
requestor_id_(0),
plugin_child_id_(-1),
app_cache_host_id_(0),
previews_state_(WebURLRequest::kPreviewsUnspecified),
request_context_(WebURLRequest::kRequestContextUnspecified),
frame_type_(network::mojom::RequestContextFrameType::kNone),
fetch_request_mode_(network::mojom::FetchRequestMode::kNoCORS),
fetch_importance_mode_(mojom::FetchImportanceMode::kImportanceAuto),
fetch_credentials_mode_(network::mojom::FetchCredentialsMode::kInclude),
fetch_redirect_mode_(network::mojom::FetchRedirectMode::kFollow),
referrer_policy_(kReferrerPolicyDefault),
did_set_http_referrer_(false),
check_for_browser_side_navigation_(true),
was_discarded_(false),
ui_start_time_(0),
is_external_request_(false),
cors_preflight_policy_(
network::mojom::CORSPreflightPolicy::kConsiderPreflight),
input_perf_metric_report_policy_(
InputToLoadPerfMetricReportPolicy::kNoReport),
redirect_status_(RedirectStatus::kNoRedirect) {}
ResourceRequest::ResourceRequest(CrossThreadResourceRequestData* data)
: ResourceRequest(data->url_) {
SetTimeoutInterval(data->timeout_interval_);
SetSiteForCookies(data->site_for_cookies_);
SetRequestorOrigin(data->requestor_origin_);
SetHTTPMethod(AtomicString(data->http_method_));
SetPriority(data->priority_, data->intra_priority_value_);
http_header_fields_.Adopt(std::move(data->http_headers_));
SetHTTPBody(data->http_body_);
SetAllowStoredCredentials(data->allow_stored_credentials_);
SetReportUploadProgress(data->report_upload_progress_);
SetHasUserGesture(data->has_user_gesture_);
SetDownloadToBlob(data->download_to_blob_);
SetUseStreamOnResponse(data->use_stream_on_response_);
SetKeepalive(data->keepalive_);
SetCacheMode(data->cache_mode_);
SetSkipServiceWorker(data->skip_service_worker_);
SetShouldResetAppCache(data->should_reset_app_cache_);
SetRequestorID(data->requestor_id_);
SetPluginChildID(data->plugin_child_id_);
SetAppCacheHostID(data->app_cache_host_id_);
SetPreviewsState(data->previews_state_);
SetRequestContext(data->request_context_);
SetFrameType(data->frame_type_);
SetFetchRequestMode(data->fetch_request_mode_);
SetFetchImportanceMode(data->fetch_importance_mode_);
SetFetchCredentialsMode(data->fetch_credentials_mode_);
SetFetchRedirectMode(data->fetch_redirect_mode_);
SetFetchIntegrity(data->fetch_integrity_.IsolatedCopy());
referrer_policy_ = data->referrer_policy_;
did_set_http_referrer_ = data->did_set_http_referrer_;
check_for_browser_side_navigation_ = data->check_for_browser_side_navigation_;
ui_start_time_ = data->ui_start_time_;
is_external_request_ = data->is_external_request_;
cors_preflight_policy_ = data->cors_preflight_policy_;
input_perf_metric_report_policy_ = data->input_perf_metric_report_policy_;
redirect_status_ = data->redirect_status_;
suggested_filename_ = data->suggested_filename_;
is_ad_resource_ = data->is_ad_resource_;
SetInitiatorCSP(data->navigation_csp_);
upgrade_if_insecure_ = data->upgrade_if_insecure_;
}
ResourceRequest::ResourceRequest(const ResourceRequest&) = default;
ResourceRequest& ResourceRequest::operator=(const ResourceRequest&) = default;
std::unique_ptr<ResourceRequest> ResourceRequest::CreateRedirectRequest(
const KURL& new_url,
const AtomicString& new_method,
const KURL& new_site_for_cookies,
const String& new_referrer,
ReferrerPolicy new_referrer_policy,
bool skip_service_worker) const {
std::unique_ptr<ResourceRequest> request =
std::make_unique<ResourceRequest>(new_url);
request->SetHTTPMethod(new_method);
request->SetSiteForCookies(new_site_for_cookies);
String referrer =
new_referrer.IsEmpty() ? Referrer::NoReferrer() : String(new_referrer);
request->SetHTTPReferrer(
Referrer(referrer, static_cast<ReferrerPolicy>(new_referrer_policy)));
request->SetSkipServiceWorker(skip_service_worker);
request->SetRedirectStatus(RedirectStatus::kFollowedRedirect);
// Copy from parameters for |this|.
request->SetDownloadToBlob(DownloadToBlob());
request->SetUseStreamOnResponse(UseStreamOnResponse());
request->SetRequestContext(GetRequestContext());
request->SetFrameType(GetFrameType());
request->SetShouldResetAppCache(ShouldResetAppCache());
request->SetFetchRequestMode(GetFetchRequestMode());
request->SetFetchCredentialsMode(GetFetchCredentialsMode());
request->SetKeepalive(GetKeepalive());
request->SetPriority(Priority());
if (request->HttpMethod() == HttpMethod())
request->SetHTTPBody(HttpBody());
request->SetCheckForBrowserSideNavigation(CheckForBrowserSideNavigation());
request->SetWasDiscarded(WasDiscarded());
request->SetCORSPreflightPolicy(CORSPreflightPolicy());
if (IsAdResource())
request->SetIsAdResource();
request->SetInitiatorCSP(GetInitiatorCSP());
request->SetUpgradeIfInsecure(UpgradeIfInsecure());
return request;
}
std::unique_ptr<CrossThreadResourceRequestData> ResourceRequest::CopyData()
const {
std::unique_ptr<CrossThreadResourceRequestData> data =
std::make_unique<CrossThreadResourceRequestData>();
data->url_ = Url().Copy();
data->timeout_interval_ = TimeoutInterval();
data->site_for_cookies_ = SiteForCookies().Copy();
data->requestor_origin_ =
RequestorOrigin() ? RequestorOrigin()->IsolatedCopy() : nullptr;
data->http_method_ = HttpMethod().GetString().IsolatedCopy();
data->http_headers_ = HttpHeaderFields().CopyData();
data->priority_ = Priority();
data->intra_priority_value_ = intra_priority_value_;
if (http_body_)
data->http_body_ = http_body_->DeepCopy();
data->allow_stored_credentials_ = allow_stored_credentials_;
data->report_upload_progress_ = report_upload_progress_;
data->has_user_gesture_ = has_user_gesture_;
data->download_to_blob_ = download_to_blob_;
data->use_stream_on_response_ = use_stream_on_response_;
data->keepalive_ = keepalive_;
data->cache_mode_ = GetCacheMode();
data->skip_service_worker_ = skip_service_worker_;
data->should_reset_app_cache_ = should_reset_app_cache_;
data->requestor_id_ = requestor_id_;
data->plugin_child_id_ = plugin_child_id_;
data->app_cache_host_id_ = app_cache_host_id_;
data->previews_state_ = previews_state_;
data->request_context_ = request_context_;
data->frame_type_ = frame_type_;
data->fetch_request_mode_ = fetch_request_mode_;
data->fetch_importance_mode_ = fetch_importance_mode_;
data->fetch_credentials_mode_ = fetch_credentials_mode_;
data->fetch_redirect_mode_ = fetch_redirect_mode_;
data->fetch_integrity_ = fetch_integrity_.IsolatedCopy();
data->referrer_policy_ = referrer_policy_;
data->did_set_http_referrer_ = did_set_http_referrer_;
data->check_for_browser_side_navigation_ = check_for_browser_side_navigation_;
data->ui_start_time_ = ui_start_time_;
data->is_external_request_ = is_external_request_;
data->cors_preflight_policy_ = cors_preflight_policy_;
data->input_perf_metric_report_policy_ = input_perf_metric_report_policy_;
data->redirect_status_ = redirect_status_;
data->suggested_filename_ = suggested_filename_;
data->is_ad_resource_ = is_ad_resource_;
data->navigation_csp_ = initiator_csp_;
data->upgrade_if_insecure_ = upgrade_if_insecure_;
return data;
}
bool ResourceRequest::IsNull() const {
return url_.IsNull();
}
const KURL& ResourceRequest::Url() const {
return url_;
}
void ResourceRequest::SetURL(const KURL& url) {
url_ = url;
}
void ResourceRequest::RemoveUserAndPassFromURL() {
if (url_.User().IsEmpty() && url_.Pass().IsEmpty())
return;
url_.SetUser(String());
url_.SetPass(String());
}
mojom::FetchCacheMode ResourceRequest::GetCacheMode() const {
return cache_mode_;
}
void ResourceRequest::SetCacheMode(mojom::FetchCacheMode cache_mode) {
cache_mode_ = cache_mode;
}
base::TimeDelta ResourceRequest::TimeoutInterval() const {
return timeout_interval_;
}
void ResourceRequest::SetTimeoutInterval(
base::TimeDelta timout_interval_seconds) {
timeout_interval_ = timout_interval_seconds;
}
const KURL& ResourceRequest::SiteForCookies() const {
return site_for_cookies_;
}
void ResourceRequest::SetSiteForCookies(const KURL& site_for_cookies) {
site_for_cookies_ = site_for_cookies;
}
scoped_refptr<const SecurityOrigin> ResourceRequest::RequestorOrigin() const {
return requestor_origin_;
}
void ResourceRequest::SetRequestorOrigin(
scoped_refptr<const SecurityOrigin> requestor_origin) {
requestor_origin_ = std::move(requestor_origin);
}
const AtomicString& ResourceRequest::HttpMethod() const {
return http_method_;
}
void ResourceRequest::SetHTTPMethod(const AtomicString& http_method) {
http_method_ = http_method;
}
const HTTPHeaderMap& ResourceRequest::HttpHeaderFields() const {
return http_header_fields_;
}
const AtomicString& ResourceRequest::HttpHeaderField(
const AtomicString& name) const {
return http_header_fields_.Get(name);
}
void ResourceRequest::SetHTTPHeaderField(const AtomicString& name,
const AtomicString& value) {
http_header_fields_.Set(name, value);
}
void ResourceRequest::SetHTTPReferrer(const Referrer& referrer) {
if (referrer.referrer.IsEmpty())
http_header_fields_.Remove(HTTPNames::Referer);
else
SetHTTPHeaderField(HTTPNames::Referer, referrer.referrer);
referrer_policy_ = referrer.referrer_policy;
did_set_http_referrer_ = true;
}
void ResourceRequest::ClearHTTPReferrer() {
http_header_fields_.Remove(HTTPNames::Referer);
referrer_policy_ = kReferrerPolicyDefault;
did_set_http_referrer_ = false;
}
void ResourceRequest::SetHTTPOrigin(const SecurityOrigin* origin) {
SetHTTPHeaderField(HTTPNames::Origin, origin->ToAtomicString());
}
void ResourceRequest::ClearHTTPOrigin() {
http_header_fields_.Remove(HTTPNames::Origin);
}
void ResourceRequest::SetHTTPOriginIfNeeded(const SecurityOrigin* origin) {
if (NeedsHTTPOrigin())
SetHTTPOrigin(origin);
}
void ResourceRequest::SetHTTPOriginToMatchReferrerIfNeeded() {
if (NeedsHTTPOrigin()) {
SetHTTPOrigin(
SecurityOrigin::CreateFromString(HttpHeaderField(HTTPNames::Referer))
.get());
}
}
void ResourceRequest::ClearHTTPUserAgent() {
http_header_fields_.Remove(HTTPNames::User_Agent);
}
EncodedFormData* ResourceRequest::HttpBody() const {
return http_body_.get();
}
void ResourceRequest::SetHTTPBody(scoped_refptr<EncodedFormData> http_body) {
http_body_ = std::move(http_body);
}
bool ResourceRequest::AllowStoredCredentials() const {
return allow_stored_credentials_;
}
void ResourceRequest::SetAllowStoredCredentials(bool allow_credentials) {
allow_stored_credentials_ = allow_credentials;
}
ResourceLoadPriority ResourceRequest::Priority() const {
return priority_;
}
int ResourceRequest::IntraPriorityValue() const {
return intra_priority_value_;
}
void ResourceRequest::SetPriority(ResourceLoadPriority priority,
int intra_priority_value) {
priority_ = priority;
intra_priority_value_ = intra_priority_value;
}
void ResourceRequest::AddHTTPHeaderField(const AtomicString& name,
const AtomicString& value) {
HTTPHeaderMap::AddResult result = http_header_fields_.Add(name, value);
if (!result.is_new_entry)
result.stored_value->value = result.stored_value->value + ", " + value;
}
void ResourceRequest::AddHTTPHeaderFields(const HTTPHeaderMap& header_fields) {
HTTPHeaderMap::const_iterator end = header_fields.end();
for (HTTPHeaderMap::const_iterator it = header_fields.begin(); it != end;
++it)
AddHTTPHeaderField(it->key, it->value);
}
void ResourceRequest::ClearHTTPHeaderField(const AtomicString& name) {
http_header_fields_.Remove(name);
}
void ResourceRequest::SetExternalRequestStateFromRequestorAddressSpace(
mojom::IPAddressSpace requestor_space) {
static_assert(mojom::IPAddressSpace::kLocal < mojom::IPAddressSpace::kPrivate,
"Local is inside Private");
static_assert(mojom::IPAddressSpace::kLocal < mojom::IPAddressSpace::kPublic,
"Local is inside Public");
static_assert(
mojom::IPAddressSpace::kPrivate < mojom::IPAddressSpace::kPublic,
"Private is inside Public");
// TODO(mkwst): This only checks explicit IP addresses. We'll have to move all
// this up to //net and //content in order to have any real impact on gateway
// attacks. That turns out to be a TON of work. https://crbug.com/378566
if (!RuntimeEnabledFeatures::CorsRFC1918Enabled()) {
is_external_request_ = false;
return;
}
mojom::IPAddressSpace target_space = mojom::IPAddressSpace::kPublic;
if (NetworkUtils::IsReservedIPAddress(url_.Host()))
target_space = mojom::IPAddressSpace::kPrivate;
if (SecurityOrigin::Create(url_)->IsLocalhost())
target_space = mojom::IPAddressSpace::kLocal;
is_external_request_ = requestor_space > target_space;
}
void ResourceRequest::SetNavigationStartTime(TimeTicks navigation_start) {
navigation_start_ = navigation_start;
}
bool ResourceRequest::IsConditional() const {
return (http_header_fields_.Contains(HTTPNames::If_Match) ||
http_header_fields_.Contains(HTTPNames::If_Modified_Since) ||
http_header_fields_.Contains(HTTPNames::If_None_Match) ||
http_header_fields_.Contains(HTTPNames::If_Range) ||
http_header_fields_.Contains(HTTPNames::If_Unmodified_Since));
}
void ResourceRequest::SetHasUserGesture(bool has_user_gesture) {
has_user_gesture_ |= has_user_gesture;
}
const CacheControlHeader& ResourceRequest::GetCacheControlHeader() const {
if (!cache_control_header_cache_.parsed) {
cache_control_header_cache_ = ParseCacheControlDirectives(
http_header_fields_.Get(HTTPNames::Cache_Control),
http_header_fields_.Get(HTTPNames::Pragma));
}
return cache_control_header_cache_;
}
bool ResourceRequest::CacheControlContainsNoCache() const {
return GetCacheControlHeader().contains_no_cache;
}
bool ResourceRequest::CacheControlContainsNoStore() const {
return GetCacheControlHeader().contains_no_store;
}
bool ResourceRequest::HasCacheValidatorFields() const {
return !http_header_fields_.Get(HTTPNames::Last_Modified).IsEmpty() ||
!http_header_fields_.Get(HTTPNames::ETag).IsEmpty();
}
bool ResourceRequest::NeedsHTTPOrigin() const {
if (!HttpOrigin().IsEmpty())
return false; // Request already has an Origin header.
// Don't send an Origin header for GET or HEAD to avoid privacy issues.
// For example, if an intranet page has a hyperlink to an external web
// site, we don't want to include the Origin of the request because it
// will leak the internal host name. Similar privacy concerns have lead
// to the widespread suppression of the Referer header at the network
// layer.
if (HttpMethod() == HTTPNames::GET || HttpMethod() == HTTPNames::HEAD)
return false;
// For non-GET and non-HEAD methods, always send an Origin header so the
// server knows we support this feature.
return true;
}
} // namespace blink