зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1740692 - Establish a speculative connection when receiving rel=preconnect in 103 response, r=necko-reviewers,manuel,valentin
Differential Revision: https://phabricator.services.mozilla.com/D163572
This commit is contained in:
Родитель
d0d17578a8
Коммит
4273a3fbb7
|
@ -11221,6 +11221,12 @@
|
||||||
value: false
|
value: false
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
|
# Enable `Link: rel=preconnect` in 103 Early Hint response.
|
||||||
|
- name: network.early-hints.preconnect.enabled
|
||||||
|
type: RelaxedAtomicBool
|
||||||
|
value: false
|
||||||
|
mirror: always
|
||||||
|
|
||||||
# Whether to use the network process or not
|
# Whether to use the network process or not
|
||||||
# Start a separate socket process. Performing networking on the socket process
|
# Start a separate socket process. Performing networking on the socket process
|
||||||
# is control by a sepparate pref
|
# is control by a sepparate pref
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "EarlyHintPreconnect.h"
|
||||||
|
|
||||||
|
#include "mozilla/CORSMode.h"
|
||||||
|
#include "mozilla/dom/Element.h"
|
||||||
|
#include "mozilla/StaticPrefs_network.h"
|
||||||
|
#include "nsIOService.h"
|
||||||
|
#include "nsIURI.h"
|
||||||
|
|
||||||
|
namespace mozilla::net {
|
||||||
|
|
||||||
|
void EarlyHintPreconnect::MaybePreconnect(const LinkHeader& aHeader,
|
||||||
|
nsIURI* aBaseURI,
|
||||||
|
nsIPrincipal* aPrincipal) {
|
||||||
|
if (!StaticPrefs::network_early_hints_preconnect_enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gIOService) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
if (NS_FAILED(aHeader.NewResolveHref(getter_AddRefs(uri), aBaseURI))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only preconnect secure context urls
|
||||||
|
if (!uri->SchemeIs("https")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that the http connection manager will limit the number of connections
|
||||||
|
// we can make, so it should be fine we don't check duplicate preconnect
|
||||||
|
// attempts here.
|
||||||
|
CORSMode corsMode = dom::Element::StringToCORSMode(aHeader.mCrossOrigin);
|
||||||
|
if (corsMode == CORS_ANONYMOUS) {
|
||||||
|
gIOService->SpeculativeAnonymousConnect(uri, aPrincipal, nullptr);
|
||||||
|
} else {
|
||||||
|
gIOService->SpeculativeConnect(uri, aPrincipal, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mozilla::net
|
|
@ -0,0 +1,24 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_net_EarlyHintPreconnect_h
|
||||||
|
#define mozilla_net_EarlyHintPreconnect_h
|
||||||
|
|
||||||
|
#include "nsNetUtil.h"
|
||||||
|
|
||||||
|
namespace mozilla::net {
|
||||||
|
|
||||||
|
class EarlyHintPreconnect final {
|
||||||
|
public:
|
||||||
|
static void MaybePreconnect(const LinkHeader& aHeader, nsIURI* aBaseURI,
|
||||||
|
nsIPrincipal* aPrincipal);
|
||||||
|
|
||||||
|
EarlyHintPreconnect() = delete;
|
||||||
|
EarlyHintPreconnect(const EarlyHintPreconnect&) = delete;
|
||||||
|
EarlyHintPreconnect& operator=(const EarlyHintPreconnect&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mozilla::net
|
||||||
|
|
||||||
|
#endif // mozilla_net_EarlyHintPreconnect_h
|
|
@ -190,10 +190,6 @@ void EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||||
nsIURI* aBaseURI, nsIPrincipal* aPrincipal,
|
nsIURI* aBaseURI, nsIPrincipal* aPrincipal,
|
||||||
nsICookieJarSettings* aCookieJarSettings,
|
nsICookieJarSettings* aCookieJarSettings,
|
||||||
const nsACString& aResponseReferrerPolicy) {
|
const nsACString& aResponseReferrerPolicy) {
|
||||||
if (!aLinkHeader.mRel.LowerCaseEqualsASCII("preload")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsAttrValue as;
|
nsAttrValue as;
|
||||||
ParseAsValue(aLinkHeader.mAs, as);
|
ParseAsValue(aLinkHeader.mAs, as);
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "EarlyHintsService.h"
|
#include "EarlyHintsService.h"
|
||||||
|
#include "EarlyHintPreconnect.h"
|
||||||
#include "EarlyHintPreloader.h"
|
#include "EarlyHintPreloader.h"
|
||||||
#include "mozilla/PreloadHashKey.h"
|
#include "mozilla/PreloadHashKey.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
|
@ -85,9 +86,13 @@ void EarlyHintsService::EarlyHint(const nsACString& aLinkHeader,
|
||||||
|
|
||||||
for (auto& linkHeader : linkHeaders) {
|
for (auto& linkHeader : linkHeaders) {
|
||||||
CollectLinkTypeTelemetry(linkHeader.mRel);
|
CollectLinkTypeTelemetry(linkHeader.mRel);
|
||||||
EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
if (linkHeader.mRel.LowerCaseEqualsLiteral("preconnect")) {
|
||||||
mOngoingEarlyHints, linkHeader, aBaseURI, principal, cookieJarSettings,
|
EarlyHintPreconnect::MaybePreconnect(linkHeader, aBaseURI, principal);
|
||||||
aReferrerPolicy);
|
} else if (linkHeader.mRel.LowerCaseEqualsLiteral("preload")) {
|
||||||
|
EarlyHintPreloader::MaybeCreateAndInsertPreload(
|
||||||
|
mOngoingEarlyHints, linkHeader, aBaseURI, principal,
|
||||||
|
cookieJarSettings, aReferrerPolicy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,7 @@ UNIFIED_SOURCES += [
|
||||||
"ConnectionEntry.cpp",
|
"ConnectionEntry.cpp",
|
||||||
"ConnectionHandle.cpp",
|
"ConnectionHandle.cpp",
|
||||||
"DnsAndConnectSocket.cpp",
|
"DnsAndConnectSocket.cpp",
|
||||||
|
"EarlyHintPreconnect.cpp",
|
||||||
"EarlyHintPreloader.cpp",
|
"EarlyHintPreloader.cpp",
|
||||||
"EarlyHintRegistrar.cpp",
|
"EarlyHintRegistrar.cpp",
|
||||||
"EarlyHintsService.cpp",
|
"EarlyHintsService.cpp",
|
||||||
|
|
|
@ -2262,8 +2262,12 @@ nsresult nsHttpHandler::SpeculativeConnectInternal(
|
||||||
if (mDebugObservations && obsService) {
|
if (mDebugObservations && obsService) {
|
||||||
// this is basically used for test coverage of an otherwise 'hintable'
|
// this is basically used for test coverage of an otherwise 'hintable'
|
||||||
// feature
|
// feature
|
||||||
|
|
||||||
|
// This is used to test if the `crossOrigin` attribute is parsed correctly.
|
||||||
|
nsPrintfCString debugURL("%s%s", aURI->GetSpecOrDefault().get(),
|
||||||
|
anonymous ? "anonymous" : "use-credentials");
|
||||||
obsService->NotifyObservers(nullptr, "speculative-connect-request",
|
obsService->NotifyObservers(nullptr, "speculative-connect-request",
|
||||||
nullptr);
|
NS_ConvertUTF8toUTF16(debugURL).get());
|
||||||
for (auto* cp :
|
for (auto* cp :
|
||||||
dom::ContentParent::AllProcesses(dom::ContentParent::eLive)) {
|
dom::ContentParent::AllProcesses(dom::ContentParent::eLive)) {
|
||||||
PNeckoParent* neckoParent =
|
PNeckoParent* neckoParent =
|
||||||
|
|
|
@ -11,6 +11,7 @@ support-files =
|
||||||
early_hint_error.sjs
|
early_hint_error.sjs
|
||||||
early_hint_asset.sjs
|
early_hint_asset.sjs
|
||||||
early_hint_asset_html.sjs
|
early_hint_asset_html.sjs
|
||||||
|
early_hint_preconnect_html.sjs
|
||||||
post.html
|
post.html
|
||||||
res.css
|
res.css
|
||||||
res.css^headers^
|
res.css^headers^
|
||||||
|
@ -146,3 +147,4 @@ skip-if =
|
||||||
support-files =
|
support-files =
|
||||||
early_hint_referrer_policy_html.sjs
|
early_hint_referrer_policy_html.sjs
|
||||||
early_hint_preload_test_helper.jsm
|
early_hint_preload_test_helper.jsm
|
||||||
|
[browser_103_preconnect.js]
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("network.early-hints.enabled", true);
|
||||||
|
Services.prefs.setBoolPref("network.early-hints.preconnect.enabled", true);
|
||||||
|
Services.prefs.setBoolPref("network.http.debug-observations", true);
|
||||||
|
|
||||||
|
registerCleanupFunction(function() {
|
||||||
|
Services.prefs.clearUserPref("network.early-hints.enabled");
|
||||||
|
Services.prefs.clearUserPref("network.early-hints.preconnect.enabled");
|
||||||
|
Services.prefs.clearUserPref("network.http.debug-observations");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test steps:
|
||||||
|
// 1. Load early_hint_preconnect_html.sjs
|
||||||
|
// 2. In early_hint_preconnect_html.sjs, a 103 response with
|
||||||
|
// "rel=preconnect" is returned.
|
||||||
|
// 3. We use "speculative-connect-request" topic to observe whether the
|
||||||
|
// speculative connection is attempted.
|
||||||
|
// 4. Finally, we check if the observed URL is the same as the expected.
|
||||||
|
async function test_hint_preconnect(href, crossOrigin) {
|
||||||
|
let requestUrl = `https://example.com/browser/netwerk/test/browser/early_hint_preconnect_html.sjs?href=${href}&crossOrigin=${crossOrigin}`;
|
||||||
|
|
||||||
|
let observed = "";
|
||||||
|
let observer = {
|
||||||
|
QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
|
||||||
|
observe(aSubject, aTopic, aData) {
|
||||||
|
if (aTopic == "speculative-connect-request") {
|
||||||
|
Services.obs.removeObserver(observer, "speculative-connect-request");
|
||||||
|
observed = aData;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Services.obs.addObserver(observer, "speculative-connect-request");
|
||||||
|
|
||||||
|
await BrowserTestUtils.withNewTab(
|
||||||
|
{
|
||||||
|
gBrowser,
|
||||||
|
url: requestUrl,
|
||||||
|
waitForLoad: true,
|
||||||
|
},
|
||||||
|
async function() {}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!crossOrigin) {
|
||||||
|
crossOrigin = "anonymous";
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.equal(observed, `${href}/${crossOrigin}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function test_103_preconnect() {
|
||||||
|
await test_hint_preconnect("https://localhost", "use-credentials");
|
||||||
|
await test_hint_preconnect("https://localhost", "");
|
||||||
|
await test_hint_preconnect("https://localhost", "anonymous");
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function handleRequest(request, response) {
|
||||||
|
Cu.importGlobalProperties(["URLSearchParams"]);
|
||||||
|
let qs = new URLSearchParams(request.queryString);
|
||||||
|
let href = qs.get("href");
|
||||||
|
let crossOrigin = qs.get("crossOrigin");
|
||||||
|
|
||||||
|
// write to raw socket
|
||||||
|
response.seizePower();
|
||||||
|
|
||||||
|
response.write("HTTP/1.1 103 Early Hint\r\n");
|
||||||
|
response.write(
|
||||||
|
`Link: <${href}>; rel=preconnect; crossOrigin=${crossOrigin}\r\n`
|
||||||
|
);
|
||||||
|
response.write("\r\n");
|
||||||
|
|
||||||
|
let body = `<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>Test rel=preconnect<h1>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
|
||||||
|
response.write("HTTP/1.1 200 OK\r\n");
|
||||||
|
response.write("Content-Type: text/html;charset=utf-8\r\n");
|
||||||
|
response.write("Cache-Control: no-cache\r\n");
|
||||||
|
response.write(`Content-Length: ${body.length}\r\n`);
|
||||||
|
response.write("\r\n");
|
||||||
|
response.write(body);
|
||||||
|
|
||||||
|
response.finish();
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче