Skip to content

Commit

Permalink
Ensure we pass the scheme and port for dns prefetches.
Browse files Browse the repository at this point in the history
Currently when a dns-prefetch was encountered either in an HTTP header, an anchor tag or on hover the browser would do a DNS lookup based on a hard coded scheme of "http". Since net::HostCache uses a key that changes based on whether scheme was used or not, along with the port, this meant that additional DNS requests were happening. When the system resolver is used the overhead is probably low since the system would cache it, but it would still slow things down because the synchronous path wasn't taken. When DoH is used this would be more expensive. And in both cases the cache fills up faster which could then take out records that are more likely to be used than the ones with incorrect scheme/port.

(cherry picked from commit 069bdb0)

Bug: 1357161
Change-Id: I8e25bf141ffc0e344a08d865b29ac9a288427edd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4151321
Reviewed-by: Nate Chapin <japhet@chromium.org>
Reviewed-by: Ryan Sturm <ryansturm@chromium.org>
Commit-Queue: John Abd-El-Malek <jam@chromium.org>
Reviewed-by: Nasko Oskov <nasko@chromium.org>
Reviewed-by: Adam Rice <ricea@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1091446}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4182143
Commit-Queue: Nate Chapin <japhet@chromium.org>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Auto-Submit: John Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/branch-heads/5481@{#503}
Cr-Branched-From: 130f3e4-refs/heads/main@{#1084008}
  • Loading branch information
John Abd-El-Malek authored and Chromium LUCI CQ committed Jan 20, 2023
1 parent e24b3e6 commit fe642ce
Show file tree
Hide file tree
Showing 22 changed files with 78 additions and 47 deletions.
Expand Up @@ -344,6 +344,7 @@ private ProductionSupportedFlagList() {}
Flag.baseFeature(BlinkFeatures.THREADED_BODY_LOADER,
"If enabled, reads and decodes navigation body data off the main thread."),
Flag.baseFeature("PreconnectOnRedirect"),
Flag.baseFeature("PreconnectInNetworkService"), Flag.baseFeature("PrefetchDNSWithURL"),
Flag.baseFeature(BlinkFeatures.SEND_MOUSE_EVENTS_DISABLED_FORM_CONTROLS,
"This changes event propagation for disabled form controls."),
Flag.baseFeature(ContentFeatures.SURFACE_SYNC_FULLSCREEN_KILLSWITCH,
Expand Down
2 changes: 1 addition & 1 deletion chrome/browser/predictors/loading_predictor_unittest.cc
Expand Up @@ -54,7 +54,7 @@ class MockPreconnectManager : public PreconnectManager {
const net::NetworkAnonymizationKey& network_anonymization_key));
MOCK_METHOD2(
StartPreresolveHosts,
void(const std::vector<std::string>& hostnames,
void(const std::vector<GURL>& urls,
const net::NetworkAnonymizationKey& network_anonymization_key));
MOCK_METHOD3(StartPreconnectUrl,
void(const GURL& url,
Expand Down
5 changes: 2 additions & 3 deletions chrome/browser/predictors/network_hints_handler_impl.cc
Expand Up @@ -43,8 +43,7 @@ void NetworkHintsHandlerImpl::Create(
std::move(receiver));
}

void NetworkHintsHandlerImpl::PrefetchDNS(
const std::vector<std::string>& names) {
void NetworkHintsHandlerImpl::PrefetchDNS(const std::vector<GURL>& urls) {
if (!preconnect_manager_)
return;

Expand All @@ -54,7 +53,7 @@ void NetworkHintsHandlerImpl::PrefetchDNS(
return;

preconnect_manager_->StartPreresolveHosts(
names, GetPendingNetworkAnonymizationKey(render_frame_host));
urls, GetPendingNetworkAnonymizationKey(render_frame_host));
}

void NetworkHintsHandlerImpl::Preconnect(const GURL& url,
Expand Down
2 changes: 1 addition & 1 deletion chrome/browser/predictors/network_hints_handler_impl.h
Expand Up @@ -29,7 +29,7 @@ class NetworkHintsHandlerImpl
receiver);

// network_hints::mojom::NetworkHintsHandler methods:
void PrefetchDNS(const std::vector<std::string>& names) override;
void PrefetchDNS(const std::vector<GURL>& urls) override;
void Preconnect(const GURL& url, bool allow_credentials) override;

private:
Expand Down
6 changes: 3 additions & 3 deletions chrome/browser/predictors/preconnect_manager.cc
Expand Up @@ -132,15 +132,15 @@ void PreconnectManager::StartPreresolveHost(
}

void PreconnectManager::StartPreresolveHosts(
const std::vector<std::string>& hostnames,
const std::vector<GURL>& urls,
const net::NetworkAnonymizationKey& network_anonymization_key) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!IsEnabled())
return;
// Push jobs in front of the queue due to higher priority.
for (const std::string& hostname : base::Reversed(hostnames)) {
for (const GURL& url : base::Reversed(urls)) {
PreresolveJobId job_id = preresolve_jobs_.Add(
std::make_unique<PreresolveJob>(GURL("http://" + hostname), 0,
std::make_unique<PreresolveJob>(url.DeprecatedGetOriginAsURL(), 0,
kAllowCredentialsOnPreconnectByDefault,
network_anonymization_key, nullptr));
queued_jobs_.push_front(job_id);
Expand Down
2 changes: 1 addition & 1 deletion chrome/browser/predictors/preconnect_manager.h
Expand Up @@ -182,7 +182,7 @@ class PreconnectManager {
const GURL& url,
const net::NetworkAnonymizationKey& network_anonymization_key);
virtual void StartPreresolveHosts(
const std::vector<std::string>& hostnames,
const std::vector<GURL>& urls,
const net::NetworkAnonymizationKey& network_anonymization_key);
virtual void StartPreconnectUrl(
const GURL& url,
Expand Down
4 changes: 2 additions & 2 deletions chrome/browser/predictors/preconnect_manager_unittest.cc
Expand Up @@ -861,7 +861,7 @@ TEST_F(PreconnectManagerTest, TestStartPreresolveHosts) {

EXPECT_CALL(*mock_network_context_, ResolveHostProxy(cdn.host()));
EXPECT_CALL(*mock_network_context_, ResolveHostProxy(fonts.host()));
preconnect_manager_->StartPreresolveHosts({cdn.host(), fonts.host()},
preconnect_manager_->StartPreresolveHosts({cdn, fonts},
network_anonymization_key);
mock_network_context_->CompleteHostLookup(cdn.host(),
network_anonymization_key, net::OK);
Expand All @@ -879,7 +879,7 @@ TEST_F(PreconnectManagerTest, TestStartPreresolveHostsDisabledViaUI) {

// mock_network_context_.ResolveHostProxy shouldn't be called. The StrictMock
// will raise an error if it happens.
preconnect_manager_->StartPreresolveHosts({cdn.host(), fonts.host()},
preconnect_manager_->StartPreresolveHosts({cdn, fonts},
network_anonymization_key);
}

Expand Down
Expand Up @@ -41,19 +41,15 @@ net::NetworkAnonymizationKey GetPendingNetworkAnonymizationKey(
.network_anonymization_key();
}

const int kDefaultPort = 80;

// This class contains a std::unique_ptr of itself, it is passed in through
// Start() method, and will be freed by the OnComplete() method when resolving
// has completed or mojo connection error has happened.
class DnsLookupRequest : public network::ResolveHostClientBase {
public:
DnsLookupRequest(int render_process_id,
int render_frame_id,
const std::string& hostname)
DnsLookupRequest(int render_process_id, int render_frame_id, const GURL& url)
: render_process_id_(render_process_id),
render_frame_id_(render_frame_id),
hostname_(hostname) {}
url_(url) {}

DnsLookupRequest(const DnsLookupRequest&) = delete;
DnsLookupRequest& operator=(const DnsLookupRequest&) = delete;
Expand All @@ -76,7 +72,7 @@ class DnsLookupRequest : public network::ResolveHostClientBase {
}

DCHECK(!receiver_.is_bound());
net::HostPortPair host_port_pair(hostname_, kDefaultPort);
url::SchemeHostPort scheme_host_port(url_);
network::mojom::ResolveHostParametersPtr resolve_host_parameters =
network::mojom::ResolveHostParameters::New();
// Lets the host resolver know it can be de-prioritized.
Expand All @@ -91,8 +87,8 @@ class DnsLookupRequest : public network::ResolveHostClientBase {
render_frame_host->GetProcess()
->GetStoragePartition()
->GetNetworkContext()
->ResolveHost(network::mojom::HostResolverHost::NewHostPortPair(
std::move(host_port_pair)),
->ResolveHost(network::mojom::HostResolverHost::NewSchemeHostPort(
std::move(scheme_host_port)),
GetPendingNetworkAnonymizationKey(render_frame_host),
std::move(resolve_host_parameters),
receiver_.BindNewPipeAndPassRemote());
Expand All @@ -110,15 +106,15 @@ class DnsLookupRequest : public network::ResolveHostClientBase {
const absl::optional<net::AddressList>& resolved_addresses,
const absl::optional<net::HostResolverEndpointResults>&
endpoint_results_with_metadata) override {
VLOG(2) << __FUNCTION__ << ": " << hostname_
VLOG(2) << __FUNCTION__ << ": " << url_.spec()
<< ", result=" << resolve_error_info.error;
request_.reset();
}

mojo::Receiver<network::mojom::ResolveHostClient> receiver_{this};
const int render_process_id_;
const int render_frame_id_;
const std::string hostname_;
const GURL url_;
std::unique_ptr<DnsLookupRequest> request_;
};

Expand All @@ -144,12 +140,11 @@ void SimpleNetworkHintsHandlerImpl::Create(
std::move(receiver));
}

void SimpleNetworkHintsHandlerImpl::PrefetchDNS(
const std::vector<std::string>& names) {
for (const std::string& hostname : names) {
void SimpleNetworkHintsHandlerImpl::PrefetchDNS(const std::vector<GURL>& urls) {
for (const GURL& url : urls) {
std::unique_ptr<DnsLookupRequest> request =
std::make_unique<DnsLookupRequest>(render_process_id_, render_frame_id_,
hostname);
url);
DnsLookupRequest* request_ptr = request.get();
request_ptr->Start(std::move(request));
}
Expand Down
Expand Up @@ -30,7 +30,7 @@ class SimpleNetworkHintsHandlerImpl : public mojom::NetworkHintsHandler {
mojo::PendingReceiver<mojom::NetworkHintsHandler> receiver);

// mojom::NetworkHintsHandler methods:
void PrefetchDNS(const std::vector<std::string>& names) override;
void PrefetchDNS(const std::vector<GURL>& urls) override;
void Preconnect(const GURL& url, bool allow_credentials) override;

private:
Expand Down
6 changes: 3 additions & 3 deletions components/network_hints/common/network_hints.mojom
Expand Up @@ -8,9 +8,9 @@ import "url/mojom/url.mojom";

// This interface is used by the renderer to provide hints to the browser.
interface NetworkHintsHandler {
// This method is called periodically with a hint to prefetch a batch set of
// hostnames.
PrefetchDNS(array<string> hostname_list);
// This method is called periodically with a hint to perform DNS lookups for
// a batch set of urls.
PrefetchDNS(array<url.mojom.Url> url_list);

// This method is called periodically with a hint to preconnect to the origin
// of the specified url.
Expand Down
1 change: 1 addition & 0 deletions components/network_hints/renderer/DEPS
Expand Up @@ -3,4 +3,5 @@ include_rules = [
"+content/public/renderer",
"+mojo",
"+third_party/blink/public",
"+services/network/public/cpp",
]
28 changes: 22 additions & 6 deletions components/network_hints/renderer/web_prescient_networking_impl.cc
Expand Up @@ -6,14 +6,19 @@

#include "base/logging.h"
#include "content/public/renderer/render_frame.h"
#include "services/network/public/cpp/features.h"
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"

namespace network_hints {
namespace {

void ForwardToHandler(mojo::Remote<mojom::NetworkHintsHandler>* handler,
const std::vector<std::string>& names) {
handler->get()->PrefetchDNS(names);
std::vector<GURL> urls;
for (const auto& name : names) {
urls.emplace_back("http://" + name);
}
handler->get()->PrefetchDNS(urls);
}

} // namespace
Expand All @@ -28,13 +33,24 @@ WebPrescientNetworkingImpl::WebPrescientNetworkingImpl(

WebPrescientNetworkingImpl::~WebPrescientNetworkingImpl() {}

void WebPrescientNetworkingImpl::PrefetchDNS(const blink::WebString& hostname) {
DVLOG(2) << "Prefetch DNS: " << hostname.Utf8();
if (hostname.IsEmpty())
void WebPrescientNetworkingImpl::PrefetchDNS(const blink::WebURL& url) {
DVLOG(2) << "Prefetch DNS: " << url.GetString().Utf8();
GURL gurl(url);
if (!gurl.is_valid() || !gurl.has_host()) {
return;
}

std::string hostname_utf8 = hostname.Utf8();
dns_prefetch_.Resolve(hostname_utf8.data(), hostname_utf8.length());
if (base::FeatureList::IsEnabled(network::features::kPrefetchDNSWithURL)) {
std::vector<GURL> urls;
urls.push_back(std::move(gurl));
handler_->PrefetchDNS(urls);
// TODO(jam): If this launches remove DnsQueue and RendererDnsPrefetch
// which are no longer needed. They were from a feature which existed
// at launch but not anymore that prefetched DNS for every link on a page.
} else {
auto host_piece = gurl.host_piece();
dns_prefetch_.Resolve(host_piece.data(), host_piece.length());
}
}

void WebPrescientNetworkingImpl::Preconnect(
Expand Down
Expand Up @@ -29,7 +29,7 @@ class WebPrescientNetworkingImpl : public blink::WebPrescientNetworking {
~WebPrescientNetworkingImpl() override;

// blink::WebPrescientNetworking methods:
void PrefetchDNS(const blink::WebString& hostname) override;
void PrefetchDNS(const blink::WebURL& url) override;
void Preconnect(const blink::WebURL& url, bool allow_credentials) override;

private:
Expand Down
10 changes: 10 additions & 0 deletions services/network/public/cpp/features.cc
Expand Up @@ -307,6 +307,16 @@ BASE_FEATURE(kPreconnectInNetworkService,
"PreconnectInNetworkService",
base::FEATURE_DISABLED_BY_DEFAULT);

// When prefetching a DNS record ensures that the scheme and port are taken
// into account so that the cache (which is keyed by scheme and port) works
// for subsequent queries.
BASE_FEATURE(kPrefetchDNSWithURL,
"PrefetchDNSWithURL",
base::FEATURE_DISABLED_BY_DEFAULT);

constexpr base::FeatureParam<bool> kPrefetchDNSWithURLAllAnchorElements{
&kPrefetchDNSWithURL, "prefetch_dns_all_anchor_elements", true};

// Preconnect to a new origin right when a redirect starts.
BASE_FEATURE(kPreconnectOnRedirect,
"PreconnectOnRedirect",
Expand Down
5 changes: 5 additions & 0 deletions services/network/public/cpp/features.h
Expand Up @@ -97,6 +97,11 @@ BASE_DECLARE_FEATURE(kPrivateNetworkAccessPreflightShortTimeout);

COMPONENT_EXPORT(NETWORK_CPP) BASE_DECLARE_FEATURE(kPreconnectInNetworkService);

COMPONENT_EXPORT(NETWORK_CPP) BASE_DECLARE_FEATURE(kPrefetchDNSWithURL);

COMPONENT_EXPORT(NETWORK_CPP)
extern const base::FeatureParam<bool> kPrefetchDNSWithURLAllAnchorElements;

COMPONENT_EXPORT(NETWORK_CPP) BASE_DECLARE_FEATURE(kPreconnectOnRedirect);

COMPONENT_EXPORT(NETWORK_CPP)
Expand Down
Expand Up @@ -42,7 +42,7 @@ class WebPrescientNetworking {

// When a page navigation is speculated, DNS prefetch is triggered to hide
// the host resolution latency.
virtual void PrefetchDNS(const WebString& hostname) {}
virtual void PrefetchDNS(const WebURL& url) {}

virtual void Preconnect(const WebURL& url, bool allow_credentials) {}
};
Expand Down
10 changes: 8 additions & 2 deletions third_party/blink/renderer/core/html/html_anchor_element.cc
Expand Up @@ -26,6 +26,7 @@

#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "services/network/public/cpp/features.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom-blink.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
Expand Down Expand Up @@ -252,14 +253,19 @@ void HTMLAnchorElement::ParseAttribute(
// GetDocument().GetFrame() could be null if this method is called from
// DOMParser::parseFromString(), which internally creates a document
// and eventually calls this.
if (GetDocument().IsDNSPrefetchEnabled() && GetDocument().GetFrame()) {
static bool enable =
!base::FeatureList::IsEnabled(
network::features::kPrefetchDNSWithURL) ||
network::features::kPrefetchDNSWithURLAllAnchorElements.Get();
if (GetDocument().IsDNSPrefetchEnabled() && GetDocument().GetFrame() &&
enable) {
if (ProtocolIs(parsed_url, "http") || ProtocolIs(parsed_url, "https") ||
parsed_url.StartsWith("//")) {
WebPrescientNetworking* web_prescient_networking =
GetDocument().GetFrame()->PrescientNetworking();
if (web_prescient_networking) {
web_prescient_networking->PrefetchDNS(
GetDocument().CompleteURL(parsed_url).Host());
GetDocument().CompleteURL(parsed_url));
}
}
}
Expand Down
Expand Up @@ -28,7 +28,7 @@ class MockPrescientNetworking : public WebPrescientNetworking {
bool DidPreconnect() const { return did_preconnect_; }

private:
void PrefetchDNS(const WebString&) override { did_dns_prefetch_ = true; }
void PrefetchDNS(const WebURL&) override { did_dns_prefetch_ = true; }
void Preconnect(const WebURL&, bool) override { did_preconnect_ = true; }

bool did_dns_prefetch_ = false;
Expand Down
Expand Up @@ -25,7 +25,7 @@ class PreloaderNetworkHintsMock : public WebPrescientNetworking {
public:
PreloaderNetworkHintsMock() : did_preconnect_(false) {}

void PrefetchDNS(const WebString& hostname) override {}
void PrefetchDNS(const WebURL& url) override {}
void Preconnect(const WebURL& url, bool allow_credentials) override {
did_preconnect_ = true;
is_https_ = url.ProtocolIs("https");
Expand Down
4 changes: 1 addition & 3 deletions third_party/blink/renderer/core/loader/link_loader_test.cc
Expand Up @@ -61,9 +61,7 @@ class NetworkHintsMock : public WebPrescientNetworking {
public:
NetworkHintsMock() = default;

void PrefetchDNS(const WebString& hostname) override {
did_dns_prefetch_ = true;
}
void PrefetchDNS(const WebURL& url) override { did_dns_prefetch_ = true; }
void Preconnect(const WebURL& url, bool allow_credentials) override {
did_preconnect_ = true;
is_https_ = url.ProtocolIs("https");
Expand Down
2 changes: 1 addition & 1 deletion third_party/blink/renderer/core/loader/preload_helper.cc
Expand Up @@ -177,7 +177,7 @@ void PreloadHelper::DnsPrefetchIfNeeded(
WebPrescientNetworking* web_prescient_networking =
frame ? frame->PrescientNetworking() : nullptr;
if (web_prescient_networking) {
web_prescient_networking->PrefetchDNS(params.href.Host());
web_prescient_networking->PrefetchDNS(params.href);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion third_party/blink/renderer/core/page/chrome_client.cc
Expand Up @@ -161,7 +161,7 @@ void ChromeClient::MouseDidMoveOverElement(LocalFrame& frame,
WebPrescientNetworking* web_prescient_networking =
frame.PrescientNetworking();
if (web_prescient_networking) {
web_prescient_networking->PrefetchDNS(result.AbsoluteLinkURL().Host());
web_prescient_networking->PrefetchDNS(result.AbsoluteLinkURL());
}
}

Expand Down

0 comments on commit fe642ce

Please sign in to comment.