blob: 7af15a15016a126152192d575f641e5e220d762e [file] [log] [blame]
// Copyright 2014 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 "content/browser/devtools/protocol/network_handler.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "base/barrier_closure.h"
#include "base/base64.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/containers/queue.h"
#include "base/json/json_reader.h"
#include "base/process/process_handle.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/task/post_task.h"
#include "base/time/time.h"
#include "content/browser/background_sync/background_sync_manager.h"
#include "content/browser/devtools/devtools_agent_host_impl.h"
#include "content/browser/devtools/devtools_io_context.h"
#include "content/browser/devtools/devtools_stream_pipe.h"
#include "content/browser/devtools/devtools_url_loader_interceptor.h"
#include "content/browser/devtools/protocol/network.h"
#include "content/browser/devtools/protocol/page.h"
#include "content/browser/devtools/protocol/security.h"
#include "content/browser/devtools/service_worker_devtools_agent_host.h"
#include "content/browser/devtools/service_worker_devtools_manager.h"
#include "content/browser/frame_host/frame_tree_node.h"
#include "content/browser/frame_host/navigation_request.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/storage_partition_impl.h"
#include "content/browser/web_package/signed_exchange_envelope.h"
#include "content/browser/web_package/signed_exchange_error.h"
#include "content/common/navigation_params.h"
#include "content/common/web_package/signed_exchange_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/global_request_id.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/origin_util.h"
#include "content/public/common/referrer.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/cert/ct_policy_status.h"
#include "net/cert/ct_sct_to_string.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_inclusion_status.h"
#include "net/cookies/cookie_util.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "services/network/public/cpp/data_element.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/http_raw_request_response_info.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/platform/resource_request_blocked_reason.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
namespace content {
namespace protocol {
namespace {
using GetCookiesCallback = Network::Backend::GetCookiesCallback;
using GetAllCookiesCallback = Network::Backend::GetAllCookiesCallback;
using SetCookieCallback = Network::Backend::SetCookieCallback;
using SetCookiesCallback = Network::Backend::SetCookiesCallback;
using DeleteCookiesCallback = Network::Backend::DeleteCookiesCallback;
using ClearBrowserCookiesCallback =
Network::Backend::ClearBrowserCookiesCallback;
Network::CertificateTransparencyCompliance SerializeCTPolicyCompliance(
net::ct::CTPolicyCompliance ct_compliance) {
switch (ct_compliance) {
case net::ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS:
return Network::CertificateTransparencyComplianceEnum::Compliant;
case net::ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS:
case net::ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS:
return Network::CertificateTransparencyComplianceEnum::NotCompliant;
case net::ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY:
case net::ct::CTPolicyCompliance::
CT_POLICY_COMPLIANCE_DETAILS_NOT_AVAILABLE:
return Network::CertificateTransparencyComplianceEnum::Unknown;
case net::ct::CTPolicyCompliance::CT_POLICY_COUNT:
NOTREACHED();
return Network::CertificateTransparencyComplianceEnum::Unknown;
}
NOTREACHED();
return Network::CertificateTransparencyComplianceEnum::Unknown;
}
std::unique_ptr<Network::Cookie> BuildCookie(
const net::CanonicalCookie& cookie) {
String cp;
switch (cookie.Priority()) {
case net::CookiePriority::COOKIE_PRIORITY_HIGH:
cp = Network::CookiePriorityEnum::High;
break;
case net::CookiePriority::COOKIE_PRIORITY_MEDIUM:
cp = Network::CookiePriorityEnum::Medium;
break;
case net::CookiePriority::COOKIE_PRIORITY_LOW:
cp = Network::CookiePriorityEnum::Low;
break;
}
std::unique_ptr<Network::Cookie> devtools_cookie =
Network::Cookie::Create()
.SetName(cookie.Name())
.SetValue(cookie.Value())
.SetDomain(cookie.Domain())
.SetPath(cookie.Path())
.SetExpires(cookie.ExpiryDate().is_null()
? -1
: cookie.ExpiryDate().ToDoubleT())
.SetSize(cookie.Name().length() + cookie.Value().length())
.SetHttpOnly(cookie.IsHttpOnly())
.SetSecure(cookie.IsSecure())
.SetSession(!cookie.IsPersistent())
.SetPriority(cp)
.Build();
switch (cookie.SameSite()) {
case net::CookieSameSite::STRICT_MODE:
devtools_cookie->SetSameSite(Network::CookieSameSiteEnum::Strict);
break;
case net::CookieSameSite::LAX_MODE:
devtools_cookie->SetSameSite(Network::CookieSameSiteEnum::Lax);
break;
case net::CookieSameSite::NO_RESTRICTION:
devtools_cookie->SetSameSite(Network::CookieSameSiteEnum::None);
break;
case net::CookieSameSite::UNSPECIFIED:
break;
}
return devtools_cookie;
}
class CookieRetrieverNetworkService
: public base::RefCounted<CookieRetrieverNetworkService> {
public:
static void Retrieve(network::mojom::CookieManager* cookie_manager,
const std::vector<GURL> urls,
std::unique_ptr<GetCookiesCallback> callback) {
scoped_refptr<CookieRetrieverNetworkService> self =
new CookieRetrieverNetworkService(std::move(callback));
net::CookieOptions cookie_options = net::CookieOptions::MakeAllInclusive();
for (const auto& url : urls) {
cookie_manager->GetCookieList(
url, cookie_options,
base::BindOnce(&CookieRetrieverNetworkService::GotCookies, self));
}
}
private:
friend class base::RefCounted<CookieRetrieverNetworkService>;
CookieRetrieverNetworkService(std::unique_ptr<GetCookiesCallback> callback)
: callback_(std::move(callback)) {}
void GotCookies(const net::CookieAccessResultList& cookies,
const net::CookieAccessResultList& excluded_cookies) {
for (const auto& cookie_with_access_result : cookies) {
const net::CanonicalCookie& cookie = cookie_with_access_result.cookie;
std::string key = base::StringPrintf(
"%s::%s::%s::%d", cookie.Name().c_str(), cookie.Domain().c_str(),
cookie.Path().c_str(), cookie.IsSecure());
all_cookies_.emplace(std::move(key), cookie);
}
}
~CookieRetrieverNetworkService() {
auto cookies = std::make_unique<Array<Network::Cookie>>();
for (const auto& entry : all_cookies_)
cookies->emplace_back(BuildCookie(entry.second));
callback_->sendSuccess(std::move(cookies));
}
std::unique_ptr<GetCookiesCallback> callback_;
std::unordered_map<std::string, net::CanonicalCookie> all_cookies_;
};
std::vector<net::CanonicalCookie> FilterCookies(
const std::vector<net::CanonicalCookie>& cookies,
const std::string& name,
const std::string& normalized_domain,
const std::string& path) {
std::vector<net::CanonicalCookie> result;
for (const auto& cookie : cookies) {
if (cookie.Name() != name)
continue;
if (cookie.Domain() != normalized_domain)
continue;
if (!path.empty() && cookie.Path() != path)
continue;
result.push_back(cookie);
}
return result;
}
void DeleteFilteredCookies(network::mojom::CookieManager* cookie_manager,
const std::string& name,
const std::string& normalized_domain,
const std::string& path,
std::unique_ptr<DeleteCookiesCallback> callback,
const std::vector<net::CanonicalCookie>& cookies) {
std::vector<net::CanonicalCookie> filtered_list =
FilterCookies(cookies, name, normalized_domain, path);
base::RepeatingClosure barrier_closure = base::BarrierClosure(
filtered_list.size(),
base::BindOnce(&DeleteCookiesCallback::sendSuccess, std::move(callback)));
for (auto& cookie : filtered_list) {
cookie_manager->DeleteCanonicalCookie(
cookie, base::BindOnce([](base::RepeatingClosure callback,
bool) { callback.Run(); },
barrier_closure));
}
}
std::unique_ptr<net::CanonicalCookie> MakeCookieFromProtocolValues(
const std::string& name,
const std::string& value,
const std::string& url_spec,
const std::string& domain,
const std::string& path,
bool secure,
bool http_only,
const std::string& same_site,
double expires,
const std::string& priority) {
std::string normalized_domain = domain;
if (url_spec.empty() && domain.empty())
return nullptr;
if (!url_spec.empty()) {
GURL source_url = GURL(url_spec);
if (!source_url.SchemeIsHTTPOrHTTPS())
return nullptr;
secure = secure || source_url.SchemeIsCryptographic();
if (normalized_domain.empty())
normalized_domain = source_url.host();
}
std::string url_host = normalized_domain;
if (!normalized_domain.empty()) {
// The value of |url_host| may have trickled down from a cookie domain,
// where leading periods are legal. However, since we want to use it as a
// URL host, we must the leading period if it exists.
if (normalized_domain[0] == '.')
url_host.erase(0, 1);
// If there is no leading period, clear out |normalized_domain|, but keep
// the value of |url_host|. CreateSanitizedCookie will determine the proper
// domain from the URL we construct with |url_host|.
else
normalized_domain = "";
}
GURL url = GURL((secure ? "https://" : "http://") + url_host);
base::Time expiration_date;
if (expires >= 0) {
expiration_date =
expires ? base::Time::FromDoubleT(expires) : base::Time::UnixEpoch();
}
net::CookieSameSite css = net::CookieSameSite::UNSPECIFIED;
if (same_site == Network::CookieSameSiteEnum::Lax)
css = net::CookieSameSite::LAX_MODE;
if (same_site == Network::CookieSameSiteEnum::Strict)
css = net::CookieSameSite::STRICT_MODE;
if (same_site == Network::CookieSameSiteEnum::None)
css = net::CookieSameSite::NO_RESTRICTION;
net::CookiePriority cp = net::CookiePriority::COOKIE_PRIORITY_MEDIUM;
if (priority == Network::CookiePriorityEnum::High)
cp = net::CookiePriority::COOKIE_PRIORITY_HIGH;
else if (priority == Network::CookiePriorityEnum::Medium)
cp = net::CookiePriority::COOKIE_PRIORITY_MEDIUM;
else if (priority == Network::CookiePriorityEnum::Low)
cp = net::CookiePriority::COOKIE_PRIORITY_LOW;
return net::CanonicalCookie::CreateSanitizedCookie(
url, name, value, normalized_domain, path, base::Time(), expiration_date,
base::Time(), secure, http_only, css, cp);
}
std::vector<GURL> ComputeCookieURLs(RenderFrameHostImpl* frame_host,
Maybe<Array<String>>& protocol_urls) {
std::vector<GURL> urls;
if (protocol_urls.isJust()) {
for (const std::string& url : *protocol_urls.fromJust())
urls.emplace_back(url);
} else {
base::queue<FrameTreeNode*> queue;
queue.push(frame_host->frame_tree_node());
while (!queue.empty()) {
FrameTreeNode* node = queue.front();
queue.pop();
urls.push_back(node->current_url());
for (size_t i = 0; i < node->child_count(); ++i)
queue.push(node->child_at(i));
}
}
return urls;
}
String resourcePriority(net::RequestPriority priority) {
switch (priority) {
case net::MINIMUM_PRIORITY:
case net::IDLE:
return Network::ResourcePriorityEnum::VeryLow;
case net::LOWEST:
return Network::ResourcePriorityEnum::Low;
case net::LOW:
return Network::ResourcePriorityEnum::Medium;
case net::MEDIUM:
return Network::ResourcePriorityEnum::High;
case net::HIGHEST:
return Network::ResourcePriorityEnum::VeryHigh;
}
NOTREACHED();
return Network::ResourcePriorityEnum::Medium;
}
String referrerPolicy(network::mojom::ReferrerPolicy referrer_policy) {
switch (referrer_policy) {
case network::mojom::ReferrerPolicy::kAlways:
return Network::Request::ReferrerPolicyEnum::UnsafeUrl;
case network::mojom::ReferrerPolicy::kDefault:
return referrerPolicy(Referrer::NetReferrerPolicyToBlinkReferrerPolicy(
content::Referrer::GetDefaultReferrerPolicy()));
case network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade:
return Network::Request::ReferrerPolicyEnum::NoReferrerWhenDowngrade;
case network::mojom::ReferrerPolicy::kNever:
return Network::Request::ReferrerPolicyEnum::NoReferrer;
case network::mojom::ReferrerPolicy::kOrigin:
return Network::Request::ReferrerPolicyEnum::Origin;
case network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin:
return Network::Request::ReferrerPolicyEnum::OriginWhenCrossOrigin;
case network::mojom::ReferrerPolicy::kSameOrigin:
return Network::Request::ReferrerPolicyEnum::SameOrigin;
case network::mojom::ReferrerPolicy::kStrictOrigin:
return Network::Request::ReferrerPolicyEnum::StrictOrigin;
case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin:
return Network::Request::ReferrerPolicyEnum::StrictOriginWhenCrossOrigin;
}
NOTREACHED();
return Network::Request::ReferrerPolicyEnum::NoReferrerWhenDowngrade;
}
String referrerPolicy(net::URLRequest::ReferrerPolicy referrer_policy) {
return referrerPolicy(
Referrer::NetReferrerPolicyToBlinkReferrerPolicy(referrer_policy));
}
String securityState(const GURL& url, const net::CertStatus& cert_status) {
if (!url.SchemeIsCryptographic()) {
// Some origins are considered secure even though they're not cryptographic,
// so treat them as secure in the UI.
if (IsOriginSecure(url))
return Security::SecurityStateEnum::Secure;
return Security::SecurityStateEnum::Insecure;
}
if (net::IsCertStatusError(cert_status))
return Security::SecurityStateEnum::Insecure;
return Security::SecurityStateEnum::Secure;
}
DevToolsURLLoaderInterceptor::InterceptionStage ToInterceptorStage(
const protocol::Network::InterceptionStage& interceptor_stage) {
if (interceptor_stage == protocol::Network::InterceptionStageEnum::Request)
return DevToolsURLLoaderInterceptor::REQUEST;
if (interceptor_stage ==
protocol::Network::InterceptionStageEnum::HeadersReceived)
return DevToolsURLLoaderInterceptor::RESPONSE;
NOTREACHED();
return DevToolsURLLoaderInterceptor::REQUEST;
}
double timeDelta(base::TimeTicks time,
base::TimeTicks start,
double invalid_value = -1) {
return time.is_null() ? invalid_value : (time - start).InMillisecondsF();
}
std::unique_ptr<Network::ResourceTiming> GetTiming(
const net::LoadTimingInfo& load_timing) {
if (load_timing.receive_headers_end.is_null())
return nullptr;
const base::TimeTicks kNullTicks;
return Network::ResourceTiming::Create()
.SetRequestTime((load_timing.request_start - kNullTicks).InSecondsF())
.SetProxyStart(
timeDelta(load_timing.proxy_resolve_start, load_timing.request_start))
.SetProxyEnd(
timeDelta(load_timing.proxy_resolve_end, load_timing.request_start))
.SetDnsStart(timeDelta(load_timing.connect_timing.dns_start,
load_timing.request_start))
.SetDnsEnd(timeDelta(load_timing.connect_timing.dns_end,
load_timing.request_start))
.SetConnectStart(timeDelta(load_timing.connect_timing.connect_start,
load_timing.request_start))
.SetConnectEnd(timeDelta(load_timing.connect_timing.connect_end,
load_timing.request_start))
.SetSslStart(timeDelta(load_timing.connect_timing.ssl_start,
load_timing.request_start))
.SetSslEnd(timeDelta(load_timing.connect_timing.ssl_end,
load_timing.request_start))
.SetWorkerStart(-1)
.SetWorkerReady(-1)
.SetWorkerFetchStart(timeDelta(load_timing.service_worker_fetch_start,
load_timing.request_start))
.SetWorkerRespondWithSettled(
timeDelta(load_timing.service_worker_respond_with_settled,
load_timing.request_start))
.SetSendStart(
timeDelta(load_timing.send_start, load_timing.request_start))
.SetSendEnd(timeDelta(load_timing.send_end, load_timing.request_start))
.SetPushStart(
timeDelta(load_timing.push_start, load_timing.request_start, 0))
.SetPushEnd(timeDelta(load_timing.push_end, load_timing.request_start, 0))
.SetReceiveHeadersEnd(
timeDelta(load_timing.receive_headers_end, load_timing.request_start))
.Build();
}
std::unique_ptr<Object> GetRawHeaders(
const std::vector<network::mojom::HttpRawHeaderPairPtr>& headers) {
std::unique_ptr<DictionaryValue> headers_dict(DictionaryValue::create());
for (const auto& header : headers) {
std::string value;
bool merge_with_another = headers_dict->getString(header->key, &value);
headers_dict->setString(header->key, merge_with_another
? value + '\n' + header->value
: header->value);
}
return Object::fromValue(headers_dict.get(), nullptr);
}
String GetProtocol(const GURL& url,
const network::mojom::URLResponseHead& info) {
std::string protocol = info.alpn_negotiated_protocol;
if (protocol.empty() || protocol == "unknown") {
if (info.was_fetched_via_spdy) {
protocol = "h2";
} else if (url.SchemeIsHTTPOrHTTPS()) {
protocol = "http";
if (info.headers) {
if (info.headers->GetHttpVersion() == net::HttpVersion(0, 9))
protocol = "http/0.9";
else if (info.headers->GetHttpVersion() == net::HttpVersion(1, 0))
protocol = "http/1.0";
else if (info.headers->GetHttpVersion() == net::HttpVersion(1, 1))
protocol = "http/1.1";
}
} else {
protocol = url.scheme();
}
}
return protocol;
}
bool GetPostData(const network::ResourceRequestBody& request_body,
std::string* result) {
const std::vector<network::DataElement>* elements = request_body.elements();
if (elements->empty())
return false;
for (const auto& element : *elements) {
// TODO(caseq): Also support blobs.
if (element.type() != network::mojom::DataElementType::kBytes)
return false;
// TODO(caseq): This should rather be sent as Binary.
result->append(element.bytes(), element.length());
}
return true;
}
String SignedExchangeErrorErrorFieldToString(SignedExchangeError::Field field) {
switch (field) {
case SignedExchangeError::Field::kSignatureSig:
return Network::SignedExchangeErrorFieldEnum::SignatureSig;
case SignedExchangeError::Field::kSignatureIintegrity:
return Network::SignedExchangeErrorFieldEnum::SignatureIntegrity;
case SignedExchangeError::Field::kSignatureCertUrl:
return Network::SignedExchangeErrorFieldEnum::SignatureCertUrl;
case SignedExchangeError::Field::kSignatureCertSha256:
return Network::SignedExchangeErrorFieldEnum::SignatureCertSha256;
case SignedExchangeError::Field::kSignatureValidityUrl:
return Network::SignedExchangeErrorFieldEnum::SignatureValidityUrl;
case SignedExchangeError::Field::kSignatureTimestamps:
return Network::SignedExchangeErrorFieldEnum::SignatureTimestamps;
}
NOTREACHED();
return "";
}
std::unique_ptr<Network::SignedExchangeError> BuildSignedExchangeError(
const SignedExchangeError& error) {
std::unique_ptr<Network::SignedExchangeError> signed_exchange_error =
Network::SignedExchangeError::Create().SetMessage(error.message).Build();
if (error.field) {
signed_exchange_error->SetSignatureIndex(error.field->first);
signed_exchange_error->SetErrorField(
SignedExchangeErrorErrorFieldToString(error.field->second));
}
return signed_exchange_error;
}
std::unique_ptr<Array<Network::SignedExchangeError>> BuildSignedExchangeErrors(
const std::vector<SignedExchangeError>& errors) {
auto signed_exchange_errors =
std::make_unique<protocol::Array<Network::SignedExchangeError>>();
for (const auto& error : errors)
signed_exchange_errors->emplace_back(BuildSignedExchangeError(error));
return signed_exchange_errors;
}
std::unique_ptr<Array<Network::SetCookieBlockedReason>>
GetProtocolBlockedSetCookieReason(net::CookieInclusionStatus status) {
std::unique_ptr<Array<Network::SetCookieBlockedReason>> blockedReasons =
std::make_unique<Array<Network::SetCookieBlockedReason>>();
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY)) {
blockedReasons->push_back(Network::SetCookieBlockedReasonEnum::SecureOnly);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT)) {
blockedReasons->push_back(
Network::SetCookieBlockedReasonEnum::SameSiteStrict);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX)) {
blockedReasons->push_back(Network::SetCookieBlockedReasonEnum::SameSiteLax);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::
EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX)) {
blockedReasons->push_back(
Network::SetCookieBlockedReasonEnum::SameSiteUnspecifiedTreatedAsLax);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE)) {
blockedReasons->push_back(
Network::SetCookieBlockedReasonEnum::SameSiteNoneInsecure);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES)) {
blockedReasons->push_back(
Network::SetCookieBlockedReasonEnum::UserPreferences);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE)) {
blockedReasons->push_back(Network::SetCookieBlockedReasonEnum::SyntaxError);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_NONCOOKIEABLE_SCHEME)) {
blockedReasons->push_back(
Network::SetCookieBlockedReasonEnum::SchemeNotSupported);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE)) {
blockedReasons->push_back(
Network::SetCookieBlockedReasonEnum::OverwriteSecure);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN)) {
blockedReasons->push_back(
Network::SetCookieBlockedReasonEnum::InvalidDomain);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX)) {
blockedReasons->push_back(
Network::SetCookieBlockedReasonEnum::InvalidPrefix);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR)) {
blockedReasons->push_back(
Network::SetCookieBlockedReasonEnum::UnknownError);
}
return blockedReasons;
}
std::unique_ptr<Array<Network::CookieBlockedReason>>
GetProtocolBlockedCookieReason(net::CookieInclusionStatus status) {
std::unique_ptr<Array<Network::CookieBlockedReason>> blockedReasons =
std::make_unique<Array<Network::CookieBlockedReason>>();
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY)) {
blockedReasons->push_back(Network::CookieBlockedReasonEnum::SecureOnly);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_NOT_ON_PATH)) {
blockedReasons->push_back(Network::CookieBlockedReasonEnum::NotOnPath);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH)) {
blockedReasons->push_back(Network::CookieBlockedReasonEnum::DomainMismatch);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT)) {
blockedReasons->push_back(Network::CookieBlockedReasonEnum::SameSiteStrict);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_SAMESITE_LAX)) {
blockedReasons->push_back(Network::CookieBlockedReasonEnum::SameSiteLax);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::
EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX)) {
blockedReasons->push_back(
Network::CookieBlockedReasonEnum::SameSiteUnspecifiedTreatedAsLax);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_SAMESITE_NONE_INSECURE)) {
blockedReasons->push_back(
Network::CookieBlockedReasonEnum::SameSiteNoneInsecure);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES)) {
blockedReasons->push_back(
Network::CookieBlockedReasonEnum::UserPreferences);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR)) {
blockedReasons->push_back(Network::CookieBlockedReasonEnum::UnknownError);
}
return blockedReasons;
}
std::unique_ptr<Array<Network::BlockedSetCookieWithReason>>
BuildProtocolBlockedSetCookies(const net::CookieAndLineStatusList& net_list) {
std::unique_ptr<Array<Network::BlockedSetCookieWithReason>> protocol_list =
std::make_unique<Array<Network::BlockedSetCookieWithReason>>();
for (const net::CookieAndLineWithStatus& cookie : net_list) {
std::unique_ptr<Array<Network::SetCookieBlockedReason>> blocked_reasons =
GetProtocolBlockedSetCookieReason(cookie.status);
if (!blocked_reasons->size())
continue;
protocol_list->push_back(
Network::BlockedSetCookieWithReason::Create()
.SetBlockedReasons(std::move(blocked_reasons))
.SetCookieLine(cookie.cookie_string)
.SetCookie(cookie.cookie.has_value()
? BuildCookie(cookie.cookie.value())
: nullptr)
.Build());
}
return protocol_list;
}
std::unique_ptr<Array<Network::BlockedCookieWithReason>>
BuildProtocolAssociatedCookies(const net::CookieAccessResultList& net_list) {
auto protocol_list =
std::make_unique<Array<Network::BlockedCookieWithReason>>();
for (const net::CookieWithAccessResult& cookie : net_list) {
std::unique_ptr<Array<Network::CookieBlockedReason>> blocked_reasons =
GetProtocolBlockedCookieReason(cookie.access_result.status);
// Note that the condition below is not always true,
// as there might be blocked reasons that we do not report.
if (blocked_reasons->size() || cookie.access_result.status.IsInclude()) {
protocol_list->push_back(
Network::BlockedCookieWithReason::Create()
.SetBlockedReasons(std::move(blocked_reasons))
.SetCookie(BuildCookie(cookie.cookie))
.Build());
}
}
return protocol_list;
}
} // namespace
class BackgroundSyncRestorer {
public:
BackgroundSyncRestorer(const std::string& host_id,
StoragePartition* storage_partition)
: host_id_(host_id),
storage_partition_(storage_partition),
offline_sw_registration_id_(
new int64_t(blink::mojom::kInvalidServiceWorkerRegistrationId)) {
SetServiceWorkerOfflineStatus(true);
}
~BackgroundSyncRestorer() { SetServiceWorkerOfflineStatus(false); }
void SetStoragePartition(StoragePartition* storage_partition) {
storage_partition_ = storage_partition;
}
private:
void SetServiceWorkerOfflineStatus(bool offline) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
scoped_refptr<DevToolsAgentHost> host =
DevToolsAgentHost::GetForId(host_id_);
if (!host || !storage_partition_ ||
host->GetType() != DevToolsAgentHost::kTypeServiceWorker) {
return;
}
scoped_refptr<ServiceWorkerDevToolsAgentHost> service_worker_host =
static_cast<ServiceWorkerDevToolsAgentHost*>(host.get());
scoped_refptr<BackgroundSyncContextImpl> sync_context =
static_cast<StoragePartitionImpl*>(storage_partition_)
->GetBackgroundSyncContext();
if (offline) {
RunOrPostTaskOnThread(
FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
base::BindOnce(
&SetServiceWorkerOfflineOnServiceWorkerCoreThread, sync_context,
base::WrapRefCounted(static_cast<ServiceWorkerContextWrapper*>(
storage_partition_->GetServiceWorkerContext())),
service_worker_host->version_id(),
offline_sw_registration_id_.get()));
} else {
RunOrPostTaskOnThread(
FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
base::BindOnce(&SetServiceWorkerOnlineOnServiceWorkerCoreThread,
sync_context, offline_sw_registration_id_.get()));
}
}
static void SetServiceWorkerOfflineOnServiceWorkerCoreThread(
scoped_refptr<BackgroundSyncContextImpl> sync_context,
scoped_refptr<ServiceWorkerContextWrapper> swcontext,
int64_t version_id,
int64_t* offline_sw_registration_id) {
DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
ServiceWorkerVersion* version = swcontext.get()->GetLiveVersion(version_id);
if (!version)
return;
int64_t registration_id = version->registration_id();
*offline_sw_registration_id = registration_id;
if (registration_id == blink::mojom::kInvalidServiceWorkerRegistrationId)
return;
sync_context->background_sync_manager()->EmulateServiceWorkerOffline(
registration_id, true);
}
static void SetServiceWorkerOnlineOnServiceWorkerCoreThread(
scoped_refptr<BackgroundSyncContextImpl> sync_context,
int64_t* offline_sw_registration_id) {
DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
if (*offline_sw_registration_id ==
blink::mojom::kInvalidServiceWorkerRegistrationId) {
return;
}
sync_context->background_sync_manager()->EmulateServiceWorkerOffline(
*offline_sw_registration_id, false);
}
std::string host_id_;
StoragePartition* storage_partition_;
std::unique_ptr<int64_t, content::BrowserThread::DeleteOnIOThread>
offline_sw_registration_id_;
DISALLOW_COPY_AND_ASSIGN(BackgroundSyncRestorer);
};
NetworkHandler::NetworkHandler(
const std::string& host_id,
const base::UnguessableToken& devtools_token,
DevToolsIOContext* io_context,
base::RepeatingClosure update_loader_factories_callback)
: DevToolsDomainHandler(Network::Metainfo::domainName),
host_id_(host_id),
devtools_token_(devtools_token),
io_context_(io_context),
browser_context_(nullptr),
storage_partition_(nullptr),
host_(nullptr),
enabled_(false),
bypass_service_worker_(false),
cache_disabled_(false),
update_loader_factories_callback_(
std::move(update_loader_factories_callback)) {
DCHECK(io_context_);
static bool have_configured_service_worker_context = false;
if (have_configured_service_worker_context)
return;
have_configured_service_worker_context = true;
}
NetworkHandler::~NetworkHandler() = default;
// static
std::unique_ptr<Array<Network::Cookie>> NetworkHandler::BuildCookieArray(
const std::vector<net::CanonicalCookie>& cookie_list) {
auto cookies = std::make_unique<Array<Network::Cookie>>();
for (const net::CanonicalCookie& cookie : cookie_list)
cookies->emplace_back(BuildCookie(cookie));
return cookies;
}
// static
net::Error NetworkHandler::NetErrorFromString(const std::string& error,
bool* ok) {
*ok = true;
if (error == Network::ErrorReasonEnum::Failed)
return net::ERR_FAILED;
if (error == Network::ErrorReasonEnum::Aborted)
return net::ERR_ABORTED;
if (error == Network::ErrorReasonEnum::TimedOut)
return net::ERR_TIMED_OUT;
if (error == Network::ErrorReasonEnum::AccessDenied)
return net::ERR_ACCESS_DENIED;
if (error == Network::ErrorReasonEnum::ConnectionClosed)
return net::ERR_CONNECTION_CLOSED;
if (error == Network::ErrorReasonEnum::ConnectionReset)
return net::ERR_CONNECTION_RESET;
if (error == Network::ErrorReasonEnum::ConnectionRefused)
return net::ERR_CONNECTION_REFUSED;
if (error == Network::ErrorReasonEnum::ConnectionAborted)
return net::ERR_CONNECTION_ABORTED;
if (error == Network::ErrorReasonEnum::ConnectionFailed)
return net::ERR_CONNECTION_FAILED;
if (error == Network::ErrorReasonEnum::NameNotResolved)
return net::ERR_NAME_NOT_RESOLVED;
if (error == Network::ErrorReasonEnum::InternetDisconnected)
return net::ERR_INTERNET_DISCONNECTED;
if (error == Network::ErrorReasonEnum::AddressUnreachable)
return net::ERR_ADDRESS_UNREACHABLE;
if (error == Network::ErrorReasonEnum::BlockedByClient)
return net::ERR_BLOCKED_BY_CLIENT;
if (error == Network::ErrorReasonEnum::BlockedByResponse)
return net::ERR_BLOCKED_BY_RESPONSE;
*ok = false;
return net::ERR_FAILED;
}
// static
String NetworkHandler::NetErrorToString(int net_error) {
switch (net_error) {
case net::ERR_ABORTED:
return Network::ErrorReasonEnum::Aborted;
case net::ERR_TIMED_OUT:
return Network::ErrorReasonEnum::TimedOut;
case net::ERR_ACCESS_DENIED:
return Network::ErrorReasonEnum::AccessDenied;
case net::ERR_CONNECTION_CLOSED:
return Network::ErrorReasonEnum::ConnectionClosed;
case net::ERR_CONNECTION_RESET:
return Network::ErrorReasonEnum::ConnectionReset;
case net::ERR_CONNECTION_REFUSED:
return Network::ErrorReasonEnum::ConnectionRefused;
case net::ERR_CONNECTION_ABORTED:
return Network::ErrorReasonEnum::ConnectionAborted;
case net::ERR_CONNECTION_FAILED:
return Network::ErrorReasonEnum::ConnectionFailed;
case net::ERR_NAME_NOT_RESOLVED:
return Network::ErrorReasonEnum::NameNotResolved;
case net::ERR_INTERNET_DISCONNECTED:
return Network::ErrorReasonEnum::InternetDisconnected;
case net::ERR_ADDRESS_UNREACHABLE:
return Network::ErrorReasonEnum::AddressUnreachable;
case net::ERR_BLOCKED_BY_CLIENT:
return Network::ErrorReasonEnum::BlockedByClient;
case net::ERR_BLOCKED_BY_RESPONSE:
return Network::ErrorReasonEnum::BlockedByResponse;
default:
return Network::ErrorReasonEnum::Failed;
}
}
// static
bool NetworkHandler::AddInterceptedResourceType(
const std::string& resource_type,
base::flat_set<blink::mojom::ResourceType>* intercepted_resource_types) {
if (resource_type == protocol::Network::ResourceTypeEnum::Document) {
intercepted_resource_types->insert(blink::mojom::ResourceType::kMainFrame);
intercepted_resource_types->insert(blink::mojom::ResourceType::kSubFrame);
return true;
}
if (resource_type == protocol::Network::ResourceTypeEnum::Stylesheet) {
intercepted_resource_types->insert(blink::mojom::ResourceType::kStylesheet);
return true;
}
if (resource_type == protocol::Network::ResourceTypeEnum::Image) {
intercepted_resource_types->insert(blink::mojom::ResourceType::kImage);
return true;
}
if (resource_type == protocol::Network::ResourceTypeEnum::Media) {
intercepted_resource_types->insert(blink::mojom::ResourceType::kMedia);
return true;
}
if (resource_type == protocol::Network::ResourceTypeEnum::Font) {
intercepted_resource_types->insert(
blink::mojom::ResourceType::kFontResource);
return true;
}
if (resource_type == protocol::Network::ResourceTypeEnum::Script) {
intercepted_resource_types->insert(blink::mojom::ResourceType::kScript);
return true;
}
if (resource_type == protocol::Network::ResourceTypeEnum::XHR) {
intercepted_resource_types->insert(blink::mojom::ResourceType::kXhr);
return true;
}
if (resource_type == protocol::Network::ResourceTypeEnum::Fetch) {
intercepted_resource_types->insert(blink::mojom::ResourceType::kPrefetch);
return true;
}
if (resource_type ==
protocol::Network::ResourceTypeEnum::CSPViolationReport) {
intercepted_resource_types->insert(blink::mojom::ResourceType::kCspReport);
return true;
}
if (resource_type == protocol::Network::ResourceTypeEnum::Ping) {
intercepted_resource_types->insert(blink::mojom::ResourceType::kPing);
return true;
}
if (resource_type == protocol::Network::ResourceTypeEnum::Other) {
intercepted_resource_types->insert(
blink::mojom::ResourceType::kSubResource);
intercepted_resource_types->insert(blink::mojom::ResourceType::kObject);
intercepted_resource_types->insert(blink::mojom::ResourceType::kWorker);
intercepted_resource_types->insert(
blink::mojom::ResourceType::kSharedWorker);
intercepted_resource_types->insert(blink::mojom::ResourceType::kFavicon);
intercepted_resource_types->insert(
blink::mojom::ResourceType::kServiceWorker);
intercepted_resource_types->insert(
blink::mojom::ResourceType::kPluginResource);
return true;
}
return false;
}
// static
const char* NetworkHandler::ResourceTypeToString(
blink::mojom::ResourceType resource_type) {
switch (resource_type) {
case blink::mojom::ResourceType::kMainFrame:
return protocol::Network::ResourceTypeEnum::Document;
case blink::mojom::ResourceType::kSubFrame:
return protocol::Network::ResourceTypeEnum::Document;
case blink::mojom::ResourceType::kStylesheet:
return protocol::Network::ResourceTypeEnum::Stylesheet;
case blink::mojom::ResourceType::kScript:
return protocol::Network::ResourceTypeEnum::Script;
case blink::mojom::ResourceType::kImage:
return protocol::Network::ResourceTypeEnum::Image;
case blink::mojom::ResourceType::kFontResource:
return protocol::Network::ResourceTypeEnum::Font;
case blink::mojom::ResourceType::kSubResource:
return protocol::Network::ResourceTypeEnum::Other;
case blink::mojom::ResourceType::kObject:
return protocol::Network::ResourceTypeEnum::Other;
case blink::mojom::ResourceType::kMedia:
return protocol::Network::ResourceTypeEnum::Media;
case blink::mojom::ResourceType::kWorker:
return protocol::Network::ResourceTypeEnum::Other;
case blink::mojom::ResourceType::kSharedWorker:
return protocol::Network::ResourceTypeEnum::Other;
case blink::mojom::ResourceType::kPrefetch:
return protocol::Network::ResourceTypeEnum::Fetch;
case blink::mojom::ResourceType::kFavicon:
return protocol::Network::ResourceTypeEnum::Other;
case blink::mojom::ResourceType::kXhr:
return protocol::Network::ResourceTypeEnum::XHR;
case blink::mojom::ResourceType::kPing:
return protocol::Network::ResourceTypeEnum::Ping;
case blink::mojom::ResourceType::kServiceWorker:
return protocol::Network::ResourceTypeEnum::Other;
case blink::mojom::ResourceType::kCspReport:
return protocol::Network::ResourceTypeEnum::CSPViolationReport;
case blink::mojom::ResourceType::kPluginResource:
return protocol::Network::ResourceTypeEnum::Other;
default:
return protocol::Network::ResourceTypeEnum::Other;
}
}
// static
std::vector<NetworkHandler*> NetworkHandler::ForAgentHost(
DevToolsAgentHostImpl* host) {
return host->HandlersByName<NetworkHandler>(Network::Metainfo::domainName);
}
void NetworkHandler::Wire(UberDispatcher* dispatcher) {
frontend_.reset(new Network::Frontend(dispatcher->channel()));
Network::Dispatcher::wire(dispatcher, this);
}
void NetworkHandler::SetRenderer(int render_process_host_id,
RenderFrameHostImpl* frame_host) {
RenderProcessHost* process_host =
RenderProcessHost::FromID(render_process_host_id);
if (process_host) {
storage_partition_ = process_host->GetStoragePartition();
browser_context_ = process_host->GetBrowserContext();
} else {
storage_partition_ = nullptr;
browser_context_ = nullptr;
}
host_ = frame_host;
if (background_sync_restorer_ && storage_partition_)
background_sync_restorer_->SetStoragePartition(storage_partition_);
}
Response NetworkHandler::Enable(Maybe<int> max_total_size,
Maybe<int> max_resource_size,
Maybe<int> max_post_data_size) {
enabled_ = true;
return Response::FallThrough();
}
Response NetworkHandler::Disable() {
enabled_ = false;
url_loader_interceptor_.reset();
SetNetworkConditions(nullptr);
extra_headers_.clear();
return Response::FallThrough();
}
Response NetworkHandler::SetCacheDisabled(bool cache_disabled) {
cache_disabled_ = cache_disabled;
return Response::FallThrough();
}
class DevtoolsClearCacheObserver
: public content::BrowsingDataRemover::Observer {
public:
explicit DevtoolsClearCacheObserver(
content::BrowsingDataRemover* remover,
std::unique_ptr<NetworkHandler::ClearBrowserCacheCallback> callback)
: remover_(remover), callback_(std::move(callback)) {
remover_->AddObserver(this);
}
~DevtoolsClearCacheObserver() override { remover_->RemoveObserver(this); }
void OnBrowsingDataRemoverDone(uint64_t failed_data_types) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
callback_->sendSuccess();
delete this;
}
private:
content::BrowsingDataRemover* remover_;
std::unique_ptr<NetworkHandler::ClearBrowserCacheCallback> callback_;
};
void NetworkHandler::ClearBrowserCache(
std::unique_ptr<ClearBrowserCacheCallback> callback) {
if (!browser_context_) {
callback->sendFailure(Response::InternalError());
return;
}
content::BrowsingDataRemover* remover =
content::BrowserContext::GetBrowsingDataRemover(browser_context_);
remover->RemoveAndReply(
base::Time(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_CACHE,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
new DevtoolsClearCacheObserver(remover, std::move(callback)));
}
void NetworkHandler::ClearBrowserCookies(
std::unique_ptr<ClearBrowserCookiesCallback> callback) {
if (!storage_partition_) {
callback->sendFailure(Response::InternalError());
return;
}
storage_partition_->GetCookieManagerForBrowserProcess()->DeleteCookies(
network::mojom::CookieDeletionFilter::New(),
base::BindOnce([](std::unique_ptr<ClearBrowserCookiesCallback> callback,
uint32_t) { callback->sendSuccess(); },
std::move(callback)));
}
void NetworkHandler::GetCookies(Maybe<Array<String>> protocol_urls,
std::unique_ptr<GetCookiesCallback> callback) {
if (!host_ || !storage_partition_) {
callback->sendFailure(Response::InternalError());
return;
}
std::vector<GURL> urls = ComputeCookieURLs(host_, protocol_urls);
CookieRetrieverNetworkService::Retrieve(
storage_partition_->GetCookieManagerForBrowserProcess(), urls,
std::move(callback));
}
void NetworkHandler::GetAllCookies(
std::unique_ptr<GetAllCookiesCallback> callback) {
if (!storage_partition_) {
callback->sendFailure(Response::InternalError());
return;
}
storage_partition_->GetCookieManagerForBrowserProcess()->GetAllCookies(
base::BindOnce(
[](std::unique_ptr<GetAllCookiesCallback> callback,
const std::vector<net::CanonicalCookie>& cookies) {
callback->sendSuccess(NetworkHandler::BuildCookieArray(cookies));
},
std::move(callback)));
}
void NetworkHandler::SetCookie(const std::string& name,
const std::string& value,
Maybe<std::string> url,
Maybe<std::string> domain,
Maybe<std::string> path,
Maybe<bool> secure,
Maybe<bool> http_only,
Maybe<std::string> same_site,
Maybe<double> expires,
Maybe<std::string> priority,
std::unique_ptr<SetCookieCallback> callback) {
if (!storage_partition_) {
callback->sendFailure(Response::InternalError());
return;
}
if (!url.isJust() && !domain.isJust()) {
callback->sendFailure(Response::InvalidParams(
"At least one of the url and domain needs to be specified"));
}
std::unique_ptr<net::CanonicalCookie> cookie = MakeCookieFromProtocolValues(
name, value, url.fromMaybe(""), domain.fromMaybe(""), path.fromMaybe(""),
secure.fromMaybe(false), http_only.fromMaybe(false),
same_site.fromMaybe(""), expires.fromMaybe(-1), priority.fromMaybe(""));
if (!cookie) {
// TODO(caseq): Current logic is for compatability only.
// Consider returning protocol error here.
callback->sendSuccess(false);
return;
}
net::CookieOptions options;
// Permit it to set a SameSite cookie if it wants to.
options.set_same_site_cookie_context(
net::CookieOptions::SameSiteCookieContext::MakeInclusive());
options.set_include_httponly();
storage_partition_->GetCookieManagerForBrowserProcess()->SetCanonicalCookie(
*cookie, net::cookie_util::SimulatedCookieSource(*cookie, "https"),
options,
net::cookie_util::AdaptCookieAccessResultToBool(base::BindOnce(
&SetCookieCallback::sendSuccess, std::move(callback))));
}
// static
void NetworkHandler::SetCookies(
StoragePartition* storage_partition,
std::unique_ptr<protocol::Array<Network::CookieParam>> cookies,
base::OnceCallback<void(bool)> callback) {
std::vector<std::unique_ptr<net::CanonicalCookie>> net_cookies;
for (const std::unique_ptr<Network::CookieParam>& cookie : *cookies) {
std::unique_ptr<net::CanonicalCookie> net_cookie =
MakeCookieFromProtocolValues(
cookie->GetName(), cookie->GetValue(), cookie->GetUrl(""),
cookie->GetDomain(""), cookie->GetPath(""),
cookie->GetSecure(false), cookie->GetHttpOnly(false),
cookie->GetSameSite(""), cookie->GetExpires(-1),
cookie->GetPriority(""));
if (!net_cookie) {
std::move(callback).Run(false);
return;
}
net_cookies.push_back(std::move(net_cookie));
}
base::RepeatingClosure barrier_closure = base::BarrierClosure(
net_cookies.size(), base::BindOnce(std::move(callback), true));
auto* cookie_manager = storage_partition->GetCookieManagerForBrowserProcess();
net::CookieOptions options;
options.set_include_httponly();
// Permit it to set a SameSite cookie if it wants to.
options.set_same_site_cookie_context(
net::CookieOptions::SameSiteCookieContext::MakeInclusive());
for (const auto& cookie : net_cookies) {
cookie_manager->SetCanonicalCookie(
*cookie, net::cookie_util::SimulatedCookieSource(*cookie, "https"),
options,
base::BindOnce([](base::RepeatingClosure callback,
net::CookieAccessResult) { callback.Run(); },
barrier_closure));
}
}
void NetworkHandler::SetCookies(
std::unique_ptr<protocol::Array<Network::CookieParam>> cookies,
std::unique_ptr<SetCookiesCallback> callback) {
if (!storage_partition_) {
callback->sendFailure(Response::InternalError());
return;
}
NetworkHandler::SetCookies(
storage_partition_, std::move(cookies),
base::BindOnce(
[](std::unique_ptr<SetCookiesCallback> callback, bool success) {
if (success) {
callback->sendSuccess();
} else {
callback->sendFailure(
Response::InvalidParams("Invalid cookie fields"));
}
},
std::move(callback)));
}
void NetworkHandler::DeleteCookies(
const std::string& name,
Maybe<std::string> url_spec,
Maybe<std::string> domain,
Maybe<std::string> path,
std::unique_ptr<DeleteCookiesCallback> callback) {
if (!storage_partition_) {
callback->sendFailure(Response::InternalError());
return;
}
if (!url_spec.isJust() && !domain.isJust()) {
callback->sendFailure(Response::InvalidParams(
"At least one of the url and domain needs to be specified"));
}
std::string normalized_domain = domain.fromMaybe("");
if (normalized_domain.empty()) {
GURL url(url_spec.fromMaybe(""));
if (!url.SchemeIsHTTPOrHTTPS()) {
callback->sendFailure(Response::InvalidParams(
"An http or https url URL must be specified"));
return;
}
normalized_domain = url.host();
}
auto* cookie_manager =
storage_partition_->GetCookieManagerForBrowserProcess();
cookie_manager->GetAllCookies(base::BindOnce(
&DeleteFilteredCookies, base::Unretained(cookie_manager), name,
normalized_domain, path.fromMaybe(""), std::move(callback)));
}
Response NetworkHandler::SetExtraHTTPHeaders(
std::unique_ptr<protocol::Network::Headers> headers) {
std::vector<std::pair<std::string, std::string>> new_headers;
std::unique_ptr<protocol::DictionaryValue> object = headers->toValue();
for (size_t i = 0; i < object->size(); ++i) {
auto entry = object->at(i);
std::string value;
if (!entry.second->asString(&value))
return Response::InvalidParams("Invalid header value, string expected");
if (!net::HttpUtil::IsValidHeaderName(entry.first))
return Response::InvalidParams("Invalid header name");
if (!net::HttpUtil::IsValidHeaderValue(value))
return Response::InvalidParams("Invalid header value");
new_headers.emplace_back(entry.first, value);
}
extra_headers_.swap(new_headers);
return Response::FallThrough();
}
Response NetworkHandler::CanEmulateNetworkConditions(bool* result) {
*result = true;
return Response::Success();
}
Response NetworkHandler::EmulateNetworkConditions(
bool offline,
double latency,
double download_throughput,
double upload_throughput,
Maybe<protocol::Network::ConnectionType>) {
network::mojom::NetworkConditionsPtr network_conditions;
bool throttling_enabled = offline || latency > 0 || download_throughput > 0 ||
upload_throughput > 0;
if (throttling_enabled) {
network_conditions = network::mojom::NetworkConditions::New();
network_conditions->offline = offline;
network_conditions->latency = base::TimeDelta::FromMilliseconds(latency);
network_conditions->download_throughput = download_throughput;
network_conditions->upload_throughput = upload_throughput;
}
SetNetworkConditions(std::move(network_conditions));
return Response::FallThrough();
}
Response NetworkHandler::SetBypassServiceWorker(bool bypass) {
bypass_service_worker_ = bypass;
return Response::FallThrough();
}
namespace {
std::unique_ptr<protocol::Network::SecurityDetails> BuildSecurityDetails(
const net::SSLInfo& ssl_info) {
if (!ssl_info.cert)
return nullptr;
auto signed_certificate_timestamp_list =
std::make_unique<protocol::Array<Network::SignedCertificateTimestamp>>();
for (auto const& sct : ssl_info.signed_certificate_timestamps) {
std::unique_ptr<protocol::Network::SignedCertificateTimestamp>
signed_certificate_timestamp =
Network::SignedCertificateTimestamp::Create()
.SetStatus(net::ct::StatusToString(sct.status))
.SetOrigin(net::ct::OriginToString(sct.sct->origin))
.SetLogDescription(sct.sct->log_description)
.SetLogId(base::HexEncode(sct.sct->log_id.c_str(),
sct.sct->log_id.length()))
.SetTimestamp((sct.sct->timestamp - base::Time::UnixEpoch())
.InMillisecondsF())
.SetHashAlgorithm(net::ct::HashAlgorithmToString(
sct.sct->signature.hash_algorithm))
.SetSignatureAlgorithm(net::ct::SignatureAlgorithmToString(
sct.sct->signature.signature_algorithm))
.SetSignatureData(
base::HexEncode(sct.sct->signature.signature_data.c_str(),
sct.sct->signature.signature_data.length()))
.Build();
signed_certificate_timestamp_list->emplace_back(
std::move(signed_certificate_timestamp));
}
std::vector<std::string> san_dns;
std::vector<std::string> san_ip;
ssl_info.cert->GetSubjectAltName(&san_dns, &san_ip);
auto san_list = std::make_unique<protocol::Array<String>>(std::move(san_dns));
for (const std::string& san : san_ip) {
san_list->emplace_back(
net::IPAddress(reinterpret_cast<const uint8_t*>(san.data()), san.size())
.ToString());
}
const char* protocol = "";
const char* key_exchange = "";
const char* cipher = "";
const char* mac = nullptr;
int ssl_version =
net::SSLConnectionStatusToVersion(ssl_info.connection_status);
if (ssl_info.connection_status) {
net::SSLVersionToString(&protocol, ssl_version);
bool is_aead;
bool is_tls13;
uint16_t cipher_suite =
net::SSLConnectionStatusToCipherSuite(ssl_info.connection_status);
net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead,
&is_tls13, cipher_suite);
if (key_exchange == nullptr) {
DCHECK(is_tls13);
key_exchange = "";
}
}
std::unique_ptr<protocol::Network::SecurityDetails> security_details =
protocol::Network::SecurityDetails::Create()
.SetProtocol(protocol)
.SetKeyExchange(key_exchange)
.SetCipher(cipher)
.SetSubjectName(ssl_info.cert->subject().common_name)
.SetSanList(std::move(san_list))
.SetIssuer(ssl_info.cert->issuer().common_name)
.SetValidFrom(ssl_info.cert->valid_start().ToDoubleT())
.SetValidTo(ssl_info.cert->valid_expiry().ToDoubleT())
.SetCertificateId(0) // Keep this in protocol for compatability.
.SetSignedCertificateTimestampList(
std::move(signed_certificate_timestamp_list))
.SetCertificateTransparencyCompliance(
SerializeCTPolicyCompliance(ssl_info.ct_policy_compliance))
.Build();
if (ssl_info.key_exchange_group != 0) {
const char* key_exchange_group =
SSL_get_curve_name(ssl_info.key_exchange_group);
if (key_exchange_group)
security_details->SetKeyExchangeGroup(key_exchange_group);
}
if (mac)
security_details->SetMac(mac);
return security_details;
}
std::unique_ptr<protocol::Object> BuildResponseHeaders(
scoped_refptr<net::HttpResponseHeaders> headers) {
auto headers_dict = DictionaryValue::create();
if (!headers)
return std::make_unique<protocol::Object>(std::move(headers_dict));
size_t iterator = 0;
std::string name;
std::string value;
while (headers->EnumerateHeaderLines(&iterator, &name, &value)) {
std::string old_value;
bool merge_with_another = headers_dict->getString(name, &old_value);
headers_dict->setString(
name, merge_with_another ? old_value + '\n' + value : value);
}
return std::make_unique<protocol::Object>(std::move(headers_dict));
}
String BuildServiceWorkerResponseSource(
const network::mojom::URLResponseHead& info) {
switch (info.service_worker_response_source) {
case network::mojom::FetchResponseSource::kCacheStorage:
return protocol::Network::ServiceWorkerResponseSourceEnum::CacheStorage;
case network::mojom::FetchResponseSource::kHttpCache:
return protocol::Network::ServiceWorkerResponseSourceEnum::HttpCache;
case network::mojom::FetchResponseSource::kNetwork:
return protocol::Network::ServiceWorkerResponseSourceEnum::Network;
case network::mojom::FetchResponseSource::kUnspecified:
return protocol::Network::ServiceWorkerResponseSourceEnum::FallbackCode;
}
}
std::unique_ptr<Network::Response> BuildResponse(
const GURL& url,
const network::mojom::URLResponseHead& info) {
int status = 0;
std::string status_text;
if (info.headers) {
status = info.headers->response_code();
status_text = info.headers->GetStatusText();
} else if (url.SchemeIs(url::kDataScheme)) {
status = net::HTTP_OK;
status_text = "OK";
}
std::string url_fragment;
auto response =
Network::Response::Create()
.SetUrl(NetworkHandler::ExtractFragment(url, &url_fragment))
.SetStatus(status)
.SetStatusText(status_text)
.SetHeaders(BuildResponseHeaders(info.headers))
.SetMimeType(info.mime_type)
.SetConnectionReused(info.load_timing.socket_reused)
.SetConnectionId(info.load_timing.socket_log_id)
.SetSecurityState(securityState(url, info.cert_status))
.SetEncodedDataLength(info.encoded_data_length)
.SetTiming(GetTiming(info.load_timing))
.SetFromDiskCache(!info.load_timing.request_start_time.is_null() &&
info.response_time <
info.load_timing.request_start_time)
.Build();
response->SetFromServiceWorker(info.was_fetched_via_service_worker);
if (info.was_fetched_via_service_worker) {
response->SetServiceWorkerResponseSource(
BuildServiceWorkerResponseSource(info));
}
response->SetFromPrefetchCache(info.was_in_prefetch_cache);
if (!info.response_time.is_null()) {
response->SetResponseTime(info.response_time.ToJsTimeIgnoringNull());
}
if (!info.cache_storage_cache_name.empty()) {
response->SetCacheStorageCacheName(info.cache_storage_cache_name);
}
auto* raw_info = info.raw_request_response_info.get();
if (raw_info) {
if (raw_info->http_status_code) {
response->SetStatus(raw_info->http_status_code);
response->SetStatusText(raw_info->http_status_text);
}
if (raw_info->request_headers.size()) {
response->SetRequestHeaders(GetRawHeaders(raw_info->request_headers));
}
if (!raw_info->request_headers_text.empty()) {
response->SetRequestHeadersText(raw_info->request_headers_text);
}
if (raw_info->response_headers.size())
response->SetHeaders(GetRawHeaders(raw_info->response_headers));
if (!raw_info->response_headers_text.empty())
response->SetHeadersText(raw_info->response_headers_text);
}
response->SetProtocol(GetProtocol(url, info));
response->SetRemoteIPAddress(
net::HostPortPair::FromIPEndPoint(info.remote_endpoint).HostForURL());
response->SetRemotePort(info.remote_endpoint.port());
if (info.ssl_info.has_value())
response->SetSecurityDetails(BuildSecurityDetails(*info.ssl_info));
return response;
}
String blockedReason(blink::ResourceRequestBlockedReason reason) {
switch (reason) {
case blink::ResourceRequestBlockedReason::kCSP:
return protocol::Network::BlockedReasonEnum::Csp;
case blink::ResourceRequestBlockedReason::kMixedContent:
return protocol::Network::BlockedReasonEnum::MixedContent;
case blink::ResourceRequestBlockedReason::kOrigin:
return protocol::Network::BlockedReasonEnum::Origin;
case blink::ResourceRequestBlockedReason::kInspector:
return protocol::Network::BlockedReasonEnum::Inspector;
case blink::ResourceRequestBlockedReason::kSubresourceFilter:
return protocol::Network::BlockedReasonEnum::SubresourceFilter;
case blink::ResourceRequestBlockedReason::kContentType:
return protocol::Network::BlockedReasonEnum::ContentType;
case blink::ResourceRequestBlockedReason::kOther:
return protocol::Network::BlockedReasonEnum::Other;
case blink::ResourceRequestBlockedReason::kCollapsedByClient:
return protocol::Network::BlockedReasonEnum::CollapsedByClient;
case blink::ResourceRequestBlockedReason::kCoepFrameResourceNeedsCoepHeader:
return protocol::Network::BlockedReasonEnum::
CoepFrameResourceNeedsCoepHeader;
case blink::ResourceRequestBlockedReason::
kCoopSandboxedIFrameCannotNavigateToCoopPage:
return protocol::Network::BlockedReasonEnum::
CoopSandboxedIframeCannotNavigateToCoopPage;
case blink::ResourceRequestBlockedReason::kCorpNotSameOrigin:
return protocol::Network::BlockedReasonEnum::CorpNotSameOrigin;
case blink::ResourceRequestBlockedReason::
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep:
return protocol::Network::BlockedReasonEnum::
CorpNotSameOriginAfterDefaultedToSameOriginByCoep;
case blink::ResourceRequestBlockedReason::kCorpNotSameSite:
return protocol::Network::BlockedReasonEnum::CorpNotSameSite;
}
NOTREACHED();
return protocol::Network::BlockedReasonEnum::Other;
}
Maybe<String> GetBlockedReasonFor(
const network::URLLoaderCompletionStatus& status) {
if (status.blocked_by_response_reason) {
switch (*status.blocked_by_response_reason) {
case network::mojom::BlockedByResponseReason::
kCoepFrameResourceNeedsCoepHeader:
return {protocol::Network::BlockedReasonEnum::
CoepFrameResourceNeedsCoepHeader};
case network::mojom::BlockedByResponseReason::
kCoopSandboxedIFrameCannotNavigateToCoopPage:
return {protocol::Network::BlockedReasonEnum::
CoopSandboxedIframeCannotNavigateToCoopPage};
case network::mojom::BlockedByResponseReason::
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep:
return {protocol::Network::BlockedReasonEnum::
CorpNotSameOriginAfterDefaultedToSameOriginByCoep};
case network::mojom::BlockedByResponseReason::kCorpNotSameOrigin:
return {protocol::Network::BlockedReasonEnum::CorpNotSameOrigin};
case network::mojom::BlockedByResponseReason::kCorpNotSameSite:
return {protocol::Network::BlockedReasonEnum::CorpNotSameSite};
}
NOTREACHED();
}
if (status.error_code != net::ERR_BLOCKED_BY_CLIENT &&
status.error_code != net::ERR_BLOCKED_BY_RESPONSE)
return Maybe<String>();
return blockedReason(static_cast<blink::ResourceRequestBlockedReason>(
status.extended_error_code));
}
} // namespace
void NetworkHandler::NavigationRequestWillBeSent(
const NavigationRequest& nav_request,
base::TimeTicks timestamp) {
if (!enabled_)
return;
net::HttpRequestHeaders headers;
headers.AddHeadersFromString(nav_request.begin_params()->headers);
std::unique_ptr<DictionaryValue> headers_dict(DictionaryValue::create());
for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext();)
headers_dict->setString(it.name(), it.value());
const mojom::CommonNavigationParams& common_params =
nav_request.common_params();
GURL referrer = common_params.referrer->url;
// This is normally added down the stack, so we have to fake it here.
if (!referrer.is_empty())
headers_dict->setString(net::HttpRequestHeaders::kReferer, referrer.spec());
std::unique_ptr<Network::Response> redirect_response;
const mojom::CommitNavigationParams& commit_params =
nav_request.commit_params();
if (!commit_params.redirect_response.empty()) {
redirect_response = BuildResponse(commit_params.redirects.back(),
*commit_params.redirect_response.back());
}
std::string url_fragment;
std::string url_without_fragment =
ExtractFragment(common_params.url, &url_fragment);
auto request =
Network::Request::Create()
.SetUrl(url_without_fragment)
.SetMethod(common_params.method)
.SetHeaders(Object::fromValue(headers_dict.get(), nullptr))
.SetInitialPriority(resourcePriority(net::HIGHEST))
.SetReferrerPolicy(referrerPolicy(common_params.referrer->policy))
.Build();
if (!url_fragment.empty())
request->SetUrlFragment(url_fragment);
std::string post_data;
if (common_params.post_data &&
GetPostData(*common_params.post_data, &post_data)) {
request->SetPostData(post_data);
}
// TODO(caseq): report potentially blockable types
request->SetMixedContentType(Security::MixedContentTypeEnum::None);
std::unique_ptr<Network::Initiator> initiator;
const base::Optional<base::Value>& initiator_optional =
nav_request.begin_params()->devtools_initiator;
if (initiator_optional.has_value()) {
initiator = protocol::ValueTypeConverter<Network::Initiator>::FromValue(
*toProtocolValue(&initiator_optional.value(), 1000));
}
if (!initiator) {
initiator = Network::Initiator::Create()
.SetType(Network::Initiator::TypeEnum::Other)
.Build();
}
std::string id = nav_request.devtools_navigation_token().ToString();
double current_ticks = timestamp.since_origin().InSecondsF();
double current_wall_time = base::Time::Now().ToDoubleT();
std::string frame_token =
nav_request.frame_tree_node()->devtools_frame_token().ToString();
frontend_->RequestWillBeSent(
id, id, url_without_fragment, std::move(request), current_ticks,
current_wall_time, std::move(initiator), std::move(redirect_response),
std::string(Network::ResourceTypeEnum::Document), std::move(frame_token),
common_params.has_user_gesture);
}
void NetworkHandler::RequestSent(const std::string& request_id,
const std::string& loader_id,
const network::ResourceRequest& request,
const char* initiator_type,
const base::Optional<GURL>& initiator_url,
base::TimeTicks timestamp) {
if (!enabled_)
return;
std::unique_ptr<DictionaryValue> headers_dict(DictionaryValue::create());
for (net::HttpRequestHeaders::Iterator it(request.headers); it.GetNext();)
headers_dict->setString(it.name(), it.value());
std::unique_ptr<Network::Initiator> initiator =
Network::Initiator::Create().SetType(initiator_type).Build();
if (initiator_url)
initiator->SetUrl(initiator_url->spec());
std::string url_fragment;
std::string url_without_fragment =
ExtractFragment(request.url, &url_fragment);
auto request_object =
Network::Request::Create()
.SetUrl(url_without_fragment)
.SetMethod(request.method)
.SetHeaders(Object::fromValue(headers_dict.get(), nullptr))
.SetInitialPriority(resourcePriority(request.priority))
.SetReferrerPolicy(referrerPolicy(request.referrer_policy))
.Build();
if (!url_fragment.empty())
request_object->SetUrlFragment(url_fragment);
frontend_->RequestWillBeSent(
request_id, loader_id, url_without_fragment, std::move(request_object),
timestamp.since_origin().InSecondsF(), base::Time::Now().ToDoubleT(),
std::move(initiator), std::unique_ptr<Network::Response>(),
std::string(Network::ResourceTypeEnum::Other),
Maybe<std::string>() /* frame_id */, request.has_user_gesture);
}
void NetworkHandler::ResponseReceived(
const std::string& request_id,
const std::string& loader_id,
const GURL& url,
const char* resource_type,
const network::mojom::URLResponseHead& head,
Maybe<std::string> frame_id) {
if (!enabled_)
return;
std::unique_ptr<Network::Response> response(BuildResponse(url, head));
frontend_->ResponseReceived(
request_id, loader_id,
base::TimeTicks::Now().ToInternalValue() /
static_cast<double>(base::Time::kMicrosecondsPerSecond),
resource_type, std::move(response), std::move(frame_id));
}
void NetworkHandler::LoadingComplete(
const std::string& request_id,
const char* resource_type,
const network::URLLoaderCompletionStatus& status) {
if (!enabled_)
return;
if (status.error_code != net::OK) {
frontend_->LoadingFailed(
request_id,
base::TimeTicks::Now().ToInternalValue() /
static_cast<double>(base::Time::kMicrosecondsPerSecond),
resource_type, net::ErrorToString(status.error_code),
status.error_code == net::Error::ERR_ABORTED,
GetBlockedReasonFor(status));
return;
}
frontend_->LoadingFinished(
request_id,
status.completion_time.ToInternalValue() /
static_cast<double>(base::Time::kMicrosecondsPerSecond),
status.encoded_data_length);
}
void NetworkHandler::OnSignedExchangeReceived(
base::Optional<const base::UnguessableToken> devtools_navigation_token,
const GURL& outer_request_url,
const network::mojom::URLResponseHead& outer_response,
const base::Optional<SignedExchangeEnvelope>& envelope,
const scoped_refptr<net::X509Certificate>& certificate,
const base::Optional<net::SSLInfo>& ssl_info,
const std::vector<SignedExchangeError>& errors) {
if (!enabled_)
return;
std::unique_ptr<Network::SignedExchangeInfo> signed_exchange_info =
Network::SignedExchangeInfo::Create()
.SetOuterResponse(BuildResponse(outer_request_url, outer_response))
.Build();
if (envelope) {
std::unique_ptr<DictionaryValue> headers_dict(DictionaryValue::create());
for (const auto& it : envelope->response_headers())
headers_dict->setString(it.first, it.second);
const SignedExchangeSignatureHeaderField::Signature& sig =
envelope->signature();
auto signatures =
std::make_unique<protocol::Array<Network::SignedExchangeSignature>>();
std::unique_ptr<Network::SignedExchangeSignature> signature =
Network::SignedExchangeSignature::Create()
.SetLabel(sig.label)
.SetSignature(base::HexEncode(sig.sig.data(), sig.sig.size()))
.SetIntegrity(sig.integrity)
.SetCertUrl(sig.cert_url.spec())
.SetValidityUrl(sig.validity_url.url.spec())
.SetDate(sig.date)
.SetExpires(sig.expires)
.Build();
if (sig.cert_sha256) {
signature->SetCertSha256(base::HexEncode(sig.cert_sha256->data,
sizeof(sig.cert_sha256->data)));
}
if (certificate) {
auto encoded_certificates = std::make_unique<protocol::Array<String>>();
encoded_certificates->emplace_back();
base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(certificate->cert_buffer()),
&encoded_certificates->back());
for (const auto& cert : certificate->intermediate_buffers()) {
encoded_certificates->emplace_back();
base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(cert.get()),
&encoded_certificates->back());
}
signature->SetCertificates(std::move(encoded_certificates));
}
signatures->emplace_back(std::move(signature));
signed_exchange_info->SetHeader(
Network::SignedExchangeHeader::Create()
.SetRequestUrl(envelope->request_url().url.spec())
.SetResponseCode(envelope->response_code())
.SetResponseHeaders(Object::fromValue(headers_dict.get(), nullptr))
.SetSignatures(std::move(signatures))
.SetHeaderIntegrity(
signed_exchange_utils::CreateHeaderIntegrityHashString(
envelope->ComputeHeaderIntegrity()))
.Build());
}
if (ssl_info)
signed_exchange_info->SetSecurityDetails(BuildSecurityDetails(*ssl_info));
if (errors.size())
signed_exchange_info->SetErrors(BuildSignedExchangeErrors(errors));
frontend_->SignedExchangeReceived(
devtools_navigation_token ? devtools_navigation_token->ToString() : "",
std::move(signed_exchange_info));
}
DispatchResponse NetworkHandler::SetRequestInterception(
std::unique_ptr<protocol::Array<protocol::Network::RequestPattern>>
patterns) {
if (patterns->empty()) {
if (url_loader_interceptor_) {
url_loader_interceptor_.reset();
update_loader_factories_callback_.Run();
}
return Response::Success();
}
std::vector<DevToolsURLLoaderInterceptor::Pattern> interceptor_patterns;
for (const std::unique_ptr<protocol::Network::RequestPattern>& pattern :
*patterns) {
base::flat_set<blink::mojom::ResourceType> resource_types;
std::string resource_type = pattern->GetResourceType("");
if (!resource_type.empty()) {
if (!AddInterceptedResourceType(resource_type, &resource_types)) {
return Response::InvalidParams(base::StringPrintf(
"Cannot intercept resources of type '%s'", resource_type.c_str()));
}
}
interceptor_patterns.emplace_back(
pattern->GetUrlPattern("*"), std::move(resource_types),
ToInterceptorStage(pattern->GetInterceptionStage(
protocol::Network::InterceptionStageEnum::Request)));
}
if (!host_)
return Response::InternalError();
if (!url_loader_interceptor_) {
url_loader_interceptor_ =
std::make_unique<DevToolsURLLoaderInterceptor>(base::BindRepeating(
&NetworkHandler::RequestIntercepted, weak_factory_.GetWeakPtr()));
url_loader_interceptor_->SetPatterns(interceptor_patterns, true);
update_loader_factories_callback_.Run();
} else {
url_loader_interceptor_->SetPatterns(interceptor_patterns, true);
}
return Response::Success();
}
void NetworkHandler::ContinueInterceptedRequest(
const std::string& interception_id,
Maybe<std::string> error_reason,
Maybe<protocol::Binary> raw_response,
Maybe<std::string> url,
Maybe<std::string> method,
Maybe<std::string> post_data,
Maybe<protocol::Network::Headers> opt_headers,
Maybe<protocol::Network::AuthChallengeResponse> auth_challenge_response,
std::unique_ptr<ContinueInterceptedRequestCallback> callback) {
scoped_refptr<net::HttpResponseHeaders> response_headers;
scoped_refptr<base::RefCountedMemory> response_body;
size_t body_offset = 0;
if (raw_response.isJust()) {
const protocol::Binary& raw = raw_response.fromJust();
std::string raw_headers;
size_t header_size = net::HttpUtil::LocateEndOfHeaders(
reinterpret_cast<const char*>(raw.data()), raw.size());
if (header_size == std::string::npos) {
LOG(WARNING) << "Can't find headers in raw response";
header_size = 0;
} else {
raw_headers = net::HttpUtil::AssembleRawHeaders(base::StringPiece(
reinterpret_cast<const char*>(raw.data()), header_size));
}
CHECK_LE(header_size, raw.size());
response_headers =
base::MakeRefCounted<net::HttpResponseHeaders>(std::move(raw_headers));
response_body = raw.bytes();
body_offset = header_size;
}
base::Optional<net::Error> error;
if (error_reason.isJust()) {
bool ok;
error = NetErrorFromString(error_reason.fromJust(), &ok);
if (!ok) {
callback->sendFailure(Response::InvalidParams("Invalid errorReason."));
return;
}
}
std::unique_ptr<DevToolsURLLoaderInterceptor::Modifications::HeadersVector>
override_headers;
if (opt_headers.isJust()) {
std::unique_ptr<protocol::DictionaryValue> headers =
opt_headers.fromJust()->toValue();
override_headers = std::make_unique<
DevToolsURLLoaderInterceptor::Modifications::HeadersVector>();
for (size_t i = 0; i < headers->size(); ++i) {
const protocol::DictionaryValue::Entry& entry = headers->at(i);
std::string value;
if (!entry.second->asString(&value)) {
callback->sendFailure(Response::InvalidParams("Invalid header value"));
return;
}
override_headers->emplace_back(entry.first, value);
}
}
using AuthChallengeResponse =
DevToolsURLLoaderInterceptor::AuthChallengeResponse;
std::unique_ptr<AuthChallengeResponse> override_auth;
if (auth_challenge_response.isJust()) {
std::string type = auth_challenge_response.fromJust()->GetResponse();
if (type == Network::AuthChallengeResponse::ResponseEnum::Default) {
override_auth = std::make_unique<AuthChallengeResponse>(
AuthChallengeResponse::kDefault);
} else if (type ==
Network::AuthChallengeResponse::ResponseEnum::CancelAuth) {
override_auth = std::make_unique<AuthChallengeResponse>(
AuthChallengeResponse::kCancelAuth);
} else if (type == Network::AuthChallengeResponse::ResponseEnum::
ProvideCredentials) {
override_auth = std::make_unique<AuthChallengeResponse>(
base::UTF8ToUTF16(
auth_challenge_response.fromJust()->GetUsername("")),
base::UTF8ToUTF16(
auth_challenge_response.fromJust()->GetPassword("")));
} else {
callback->sendFailure(
Response::InvalidParams("Unrecognized authChallengeResponse."));
return;
}
}
auto modifications =
std::make_unique<DevToolsURLLoaderInterceptor::Modifications>(
std::move(error), std::move(response_headers),
std::move(response_body), body_offset, std::move(url),
std::move(method), std::move(post_data), std::move(override_headers),
std::move(override_auth));
if (!url_loader_interceptor_)
return;
url_loader_interceptor_->ContinueInterceptedRequest(
interception_id, std::move(modifications), std::move(callback));
}
void NetworkHandler::GetResponseBodyForInterception(
const String& interception_id,
std::unique_ptr<GetResponseBodyForInterceptionCallback> callback) {
if (!url_loader_interceptor_)
return;
url_loader_interceptor_->GetResponseBody(interception_id,
std::move(callback));
}
void NetworkHandler::TakeResponseBodyForInterceptionAsStream(
const String& interception_id,
std::unique_ptr<TakeResponseBodyForInterceptionAsStreamCallback> callback) {
if (url_loader_interceptor_) {
url_loader_interceptor_->TakeResponseBodyPipe(
interception_id,
base::BindOnce(&NetworkHandler::OnResponseBodyPipeTaken,
weak_factory_.GetWeakPtr(), std::move(callback)));
return;
}
callback->sendFailure(Response::ServerError(
"Network.takeResponseBodyForInterceptionAsStream is only "
"currently supported with --enable-features=NetworkService"));
}
void NetworkHandler::OnResponseBodyPipeTaken(
std::unique_ptr<TakeResponseBodyForInterceptionAsStreamCallback> callback,
Response response,
mojo::ScopedDataPipeConsumerHandle pipe,
const std::string& mime_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_EQ(response.IsSuccess(), pipe.is_valid());
if (!response.IsSuccess()) {
callback->sendFailure(std::move(response));
return;
}
// The pipe stream is owned only by io_context after we return.
bool is_binary = !DevToolsIOContext::IsTextMimeType(mime_type);
auto stream =
DevToolsStreamPipe::Create(io_context_, std::move(pipe), is_binary);
callback->sendSuccess(stream->handle());
}
// static
std::string NetworkHandler::ExtractFragment(const GURL& url,
std::string* fragment) {
if (!url.has_ref()) {
*fragment = std::string();
return url.spec();
}
*fragment = "#" + url.ref();
GURL::Replacements replacements;
replacements.ClearRef();
return url.ReplaceComponents(replacements).spec();
}
// static
std::unique_ptr<Network::Request>
NetworkHandler::CreateRequestFromResourceRequest(
const network::ResourceRequest& request,
const std::string& cookie_line) {
std::unique_ptr<DictionaryValue> headers_dict(DictionaryValue::create());
for (net::HttpRequestHeaders::Iterator it(request.headers); it.GetNext();)
headers_dict->setString(it.name(), it.value());
if (request.referrer.is_valid()) {
headers_dict->setString(net::HttpRequestHeaders::kReferer,
request.referrer.spec());
}
if (!cookie_line.empty())
headers_dict->setString(net::HttpRequestHeaders::kCookie, cookie_line);
std::string url_fragment;
std::unique_ptr<protocol::Network::Request> request_object =
Network::Request::Create()
.SetUrl(ExtractFragment(request.url, &url_fragment))
.SetMethod(request.method)
.SetHeaders(Object::fromValue(headers_dict.get(), nullptr))
.SetInitialPriority(resourcePriority(request.priority))
.SetReferrerPolicy(referrerPolicy(request.referrer_policy))
.Build();
if (!url_fragment.empty())
request_object->SetUrlFragment(url_fragment);
std::string post_data;
if (request.request_body && GetPostData(*request.request_body, &post_data))
request_object->SetPostData(std::move(post_data));
return request_object;
}
bool NetworkHandler::MaybeCreateProxyForInterception(
RenderProcessHost* rph,
const base::UnguessableToken& frame_token,
bool is_navigation,
bool is_download,
network::mojom::URLLoaderFactoryOverride* intercepting_factory) {
return url_loader_interceptor_ &&
url_loader_interceptor_->CreateProxyForInterception(
rph, frame_token, is_navigation, is_download,
intercepting_factory);
}
void NetworkHandler::ApplyOverrides(net::HttpRequestHeaders* headers,
bool* skip_service_worker,
bool* disable_cache) {
for (auto& entry : extra_headers_)
headers->SetHeader(entry.first, entry.second);
*skip_service_worker |= bypass_service_worker_;
*disable_cache |= cache_disabled_;
}
void NetworkHandler::RequestIntercepted(
std::unique_ptr<InterceptedRequestInfo> info) {
protocol::Maybe<protocol::Network::ErrorReason> error_reason;
if (info->response_error_code < 0)
error_reason = NetErrorToString(info->response_error_code);
Maybe<int> status_code;
Maybe<protocol::Network::Headers> response_headers;
if (info->response_headers) {
status_code = info->response_headers->response_code();
response_headers = BuildResponseHeaders(info->response_headers);
}
std::unique_ptr<protocol::Network::AuthChallenge> auth_challenge;
if (info->auth_challenge) {
auth_challenge =
protocol::Network::AuthChallenge::Create()
.SetSource(info->auth_challenge->is_proxy
? Network::AuthChallenge::SourceEnum::Proxy
: Network::AuthChallenge::SourceEnum::Server)
.SetOrigin(info->auth_challenge->challenger.Serialize())
.SetScheme(info->auth_challenge->scheme)
.SetRealm(info->auth_challenge->realm)
.Build();
}
frontend_->RequestIntercepted(
info->interception_id, std::move(info->network_request),
info->frame_id.ToString(), ResourceTypeToString(info->resource_type),
info->is_navigation, std::move(info->is_download),
std::move(info->redirect_url), std::move(auth_challenge),
std::move(error_reason), std::move(status_code),
std::move(response_headers), std::move(info->renderer_request_id));
}
void NetworkHandler::SetNetworkConditions(
network::mojom::NetworkConditionsPtr conditions) {
if (!storage_partition_)
return;
network::mojom::NetworkContext* context =
storage_partition_->GetNetworkContext();
bool offline = conditions ? conditions->offline : false;
if (!devtools_token_.is_empty())
context->SetNetworkConditions(devtools_token_, std::move(conditions));
if (offline == !!background_sync_restorer_)
return;
background_sync_restorer_.reset(
offline ? new BackgroundSyncRestorer(host_id_, storage_partition_)
: nullptr);
}
void NetworkHandler::OnRequestWillBeSentExtraInfo(
const std::string& devtools_request_id,
const net::CookieAccessResultList& request_cookie_list,
const std::vector<network::mojom::HttpRawHeaderPairPtr>& request_headers) {
if (!enabled_)
return;
frontend_->RequestWillBeSentExtraInfo(
devtools_request_id, BuildProtocolAssociatedCookies(request_cookie_list),
GetRawHeaders(request_headers));
}
void NetworkHandler::OnResponseReceivedExtraInfo(
const std::string& devtools_request_id,
const net::CookieAndLineStatusList& response_cookie_list,
const std::vector<network::mojom::HttpRawHeaderPairPtr>& response_headers,
const base::Optional<std::string>& response_headers_text) {
if (!enabled_)
return;
frontend_->ResponseReceivedExtraInfo(
devtools_request_id, BuildProtocolBlockedSetCookies(response_cookie_list),
GetRawHeaders(response_headers),
response_headers_text.has_value() ? response_headers_text.value()
: Maybe<String>());
}
} // namespace protocol
} // namespace content