fix: force-stop lingering gateway client sockets

This commit is contained in:
Peter Steinberger 2026-03-14 00:33:14 +00:00
parent 4dbab064f0
commit 727fc79ed2
No known key found for this signature in database
2 changed files with 42 additions and 1 deletions

View File

@ -23,6 +23,8 @@ class MockWebSocket {
private closeHandlers: WsEventHandlers["close"][] = [];
private errorHandlers: WsEventHandlers["error"][] = [];
readonly sent: string[] = [];
closeCalls = 0;
terminateCalls = 0;
constructor(_url: string, _options?: unknown) {
wsInstances.push(this);
@ -52,9 +54,14 @@ class MockWebSocket {
}
close(code?: number, reason?: string): void {
this.closeCalls += 1;
this.emitClose(code ?? 1000, reason ?? "");
}
terminate(): void {
this.terminateCalls += 1;
}
send(data: string): void {
this.sent.push(data);
}
@ -297,6 +304,29 @@ describe("GatewayClient close handling", () => {
client.stop();
});
it("force-terminates a lingering socket after stop", async () => {
vi.useFakeTimers();
try {
const client = new GatewayClient({
url: "ws://127.0.0.1:18789",
});
client.start();
const ws = getLatestWs();
client.stop();
expect(ws.closeCalls).toBe(1);
expect(ws.terminateCalls).toBe(0);
await vi.advanceTimersByTimeAsync(250);
expect(ws.terminateCalls).toBe(1);
} finally {
vi.useRealTimers();
}
});
it("does not clear persisted device auth when explicit shared token is provided", () => {
const onClose = vi.fn();
const identity: DeviceIdentity = {

View File

@ -117,6 +117,8 @@ export function describeGatewayCloseCode(code: number): string | undefined {
return GATEWAY_CLOSE_CODE_HINTS[code];
}
const FORCE_STOP_TERMINATE_GRACE_MS = 250;
export class GatewayClient {
private ws: WebSocket | null = null;
private opts: GatewayClientOptions;
@ -273,8 +275,17 @@ export class GatewayClient {
clearInterval(this.tickTimer);
this.tickTimer = null;
}
this.ws?.close();
const ws = this.ws;
this.ws = null;
if (ws) {
ws.close();
const forceTerminateTimer = setTimeout(() => {
try {
ws.terminate();
} catch {}
}, FORCE_STOP_TERMINATE_GRACE_MS);
forceTerminateTimer.unref?.();
}
this.flushPendingErrors(new Error("gateway client stopped"));
}