From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../browser/browser_103_redirect_from_server.js | 321 +++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 netwerk/test/browser/browser_103_redirect_from_server.js (limited to 'netwerk/test/browser/browser_103_redirect_from_server.js') diff --git a/netwerk/test/browser/browser_103_redirect_from_server.js b/netwerk/test/browser/browser_103_redirect_from_server.js new file mode 100644 index 0000000000..0357d11516 --- /dev/null +++ b/netwerk/test/browser/browser_103_redirect_from_server.js @@ -0,0 +1,321 @@ +/* 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/. */ + +"use strict"; + +Services.prefs.setBoolPref("network.early-hints.enabled", true); + +registerCleanupFunction(function () { + Services.prefs.clearUserPref("network.early-hints.enabled"); +}); + +// This function tests Early Hint responses before and in between HTTP redirects. +// +// Arguments: +// - name: String identifying the test case for easier parsing in the log +// - chain and destination: defines the redirect chain, see example below +// note: ALL preloaded urls must be image urls +// - expected: number of normal, cancelled and completed hinted responses. +// +// # Example +// The parameter values of +// ``` +// chain = [ +// {link:"https://link1", host:"https://host1.com"}, +// {link:"https://link2", host:"https://host2.com"}, +// ] +// ``` +// and `destination = "https://host3.com/page.html" would result in the +// following HTTP exchange (simplified): +// +// ``` +// > GET https://host1.com/redirect?something1 +// +// < 103 Early Hints +// < Link: ;rel=preload;as=image +// < +// < 307 Temporary Redirect +// < Location: https://host2.com/redirect?something2 +// < +// +// > GET https://host2.com/redirect?something2 +// +// < 103 Early Hints +// < Link: ;rel=preload;as=image +// < +// < 307 Temporary Redirect +// < Location: https://host3.com/page.html +// < +// +// > GET https://host3.com/page.html +// +// < [...] Result depends on the final page +// ``` +// +// Legend: +// * `>` indicates a request going from client to server +// * `<` indicates a response going from server to client +// * all lines are terminated with a `\r\n` +// +async function test_hint_redirect( + name, + chain, + destination, + hint_destination, + expected +) { + // pass the full redirect chain as a url parameter. Each redirect is handled + // by `early_hint_redirect_html.sjs` which url-decodes the query string and + // redirects to the result + let links = []; + let url = destination; + for (let i = chain.length - 1; i >= 0; i--) { + let qp = new URLSearchParams(); + if (chain[i].link != "") { + qp.append("link", "<" + chain[i].link + ">;rel=preload;as=image"); + links.push(chain[i].link); + } + qp.append("location", url); + + url = `${ + chain[i].host + }/browser/netwerk/test/browser/early_hint_redirect_html.sjs?${qp.toString()}`; + } + if (hint_destination != "") { + links.push(hint_destination); + } + + // reset the count + let headers = new Headers(); + headers.append("X-Early-Hint-Count-Start", ""); + await fetch( + "https://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs", + { headers } + ); + + // main request and all other must get their respective OnStopRequest + let numRequestRemaining = + expected.normal + expected.hinted + expected.cancelled; + let observed = { + hinted: 0, + normal: 0, + cancelled: 0, + }; + // store channelIds + let observedChannelIds = []; + let callback; + let promise = new Promise(resolve => { + callback = resolve; + }); + if (numRequestRemaining > 0) { + let observer = { + QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), + observe(aSubject, aTopic, aData) { + aSubject.QueryInterface(Ci.nsIIdentChannel); + let id = aSubject.channelId; + if (observedChannelIds.includes(id)) { + return; + } + aSubject.QueryInterface(Ci.nsIRequest); + dump("Observer aSubject.name " + aSubject.name + "\n"); + if (aTopic == "http-on-stop-request" && links.includes(aSubject.name)) { + if (aSubject.status == Cr.NS_ERROR_ABORT) { + observed.cancelled += 1; + } else { + aSubject.QueryInterface(Ci.nsIHttpChannel); + let initiator = ""; + try { + initiator = aSubject.getRequestHeader("X-Moz"); + } catch {} + if (initiator == "early hint") { + observed.hinted += 1; + } else { + observed.normal += 1; + } + } + observedChannelIds.push(id); + numRequestRemaining -= 1; + dump("Observer numRequestRemaining " + numRequestRemaining + "\n"); + } + if (numRequestRemaining == 0) { + Services.obs.removeObserver(observer, "http-on-stop-request"); + callback(); + } + }, + }; + Services.obs.addObserver(observer, "http-on-stop-request"); + } else { + callback(); + } + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url, + waitForLoad: true, + }, + async function () {} + ); + + // wait until all requests are stopped, especially the cancelled ones + await promise; + + let got = await fetch( + "https://example.com/browser/netwerk/test/browser/early_hint_pixel_count.sjs" + ).then(response => response.json()); + + // stringify to pretty print assert output + let g = JSON.stringify(observed); + let e = JSON.stringify(expected); + Assert.equal( + expected.normal, + observed.normal, + `${name} normal observed from client expected ${expected.normal} (${e}) got ${observed.normal} (${g})` + ); + Assert.equal( + expected.hinted, + observed.hinted, + `${name} hinted observed from client expected ${expected.hinted} (${e}) got ${observed.hinted} (${g})` + ); + Assert.equal( + expected.cancelled, + observed.cancelled, + `${name} cancelled observed from client expected ${expected.cancelled} (${e}) got ${observed.cancelled} (${g})` + ); + + // each cancelled request might be cancelled after the request was already + // made. Allow cancelled responses to count towards the hinted to avoid + // intermittent test failures. + Assert.ok( + expected.hinted <= got.hinted && + got.hinted <= expected.hinted + expected.cancelled, + `${name}: unexpected amount of hinted request made got ${ + got.hinted + }, expected between ${expected.hinted} and ${ + expected.hinted + expected.cancelled + }` + ); + Assert.ok( + got.normal == expected.normal, + `${name}: unexpected amount of normal request made expected ${expected.normal}, got ${got.normal}` + ); + Assert.equal(numRequestRemaining, 0, "Requests remaining"); +} + +add_task(async function double_redirect_cross_origin() { + await test_hint_redirect( + "double_redirect_cross_origin_both_hints", + [ + { + link: "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + host: "https://example.com/", + }, + { + link: "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + host: "https://example.net", + }, + ], + "https://example.org/browser/netwerk/test/browser/early_hint_asset_html.sjs?as=image&hinted=1", + "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + { hinted: 1, normal: 0, cancelled: 2 } + ); + await test_hint_redirect( + "double_redirect_second_hint", + [ + { + link: "", + host: "https://example.com/", + }, + { + link: "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + host: "https://example.net", + }, + ], + "https://example.org/browser/netwerk/test/browser/early_hint_asset_html.sjs?as=image&hinted=1", + "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + { hinted: 1, normal: 0, cancelled: 1 } + ); + await test_hint_redirect( + "double_redirect_first_hint", + [ + { + link: "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + host: "https://example.com/", + }, + { + link: "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + host: "https://example.net", + }, + ], + "https://example.org/browser/netwerk/test/browser/early_hint_asset_html.sjs?as=image&hinted=0", + "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + { hinted: 0, normal: 1, cancelled: 2 } + ); +}); + +add_task(async function redirect_cross_origin() { + await test_hint_redirect( + "redirect_cross_origin_start_second_preload", + [ + { + link: "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + host: "https://example.net", + }, + ], + "https://example.org/browser/netwerk/test/browser/early_hint_asset_html.sjs?as=image&hinted=1", + "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + { hinted: 1, normal: 0, cancelled: 1 } + ); + await test_hint_redirect( + "redirect_cross_origin_dont_use_first_preload", + [ + { + link: "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image&a", + host: "https://example.net", + }, + ], + "https://example.org/browser/netwerk/test/browser/early_hint_asset_html.sjs?as=image&hinted=0", + "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + { hinted: 0, normal: 1, cancelled: 1 } + ); +}); + +add_task(async function redirect_same_origin() { + await test_hint_redirect( + "hint_before_redirect_same_origin", + [ + { + link: "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + host: "https://example.org", + }, + ], + "https://example.org/browser/netwerk/test/browser/early_hint_asset_html.sjs?as=image&hinted=1", + "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + { hinted: 1, normal: 0, cancelled: 0 } + ); + await test_hint_redirect( + "hint_after_redirect_same_origin", + [ + { + link: "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + host: "https://example.org", + }, + ], + "https://example.org/browser/netwerk/test/browser/early_hint_asset_html.sjs?as=image&hinted=0", + "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + { hinted: 1, normal: 0, cancelled: 0 } + ); + await test_hint_redirect( + "hint_after_redirect_same_origin", + [ + { + link: "", + host: "https://example.org", + }, + ], + "https://example.org/browser/netwerk/test/browser/early_hint_asset_html.sjs?as=image&hinted=1", + "https://example.org/browser/netwerk/test/browser/early_hint_asset.sjs?as=image", + { hinted: 1, normal: 0, cancelled: 0 } + ); +}); -- cgit v1.2.3