Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

  • This test gets skipped with pattern: os == 'win' && os_version == '10.2009' && arch == 'x86_64' OR os == 'win' && os_version == '11.26100' && arch == 'x86' OR os == 'win' && os_version == '11.26100' && arch == 'x86_64' OR os == 'win' && os_version == '11.26200' && arch == 'x86' OR os == 'win' && os_version == '11.26200' && arch == 'x86_64'
  • Manifest: netwerk/test/unit/xpcshell.toml
/* 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";
// Tests that QUIC resumption tokens (address validation tokens) are not reused
// across first-party partition contexts. A token obtained in one first-party
// context must not allow 0-RTT resumption in a different first-party context.
var { setTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
registerCleanupFunction(async () => {
http3_clear_prefs();
Services.prefs.clearUserPref("network.http.http3.enable_0rtt");
});
add_task(async function setup() {
await http3_setup_tests("h3");
});
function makeChan(uri, partitionKey) {
let chan = NetUtil.newChannel({
uri,
loadUsingSystemPrincipal: true,
}).QueryInterface(Ci.nsIHttpChannel);
chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
if (partitionKey) {
chan.loadInfo.originAttributes = { partitionKey };
}
return chan;
}
function chanPromise(chan) {
return new Promise(resolve => {
chan.asyncOpen({
onStartRequest(_request) {},
onDataAvailable(_request, stream, _offset, count) {
read_stream(stream, count);
},
onStopRequest(request) {
resolve(request);
},
});
});
}
async function cancelAllAndWait() {
Services.obs.notifyObservers(null, "net:cancel-all-connections");
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 1000));
}
// Verifies that a QUIC resumption token stored under first-party context A
// cannot be used for 0-RTT resumption in first-party context B, and that
// resumption within the same first-party context still works.
add_task(async function test_quic_token_not_reused_across_partitions() {
Services.prefs.setBoolPref("network.http.http3.enable_0rtt", true);
// Initial connection under partition key A. No prior token exists, so the
// handshake is not resumed. The server sends a resumption token stored under
// A's peerId.
let chanA1 = makeChan("https://foo.example.com/30", "(https,a.example.com)");
let reqA1 = await chanPromise(chanA1);
Assert.equal(reqA1.status, Cr.NS_OK, "first A connection succeeded");
Assert.equal(reqA1.protocolVersion, "h3", "first A connection used H3");
Assert.equal(
reqA1.securityInfo.resumed,
false,
"first A connection not resumed (no prior token)"
);
await cancelAllAndWait();
// Connection under partition key B to the same server. The token stored under
// A's peerId must not be visible under B's peerId.
let chanB = makeChan("https://foo.example.com/30", "(https,b.example.com)");
let reqB = await chanPromise(chanB);
Assert.equal(reqB.status, Cr.NS_OK, "B connection succeeded");
Assert.equal(reqB.protocolVersion, "h3", "B connection used H3");
Assert.equal(
reqB.securityInfo.resumed,
false,
"B connection not resumed (A's token must not be reused across partitions)"
);
Assert.notEqual(
reqA1.securityInfo.peerId,
reqB.securityInfo.peerId,
"A and B use distinct peerIds confirming they run on separate QUIC sessions"
);
await cancelAllAndWait();
// Reconnect under partition key A. The token from the first A connection is
// still cached under A's peerId, so this connection must be resumed.
let chanA2 = makeChan("https://foo.example.com/30", "(https,a.example.com)");
let reqA2 = await chanPromise(chanA2);
Assert.equal(reqA2.status, Cr.NS_OK, "second A connection succeeded");
Assert.equal(reqA2.protocolVersion, "h3", "second A connection used H3");
Assert.equal(
reqA2.securityInfo.resumed,
true,
"second A connection resumed using A's own token"
);
Assert.equal(
reqA1.securityInfo.peerId,
reqA2.securityInfo.peerId,
"A1 and A2 have the same peerId"
);
});