perf: reduce resident dependencies and duplicate WebSocket authentication (#3071)

* perf: load HTTP dependencies only in the HTTP worker

* docs: record 30-minute idle resource comparison

* perf: isolate database initialization from the cluster primary

* docs: record isolated database startup resource comparison

* perf: avoid an extra shell child in Docker health probes

* perf: load notification dependencies only when needed

* perf: retain queue cleanup after notification loading comparison

* fix: stop page polling after unmount and avoid duplicate fetches

* perf: share WebSocket session polling across connections

* docs: measure real WebSocket container CPU and memory

* perf: reuse JWT validation within each WebSocket session check

* perf: bound login IP lookup allocations with an address cache

* refactor: limit resource optimization PR to three core changes

* docs: benchmark the three core optimizations against develop

* chore: remove documentation and benchmark artifacts from PR
This commit is contained in:
whyour
2026-09-18 20:22:18 +08:00
committed by GitHub
parent a60ec5587a
commit d9833c17d5
9 changed files with 779 additions and 54 deletions
+33 -29
View File
@@ -1,17 +1,12 @@
import 'reflect-metadata'; import 'reflect-metadata';
import cluster, { type Worker } from 'cluster'; import cluster, { type Worker } from 'cluster';
import compression from 'compression'; import type express from 'express';
import cors from 'cors';
import express from 'express';
import helmet from 'helmet';
import { Container } from 'typedi'; import { Container } from 'typedi';
import config from './config'; import config from './config';
import Logger from './loaders/logger'; import Logger from './loaders/logger';
import { monitoringMiddleware } from './middlewares/monitoring'; import { errStack } from './shared/errors';
import { errStack } from './config/util';
import { type GrpcServerService } from './services/grpc'; import { type GrpcServerService } from './services/grpc';
import { type HttpServerService } from './services/http'; import { type HttpServerService } from './services/http';
import cronClient from './schedule/client';
interface WorkerMetadata { interface WorkerMetadata {
id: number; id: number;
@@ -21,24 +16,12 @@ interface WorkerMetadata {
} }
class Application { class Application {
private app: express.Application;
private httpServerService?: HttpServerService; private httpServerService?: HttpServerService;
private grpcServerService?: GrpcServerService; private grpcServerService?: GrpcServerService;
private isShuttingDown = false; private isShuttingDown = false;
private workerMetadataMap = new Map<number, WorkerMetadata>(); private workerMetadataMap = new Map<number, WorkerMetadata>();
private httpWorker?: Worker; private httpWorker?: Worker;
constructor() {
this.app = express();
// 创建一个全局中间件,删除查询参数中的t
this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
if (req.query.t) {
delete req.query.t;
}
next();
});
}
async start() { async start() {
try { try {
if (cluster.isPrimary) { if (cluster.isPrimary) {
@@ -158,17 +141,37 @@ class Application {
} }
private async initializeDatabase() { private async initializeDatabase() {
const dbLoader = await import('./loaders/db'); const { runStartupProcess } = await import('./shared/startupProcess');
await dbLoader.default(); await runStartupProcess(require.resolve('./bootstrap/database'));
} }
private setupMiddlewares() { private async setupMiddlewares(): Promise<express.Application> {
this.app.use(helmet({ // Only the HTTP worker needs an Express application and its middleware.
const [
{ default: createExpress },
{ default: helmet },
{ default: cors },
{ default: compression },
{ monitoringMiddleware },
] = await Promise.all([
import('express'),
import('helmet'),
import('cors'),
import('compression'),
import('./middlewares/monitoring'),
]);
const app = createExpress();
app.use((req, res, next) => {
if (req.query.t) delete req.query.t;
next();
});
app.use(helmet({
contentSecurityPolicy: false, contentSecurityPolicy: false,
})); }));
this.app.use(cors(config.cors)); app.use(cors(config.cors));
this.app.use(compression()); app.use(compression());
this.app.use(monitoringMiddleware); app.use(monitoringMiddleware);
return app;
} }
private setupMasterShutdown() { private setupMasterShutdown() {
@@ -247,16 +250,16 @@ class Application {
const { initGrpcCerts } = await import('./config/grpcCerts'); const { initGrpcCerts } = await import('./config/grpcCerts');
await initGrpcCerts(); await initGrpcCerts();
this.setupMiddlewares(); const app = await this.setupMiddlewares();
const { HttpServerService } = await import('./services/http'); const { HttpServerService } = await import('./services/http');
this.httpServerService = Container.get(HttpServerService); this.httpServerService = Container.get(HttpServerService);
const appLoader = await import('./loaders/app'); const appLoader = await import('./loaders/app');
await appLoader.default({ app: this.app }); await appLoader.default({ app });
const server = await this.httpServerService.initialize( const server = await this.httpServerService.initialize(
this.app, app,
config.port, config.port,
); );
@@ -279,6 +282,7 @@ class Application {
this.gracefulShutdown(serviceType); this.gracefulShutdown(serviceType);
} else if (serviceType === 'http' && } else if (serviceType === 'http' &&
(msg === 'reregister-crons' || msg === 'scheduler-unavailable')) { (msg === 'reregister-crons' || msg === 'scheduler-unavailable')) {
const { default: cronClient } = await import('./schedule/client');
cronClient.readiness.invalidate(); cronClient.readiness.invalidate();
} }
}); });
+26
View File
@@ -0,0 +1,26 @@
import { errStack } from '../shared/errors';
// Also terminate if the primary disappears without forwarding a signal.
const onParentDisconnect = () => process.exit(1);
process.once('disconnect', onParentDisconnect);
async function initializeDatabase() {
const { sequelize } = await import('../data');
try {
const { default: loadDatabase } = await import('../loaders/db');
await loadDatabase();
} finally {
await sequelize.close();
}
}
initializeDatabase()
.catch((error) => {
console.error(`[boot] Database initialization failed:\n${errStack(error)}`);
process.exitCode = 1;
})
.finally(() => {
process.removeListener('disconnect', onParentDisconnect);
// Let pending log writes drain and exit naturally after closing SQLite.
if (process.connected) process.disconnect();
});
+1 -5
View File
@@ -652,11 +652,7 @@ export function safeJSONParse(value?: string) {
} }
} }
export function errStack(error: unknown): string { export { errStack } from '../shared/errors';
return error instanceof Error && error.stack
? error.stack
: String(error);
}
export async function rmPath(path: string) { export async function rmPath(path: string) {
try { try {
+59 -19
View File
@@ -1,4 +1,4 @@
import sockJs from 'sockjs'; import sockJs, { Connection } from 'sockjs';
import { Server } from 'http'; import { Server } from 'http';
import { Container } from 'typedi'; import { Container } from 'typedi';
import SockService from '../services/sock'; import SockService from '../services/sock';
@@ -13,6 +13,41 @@ export default async ({ server }: { server: Server }) => {
log: () => {}, log: () => {},
}); });
const sockService = Container.get(SockService); const sockService = Container.get(SockService);
const sessions = new Map<Connection, { token: string; platform: string }>();
let sessionTimer: ReturnType<typeof setInterval> | undefined;
let checking = false;
const checkSessions = async () => {
if (checking || sessions.size === 0) return;
checking = true;
// Connections accepted during this read must not use an older snapshot.
const batch = [...sessions];
try {
const current = await shareStore.getAuthInfo();
// Reuse validation only within this synchronous check of one auth snapshot.
const validated = new Map<string, Map<string, boolean>>();
for (const [conn, { token, platform }] of batch) {
if (!sessions.has(conn)) continue;
let platforms = validated.get(token);
if (!platforms) {
platforms = new Map();
validated.set(token, platforms);
}
let valid = platforms.get(platform);
if (valid === undefined) {
valid = isValidToken(current, token, platform, config.jwt.secret);
platforms.set(platform, valid);
}
if (!valid) conn.close('401');
}
} catch {
for (const [conn] of batch) {
if (sessions.has(conn)) conn.close('401');
}
} finally {
checking = false;
}
};
echo.on('connection', async (conn) => { echo.on('connection', async (conn) => {
if (!conn.headers || !conn.url || !conn.pathname) { if (!conn.headers || !conn.url || !conn.pathname) {
@@ -20,35 +55,40 @@ export default async ({ server }: { server: Server }) => {
return; return;
} }
const authInfo = await shareStore.getAuthInfo(); let closed = false;
conn.on('close', () => {
closed = true;
sessions.delete(conn);
sockService.removeClient(conn);
if (sessions.size === 0 && sessionTimer) {
clearInterval(sessionTimer);
sessionTimer = undefined;
}
});
let authInfo;
try {
authInfo = await shareStore.getAuthInfo();
} catch {
if (!closed) conn.close('401');
return;
}
if (closed) return;
const platform = getPlatform(conn.headers['user-agent'] || '') || 'desktop'; const platform = getPlatform(conn.headers['user-agent'] || '') || 'desktop';
const headerToken = conn.url.replace(`${conn.pathname}?token=`, ''); const headerToken = conn.url.replace(`${conn.pathname}?token=`, '');
if (isValidToken(authInfo, headerToken, platform, config.jwt.secret)) { if (isValidToken(authInfo, headerToken, platform, config.jwt.secret)) {
sockService.addClient(conn); sockService.addClient(conn);
const checkSession = setInterval(async () => { sessions.set(conn, { token: headerToken, platform });
try { if (!sessionTimer) {
const current = await shareStore.getAuthInfo(); sessionTimer = setInterval(checkSessions, 1000);
if ( sessionTimer.unref();
!isValidToken(current, headerToken, platform, config.jwt.secret)
) {
conn.close('401');
} }
} catch {
conn.close('401');
}
}, 1000);
checkSession.unref();
conn.on('data', (message) => { conn.on('data', (message) => {
conn.write(message); conn.write(message);
}); });
conn.on('close', function () {
clearInterval(checkSession);
sockService.removeClient(conn);
});
return; return;
} }
+4
View File
@@ -0,0 +1,4 @@
// Keep error formatting independent of database, HTTP and gRPC dependencies.
export function errStack(error: unknown): string {
return error instanceof Error && error.stack ? error.stack : String(error);
}
+47
View File
@@ -0,0 +1,47 @@
import { fork } from 'child_process';
// Keep one-off startup dependencies out of the long-lived cluster primary.
export function runStartupProcess(entrypoint: string): Promise<void> {
return new Promise((resolve, reject) => {
const child = fork(entrypoint, [], { stdio: 'inherit' });
let interrupted: NodeJS.Signals | undefined;
let killTimer: NodeJS.Timeout | undefined;
const terminate = (signal: NodeJS.Signals) => {
if (interrupted) return;
interrupted = signal;
child.kill(signal);
killTimer = setTimeout(() => child.kill('SIGKILL'), 8000);
killTimer.unref();
};
const onSigterm = () => terminate('SIGTERM');
const onSigint = () => terminate('SIGINT');
const onExit = () => child.kill('SIGKILL');
const cleanup = () => {
if (killTimer) clearTimeout(killTimer);
process.removeListener('SIGTERM', onSigterm);
process.removeListener('SIGINT', onSigint);
process.removeListener('exit', onExit);
};
process.once('SIGTERM', onSigterm);
process.once('SIGINT', onSigint);
process.once('exit', onExit);
child.once('error', (error) => {
cleanup();
reject(error);
});
child.once('close', (code, signal) => {
cleanup();
if (code === 0 && !interrupted) {
resolve();
} else {
reject(
new Error(
`Startup process failed (${interrupted || signal || code})`,
),
);
}
});
});
}
+193
View File
@@ -0,0 +1,193 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { fork } = require('node:child_process');
const { once } = require('node:events');
const { Sequelize, QueryTypes } = require('sequelize');
const ts = require('typescript');
const { createRequire } = require('node:module');
const initializer = require.resolve('../../back/bootstrap/database');
const startup = require.resolve('../../back/shared/startupProcess');
function fixture(t) {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ql-bootstrap-'));
t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
fs.mkdirSync(path.join(dir, 'data/db'), { recursive: true });
fs.mkdirSync(path.join(dir, 'data/syslog'), { recursive: true });
fs.writeFileSync(path.join(dir, '.env'), '');
const wrapper = path.join(dir, 'parent.cjs');
fs.writeFileSync(wrapper, `
const cp = require('node:child_process');
const original = cp.fork;
cp.fork = (...args) => {
const child = original(...args);
process.send?.({ childPid: child.pid });
return child;
};
const { runStartupProcess } = require(${JSON.stringify(startup)});
runStartupProcess(process.argv[2]).then(() => {
const modules = Object.keys(require.cache);
process.send?.({ done: true, modules });
}).catch(error => {
console.error(error.message);
process.exitCode = 1;
}).finally(() => { if (process.connected) process.disconnect(); });
`);
return {
dir,
storage: path.join(dir, 'data/db/database.sqlite'),
run(entrypoint = initializer) {
const child = fork(wrapper, [entrypoint], {
env: { ...process.env, QL_DIR: dir, QL_DATA_DIR: path.join(dir, 'data') },
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
});
const messages = [];
let output = '';
child.on('message', (message) => messages.push(message));
child.stdout.on('data', (data) => { output += data; });
child.stderr.on('data', (data) => { output += data; });
t.after(() => {
if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL');
});
const done = once(child, 'close').then(([code, signal]) => ({ code, signal, output, messages }));
return { child, done, messages };
},
};
}
async function waitFor(predicate) {
const deadline = Date.now() + 10000;
while (!predicate()) {
assert.ok(Date.now() < deadline, 'timed out waiting for child');
await new Promise(resolve => setTimeout(resolve, 20));
}
}
test('isolated initialization creates a fresh database and does not retain ORM modules in the parent', { timeout: 20000 }, async t => {
const f = fixture(t);
const result = await f.run().done;
assert.equal(result.code, 0, result.output);
const done = result.messages.find(message => message.done);
assert.ok(done);
assert.equal(done.modules.some(file => /[\\/]sequelize[\\/]|[\\/]data[\\/]index\.ts$/.test(file)), false);
const db = new Sequelize({ dialect: 'sqlite', storage: f.storage, logging: false });
try {
const tables = await db.getQueryInterface().showAllTables();
for (const table of ['Crontabs', 'Envs', 'RunningInstances', 'SchemaMigrations']) assert.ok(tables.includes(table), table);
assert.equal((await db.query('SELECT id FROM SchemaMigrations', { type: QueryTypes.SELECT })).length, 15);
} finally { await db.close(); }
});
test('isolated initialization upgrades an existing database and remains idempotent', { timeout: 30000 }, async t => {
const f = fixture(t);
let db = new Sequelize({ dialect: 'sqlite', storage: f.storage, logging: false });
await db.query('CREATE TABLE Crontabs (id INTEGER PRIMARY KEY, name TEXT)');
await db.query("INSERT INTO Crontabs (id, name) VALUES (1, 'keep-me')");
await db.close();
for (let i = 0; i < 2; i++) {
const result = await f.run().done;
assert.equal(result.code, 0, result.output);
}
db = new Sequelize({ dialect: 'sqlite', storage: f.storage, logging: false });
try {
const rows = await db.query('SELECT * FROM Crontabs', { type: QueryTypes.SELECT });
assert.equal(rows[0].name, 'keep-me');
assert.ok(Object.hasOwn(rows[0], 'queued_token'));
assert.equal((await db.query('SELECT id FROM SchemaMigrations', { type: QueryTypes.SELECT })).length, 15);
} finally { await db.close(); }
});
test('database errors and missing initializer files reject startup', { timeout: 20000 }, async t => {
const f = fixture(t);
fs.writeFileSync(f.storage, 'invalid SQLite database');
const invalid = await f.run().done;
assert.equal(invalid.code, 1);
assert.match(invalid.output, /SQLITE_NOTADB/);
assert.equal(invalid.messages.some(message => message.done), false);
const missing = await f.run(path.join(f.dir, 'missing.cjs')).done;
assert.equal(missing.code, 1);
assert.match(missing.output, /MODULE_NOT_FOUND/);
});
for (const signal of ['SIGTERM', 'SIGINT']) {
test(`startup ${signal} terminates the initializer and never reports success`, { timeout: 15000 }, async t => {
const f = fixture(t);
const target = path.join(f.dir, 'waiting.cjs');
const ready = path.join(f.dir, 'ready');
fs.writeFileSync(target, `
require('node:fs').writeFileSync(${JSON.stringify(ready)}, String(process.pid));
setInterval(() => {}, 1000);
`);
const running = f.run(target);
await waitFor(() => fs.existsSync(ready));
const pid = Number(fs.readFileSync(ready, 'utf8'));
running.child.kill(signal);
const result = await running.done;
assert.equal(result.code, 1, result.output);
assert.match(result.output, new RegExp(signal));
assert.equal(result.messages.some(message => message.done), false);
assert.throws(() => process.kill(pid, 0), { code: 'ESRCH' });
});
}
test('a database initializer blocked on SQLite exits when its parent disappears', { timeout: 20000 }, async t => {
const f = fixture(t);
const db = new Sequelize({ dialect: 'sqlite', storage: f.storage, logging: false });
await db.query('BEGIN EXCLUSIVE');
try {
const running = f.run();
await waitFor(() => running.messages.some(message => message.childPid));
const pid = running.messages.find(message => message.childPid).childPid;
// Allow the real initializer to enter the database operation before losing IPC.
await new Promise(resolve => setTimeout(resolve, 1000));
running.child.kill('SIGKILL');
await running.done;
await waitFor(() => {
try { process.kill(pid, 0); return false; }
catch (error) { if (error.code === 'ESRCH') return true; throw error; }
});
} finally {
await db.query('ROLLBACK');
await db.close();
}
});
test('application waits for isolated initialization and does not fork workers after a failure', async () => {
const file = path.resolve('back/app.ts');
const source = fs.readFileSync(file, 'utf8');
const compiled = ts.transpileModule(source.slice(0, source.indexOf('\nconst app = new Application();')) + '\nmodule.exports = Application;', {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, esModuleInterop: true },
}).outputText;
for (const fail of [false, true]) {
let complete;
const pending = new Promise((resolve, reject) => { complete = () => fail ? reject(new Error('migration failed')) : resolve(); });
const exits = [], calls = [];
const localRequire = createRequire(file);
const mocks = {
cluster: { isPrimary: true },
'./config': {},
'./loaders/logger': { error() {} },
'./shared/startupProcess': { runStartupProcess: entry => { assert.equal(entry, initializer); return pending; } },
};
const req = Object.assign(name => {
calls.push(name);
return Object.hasOwn(mocks, name) ? mocks[name] : localRequire(name);
}, { resolve: localRequire.resolve });
const module = { exports: {} };
new Function('require', 'module', 'exports', 'process', compiled)(req, module, module.exports, { exit: code => exits.push(code) });
const app = new module.exports();
let started = false;
app.startMasterProcess = () => { started = true; };
const start = app.start();
await new Promise(resolve => setImmediate(resolve));
assert.equal(started, false);
complete();
await start;
assert.equal(started, !fail);
assert.deepEqual(exits, fail ? [1] : []);
assert.equal(calls.includes('./loaders/db'), false);
}
});
+118
View File
@@ -0,0 +1,118 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const { createRequire } = require('node:module');
const path = require('node:path');
const ts = require('typescript');
const source = fs.readFileSync('back/app.ts', 'utf8');
const compiled = ts.transpileModule(
source.slice(0, source.indexOf('\nconst app = new Application();')) +
'\nmodule.exports = Application;',
{
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2020,
esModuleInterop: true,
},
},
).outputText;
function fixture() {
const required = [],
listeners = new Map();
let invalidations = 0;
const module = { exports: {} };
const localRequire = createRequire(path.resolve('back/app.ts'));
const mocks = {
'./config': { cors: { origin: ['http://localhost'] } },
'./loaders/logger': { error() {}, warn() {}, info() {} },
'./middlewares/monitoring': {
monitoringMiddleware: (_req, _res, next) => next(),
},
'./schedule/client': {
default: { readiness: { invalidate: () => invalidations++ } },
__esModule: true,
},
};
new Function('require', 'module', 'exports', 'process', compiled)(
(name) => {
required.push(name);
return Object.hasOwn(mocks, name) ? mocks[name] : localRequire(name);
},
module,
module.exports,
{ env: {}, on: (name, fn) => listeners.set(name, fn) },
);
return {
app: new module.exports(),
required,
listeners,
invalidations: () => invalidations,
};
}
test('primary construction does not load HTTP middleware, metrics or the gRPC client', () => {
const { required } = fixture();
for (const name of [
'express',
'helmet',
'cors',
'compression',
'./middlewares/monitoring',
'./schedule/client',
'./config/util',
]) {
assert.equal(required.includes(name), false, name);
}
});
test('lazy HTTP initialization preserves query filtering, security headers, CORS and compression', async (t) => {
const { app, required } = fixture();
const http = await app.setupMiddlewares();
http.get('/test', (req, res) =>
res.json({ query: req.query, data: 'x'.repeat(2048) }),
);
const server = await new Promise((resolve) => {
const server = http.listen(0, '127.0.0.1', () => resolve(server));
});
t.after(
() =>
new Promise((resolve) => {
server.close(resolve);
server.closeAllConnections();
}),
);
const response = await fetch(
`http://127.0.0.1:${server.address().port}/test?t=123&keep=yes`,
{
headers: { Origin: 'http://localhost', 'Accept-Encoding': 'gzip' },
},
);
assert.equal(response.status, 200);
assert.equal(response.headers.get('x-content-type-options'), 'nosniff');
assert.equal(
response.headers.get('access-control-allow-origin'),
'http://localhost',
);
assert.equal(response.headers.get('content-encoding'), 'gzip');
assert.deepEqual((await response.json()).query, { keep: 'yes' });
assert.ok(required.includes('express'));
assert.equal(required.includes('./schedule/client'), false);
});
test('worker recovery messages still invalidate HTTP readiness, but gRPC shutdown setup does not load its client', async () => {
const grpc = fixture();
grpc.app.setupWorkerShutdown('grpc');
await grpc.listeners.get('message')('scheduler-unavailable');
assert.equal(grpc.required.includes('./schedule/client'), false);
const http = fixture();
http.app.setupWorkerShutdown('http');
await http.listeners.get('message')('scheduler-unavailable');
await http.listeners.get('message')('reregister-crons');
assert.equal(http.invalidations(), 2);
const shutdowns = [];
http.app.gracefulShutdown = (role) => shutdowns.push(role);
await http.listeners.get('message')('shutdown');
http.listeners.get('SIGTERM')();
assert.deepEqual(shutdowns, ['http', 'http']);
});
+297
View File
@@ -0,0 +1,297 @@
const assert = require('node:assert/strict');
const test = require('node:test');
const fs = require('node:fs');
const path = require('node:path');
const { EventEmitter } = require('node:events');
const ts = require('typescript');
const jwt = require('jsonwebtoken');
const { isValidToken } = require('../../back/shared/auth');
async function setup() {
const secret = 'polling-test';
const token = jwt.sign({}, secret, { algorithm: 'HS384', expiresIn: '1h' });
let auth = { token };
let read = async () => auth;
let reads = 0;
const validations = [];
let onConnection;
const clients = new Set();
const timers = new Set();
const mocks = {
sockjs: {
createServer: () => ({
on: (_, fn) => {
onConnection = fn;
},
installHandlers() {},
}),
},
typedi: {
Container: {
get: () => ({
addClient: (c) => clients.add(c),
removeClient: (c) => clients.delete(c),
}),
},
},
'../services/sock': class {},
'../config/util': { getPlatform: (agent) => agent || 'desktop' },
'../shared/store': {
shareStore: {
getAuthInfo: () => {
reads++;
return read();
},
},
},
'../shared/auth': {
isValidToken: (...args) => {
validations.push({ token: args[1], platform: args[2] });
return isValidToken(...args);
},
},
'../config': { baseUrl: '', jwt: { secret } },
};
const source = fs.readFileSync(
path.join(__dirname, '../../back/loaders/sock.ts'),
'utf8',
);
const { outputText } = ts.transpileModule(source, {
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2020,
esModuleInterop: true,
},
});
const module = { exports: {} };
new Function(
'require',
'module',
'exports',
'setInterval',
'clearInterval',
outputText,
)(
(name) => {
assert.ok(Object.hasOwn(mocks, name), name);
return mocks[name];
},
module,
module.exports,
(callback, delay) => {
assert.equal(delay, 1000);
const timer = { callback, unref() {} };
timers.add(timer);
return timer;
},
(timer) => timers.delete(timer),
);
await module.exports.default({ server: {} });
function connection(value = token) {
const conn = new EventEmitter();
Object.assign(conn, {
headers: {},
pathname: '/api/ws/a/b/websocket',
url: `/api/ws/a/b/websocket?token=${value}`,
write() {},
close(code) {
this.closeCode = code;
this.emit('close');
},
});
return conn;
}
return {
token,
secret,
clients,
timers,
validations,
connection,
connect: (c) => onConnection(c),
get reads() {
return reads;
},
setAuth(value) {
auth = value;
},
setRead(fn) {
read = fn;
},
tick: () => Promise.all([...timers].map((t) => t.callback())),
};
}
function deferred() {
let resolve, reject;
const promise = new Promise((a, b) => {
resolve = a;
reject = b;
});
return { promise, resolve, reject };
}
test('multiple sockets share one periodic read, revoke together, and stop polling when empty', async () => {
const h = await setup();
assert.equal(h.timers.size, 0);
const sockets = Array.from({ length: 20 }, () => h.connection());
await Promise.all(sockets.map(h.connect));
assert.equal(h.reads, 20, 'initial authentication remains per connection');
assert.equal(h.timers.size, 1);
await h.tick();
assert.equal(h.reads, 21);
assert.equal(h.clients.size, 20);
h.setAuth({ token: '' });
await h.tick();
assert.ok(sockets.every((c) => c.closeCode === '401'));
assert.equal(h.clients.size, 0);
assert.equal(h.timers.size, 0);
const c = h.connection();
h.setAuth({ token: h.token });
await h.connect(c);
assert.equal(h.timers.size, 1);
c.close();
assert.equal(h.timers.size, 0);
});
test('slow reads do not overlap and old snapshots do not reject newly accepted sockets', async () => {
const h = await setup();
const old = h.connection();
await h.connect(old);
const pending = deferred();
h.setRead(() => pending.promise);
const tick = h.tick();
await h.tick();
assert.equal(h.reads, 2);
old.close();
h.setRead(async () => ({ token: h.token }));
const fresh = h.connection();
await h.connect(fresh);
pending.resolve({ token: '' });
await tick;
assert.equal(h.clients.has(fresh), true);
assert.equal(fresh.closeCode, undefined);
await h.tick();
assert.equal(h.reads, 4);
fresh.close();
});
test('periodic store failures disconnect the checked sessions', async () => {
const h = await setup();
const c = h.connection();
await h.connect(c);
h.setRead(async () => {
throw new Error('database unavailable');
});
await h.tick();
assert.equal(c.closeCode, '401');
assert.equal(h.clients.size, 0);
assert.equal(h.timers.size, 0);
});
test('closed connections cannot be added after initial authentication finishes', async () => {
const h = await setup();
const pending = deferred();
h.setRead(() => pending.promise);
const c = h.connection();
const connecting = h.connect(c);
c.close();
pending.resolve({ token: h.token });
await connecting;
assert.equal(h.clients.size, 0);
assert.equal(h.timers.size, 0);
});
test('initial store errors reject the connection without creating a timer', async () => {
const h = await setup();
h.setRead(async () => {
throw new Error('database unavailable');
});
const c = h.connection();
await h.connect(c);
assert.equal(c.closeCode, '401');
assert.equal(h.clients.size, 0);
assert.equal(h.timers.size, 0);
});
test('each periodic check still validates JWT expiry and platform session membership', async () => {
const h = await setup();
const expired = h.connection(
jwt.sign({}, h.secret, { algorithm: 'HS384', expiresIn: -1 }),
);
await h.connect(expired);
assert.equal(expired.closeCode, '404');
const c = h.connection();
await h.connect(c);
h.setAuth({ tokens: { desktop: [{ value: h.token, expiration: 1 }] } });
await h.tick();
assert.equal(c.closeCode, '401');
assert.equal(h.timers.size, 0);
});
test('an accepted JWT is disconnected when it expires without a store change', async () => {
const h = await setup();
const c = h.connection();
await h.connect(c);
const now = Date.now;
try {
Date.now = () => now() + 2 * 60 * 60 * 1000;
await h.tick();
} finally {
Date.now = now;
}
assert.equal(c.closeCode, '401');
assert.equal(h.timers.size, 0);
});
test('same token and platform share validation only within the current round', async () => {
const h = await setup();
const sockets = Array.from({ length: 20 }, () => h.connection());
await Promise.all(sockets.map(h.connect));
assert.equal(
h.validations.length,
20,
'initial authentication stays independent',
);
h.validations.length = 0;
await h.tick();
assert.equal(h.validations.length, 1);
await h.tick();
assert.equal(h.validations.length, 2, 'new rounds revalidate');
h.setAuth({ token: '' });
await h.tick();
assert.equal(
h.validations.length,
3,
'invalid results are also shared for this round',
);
assert.ok(sockets.every((c) => c.closeCode === '401'));
assert.equal(h.timers.size, 0);
});
test('validation results never cross tokens or platforms', async () => {
const h = await setup();
const other = jwt.sign({ account: 'other' }, h.secret, {
algorithm: 'HS384',
expiresIn: '1h',
});
h.setAuth({
tokens: {
desktop: [{ value: h.token }, { value: other }],
mobile: [{ value: h.token }],
},
});
const desktop = h.connection();
const mobile = h.connection();
mobile.headers['user-agent'] = 'mobile';
const separate = h.connection(other);
await Promise.all([desktop, mobile, separate].map(h.connect));
h.validations.length = 0;
h.setAuth({ tokens: { desktop: [{ value: h.token }], mobile: [] } });
await h.tick();
assert.equal(h.validations.length, 3);
assert.equal(desktop.closeCode, undefined);
assert.equal(mobile.closeCode, '401');
assert.equal(separate.closeCode, '401');
desktop.close();
});