Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'win' && os_version == '11.26100' && arch == 'x86_64' && msix OR os == 'win' && os_version == '11.26200' && arch == 'x86_64' && msix
- 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
//
// Verifies that ssl_tokens_cache.bin is written to the profile directory
// for each of the three triggers:
// 1. "idle-daily" — once-a-day write
// 2. "application-background" — Android backgrounding write
// 3. profile-before-change via AsyncShutdown — on-quit write
//
// The network.ssl_tokens_cache_persistence pref must be in the toml [prefs]
// block so that SSLTokensCache::Init() sees it when the IO service starts
// (triggered by head.js before any test code runs).
//
// profile-after-change fires during the test infrastructure setup; our
// Observe() handler sets mBackingFile and registers the shutdown blocker.
"use strict";
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
const { AsyncShutdown } = ChromeUtils.importESModule(
"resource://gre/modules/AsyncShutdown.sys.mjs"
);
let gProfileDir = null;
let gCacheFile = null;
add_setup({ skip_if: () => AppConstants.MOZ_SYSTEM_NSS }, async () => {
const { HttpServer } = ChromeUtils.importESModule(
);
// head_http3.js and head_trr.js call do_get_profile() at load time, setting
// _profileInitialized = true. A subsequent do_get_profile(true) early-
// returns without firing profile-after-change. Fire it explicitly so that
// SSLTokensCache::Observe() sets mBackingFile and schedules the shutdown
// blocker.
gProfileDir = do_get_profile();
gCacheFile = PathUtils.join(gProfileDir.path, "ssl_tokens_cache.bin");
Services.obs.notifyObservers(
null,
"profile-after-change",
"xpcshell-persist-test"
);
// Yield so async setup triggered by the profile-after-change handler
// (background load, etc.) has a chance to execute.
await new Promise(resolve => do_timeout(0, resolve));
let httpServer = new HttpServer();
httpServer.registerPathHandler("/", (req, resp) => {
resp.setStatusLine(req.httpVersion, 200, "OK");
resp.setHeader("Content-Type", "text/plain");
resp.bodyOutputStream.write("OK", 2);
});
httpServer.start(-1);
registerCleanupFunction(async () => httpServer.stop());
await asyncSetupFaultyServer(httpServer);
});
// Makes one HTTPS connection to a FaultyServer host. The first connection
// always succeeds and the server issues a NewSessionTicket (MOZ_TLS_SERVER_0RTT
// is set by asyncSetupFaultyServer), populating the in-memory cache.
async function makeConnection() {
const kHost = "decrypt-error-on-resume.example.com";
Services.prefs.setCharPref("network.dns.localDomains", kHost);
registerCleanupFunction(() =>
Services.prefs.clearUserPref("network.dns.localDomains")
);
let [, buf] = await channelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL);
ok(buf, "connection succeeded and NewSessionTicket was issued");
}
// Polls until ssl_tokens_cache.bin appears (or the timeout is reached).
async function waitForCacheFile() {
for (let i = 0; i < 50; i++) {
if (await IOUtils.exists(gCacheFile)) {
return true;
}
await new Promise(resolve => do_timeout(100, resolve));
}
return false;
}
// --- Test 1: idle-daily --------------------------------------------------
add_task(
{ skip_if: () => AppConstants.MOZ_SYSTEM_NSS },
async function test_ssl_token_cache_written_on_idle_daily() {
await makeConnection();
await IOUtils.remove(gCacheFile, { ignoreAbsent: true });
Services.obs.notifyObservers(null, "idle-daily");
ok(
await waitForCacheFile(),
"ssl_tokens_cache.bin written after idle-daily"
);
const info = await IOUtils.stat(gCacheFile);
Assert.greater(
info.size,
0,
`cache file is non-empty (${info.size} bytes)`
);
}
);
// --- Test 2: application-background -------------------------------------
add_task(
{ skip_if: () => AppConstants.MOZ_SYSTEM_NSS },
async function test_ssl_token_cache_written_on_application_background() {
await makeConnection();
await IOUtils.remove(gCacheFile, { ignoreAbsent: true });
Services.obs.notifyObservers(null, "application-background");
ok(
await waitForCacheFile(),
"ssl_tokens_cache.bin written after application-background"
);
const info = await IOUtils.stat(gCacheFile);
Assert.greater(
info.size,
0,
`cache file is non-empty (${info.size} bytes)`
);
}
);
// --- Test 3: profile-before-change (on-quit) ----------------------------
add_task(
{ skip_if: () => AppConstants.MOZ_SYSTEM_NSS },
async function test_ssl_token_cache_written_on_quit() {
await makeConnection();
await IOUtils.remove(gCacheFile, { ignoreAbsent: true });
// Simulate the profile-before-change shutdown phase. The async shutdown
// blocker registered by SSLTokensCache calls BlockShutdown, which
// dispatches DoWrite(true) to the write task queue. _trigger() awaits
// all blockers, so when it resolves the write has completed.
Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true);
registerCleanupFunction(() =>
Services.prefs.clearUserPref("toolkit.asyncshutdown.testing")
);
await AsyncShutdown.profileBeforeChange._trigger();
ok(
await IOUtils.exists(gCacheFile),
"ssl_tokens_cache.bin written after profile-before-change"
);
const info = await IOUtils.stat(gCacheFile);
Assert.greater(
info.size,
0,
`cache file is non-empty (${info.size} bytes)`
);
}
);