Skip to content

Commit 09bfe29

Browse files
committed
fix(client): handle ws closing during shutdown
1 parent 2b70752 commit 09bfe29

File tree

5 files changed

+47
-22
lines changed

5 files changed

+47
-22
lines changed

client/connections/ConnectionToCore.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export default abstract class ConnectionToCore extends TypedEventEmitter<{
3030
public readonly hostOrError: Promise<string | Error>;
3131
public options: IConnectionToCoreOptions;
3232
public isDisconnecting = false;
33+
public isConnectionTerminated = false;
3334

3435
protected resolvedHost: string;
3536

@@ -287,11 +288,30 @@ export default abstract class ConnectionToCore extends TypedEventEmitter<{
287288
session?.onEvent(meta, listenerId, eventArgs);
288289
}
289290

291+
protected async onConnectionTerminated(): Promise<void> {
292+
if (this.isConnectionTerminated) return;
293+
294+
this.isConnectionTerminated = true;
295+
await this.internalDisconnect();
296+
if (this.connectRequestId) {
297+
this.onResponse(this.connectRequestId, {
298+
data: new DisconnectedFromCoreError(this.resolvedHost),
299+
});
300+
}
301+
if (this.disconnectRequestId) {
302+
this.onResponse(this.disconnectRequestId, {
303+
data: new DisconnectedFromCoreError(this.resolvedHost),
304+
});
305+
}
306+
}
307+
290308
protected onResponse(id: string, message: ICoreResponsePayload): void {
291309
const pending = this.pendingRequestsById.get(id);
292310
if (!pending) return;
293311
this.pendingRequestsById.delete(id);
294312
const isInternalRequest = this.connectRequestId === id || this.disconnectRequestId === id;
313+
if (this.disconnectRequestId === id) this.disconnectRequestId = null;
314+
if (this.connectRequestId === id) this.connectRequestId = null;
295315

296316
if (message.data instanceof Error) {
297317
let responseError = message.data;

client/connections/RemoteConnectionToCore.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default class RemoteConnectionToCore extends ConnectionToCore {
4343
const webSocket = await this.getWebsocket(false);
4444
if (webSocket?.readyState === WebSocket.OPEN) {
4545
try {
46-
webSocket.off('close', this.internalDisconnect);
46+
webSocket.off('close', this.onConnectionTerminated);
4747
webSocket.off('error', this.internalDisconnect);
4848
webSocket.terminate();
4949
} catch (_) {
@@ -61,7 +61,7 @@ export default class RemoteConnectionToCore extends ConnectionToCore {
6161
this.webSocketOrError = connectToWebsocketHost(hostOrError);
6262
try {
6363
const webSocket = await this.getWebsocket();
64-
webSocket.once('close', this.internalDisconnect);
64+
webSocket.once('close', this.onConnectionTerminated);
6565
webSocket.once('error', this.internalDisconnect);
6666
webSocket.on('message', message => {
6767
const payload = TypeSerializer.parse(message.toString(), 'REMOTE CORE');

commons/ShutdownHandler.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ export default class ShutdownHandler {
2121
this.onShutdownFns.push({ fn: onShutdownFn, callsite });
2222
}
2323

24+
public static async shutdown(exitProcess: boolean): Promise<void> {
25+
await this.onSignal('exit');
26+
if (exitProcess) process.exit(0);
27+
}
28+
2429
private static registerSignals(): void {
2530
if (!this.isRegistered) {
2631
this.isRegistered = true;
@@ -39,7 +44,9 @@ export default class ShutdownHandler {
3944
sessionId: null,
4045
});
4146

42-
for (const entry of this.onShutdownFns) {
47+
while (this.onShutdownFns.length) {
48+
const entry = this.onShutdownFns.shift();
49+
4350
log.stats('ShutdownHandler.execute', {
4451
signal,
4552
fn: entry.fn.toString(),

core/index.ts

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { LocationTrigger } from '@secret-agent/interfaces/Location';
33
import Log, { hasBeenLoggedSymbol } from '@secret-agent/commons/Logger';
44
import Resolvable from '@secret-agent/commons/Resolvable';
55
import {
6-
IHumanEmulatorClass,
76
IBrowserEmulatorClass,
87
ICorePluginClass,
8+
IHumanEmulatorClass,
99
} from '@secret-agent/interfaces/ICorePlugin';
1010
import { PluginTypes } from '@secret-agent/interfaces/IPluginTypes';
1111
import DefaultBrowserEmulator from '@secret-agent/default-browser-emulator';
@@ -20,7 +20,6 @@ import CoreProcess from './lib/CoreProcess';
2020
import Session from './lib/Session';
2121
import Tab from './lib/Tab';
2222
import GlobalPool from './lib/GlobalPool';
23-
import Signals = NodeJS.Signals;
2423

2524
const { log } = Log(module);
2625

@@ -94,6 +93,7 @@ export default class Core {
9493
this.isStarting = true;
9594
if (isExplicitlyStarted) this.wasManuallyStarted = true;
9695

96+
this.registerSignals();
9797
const { localProxyPortStart, sessionsDir, maxConcurrentAgentsCount } = options;
9898

9999
if (maxConcurrentAgentsCount !== undefined)
@@ -175,21 +175,18 @@ export default class Core {
175175
});
176176
});
177177
}
178-
}
179178

180-
['exit', 'SIGTERM', 'SIGINT', 'SIGQUIT'].forEach(name => {
181-
process.once(name as Signals, async () => {
182-
await Core.shutdown();
183-
process.exit(0);
184-
});
185-
});
186-
187-
if (process.env.NODE_ENV !== 'test') {
188-
process.on('uncaughtExceptionMonitor', async (error: Error) => {
189-
await Core.logUnhandledError(error, true);
190-
await Core.shutdown();
191-
});
192-
process.on('unhandledRejection', async (error: Error) => {
193-
await Core.logUnhandledError(error, false);
194-
});
179+
private static registerSignals() {
180+
ShutdownHandler.register(() => Core.shutdown());
181+
182+
if (process.env.NODE_ENV !== 'test') {
183+
process.on('uncaughtExceptionMonitor', async (error: Error) => {
184+
await Core.logUnhandledError(error, true);
185+
await Core.shutdown();
186+
});
187+
process.on('unhandledRejection', async (error: Error) => {
188+
await Core.logUnhandledError(error, false);
189+
});
190+
}
191+
}
195192
}

core/start.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import '@secret-agent/commons/SourceMapSupport';
22
import ICoreConfigureOptions from '@secret-agent/interfaces/ICoreConfigureOptions';
33
import Log from '@secret-agent/commons/Logger';
4+
import ShutdownHandler from '@secret-agent/commons/ShutdownHandler';
45
import Core from '.';
56

67
const { log } = Log(module);
@@ -11,7 +12,7 @@ const { log } = Log(module);
1112

1213
Core.onShutdown = () => {
1314
log.stats('Exiting Core Process');
14-
process.exit();
15+
ShutdownHandler.shutdown(true);
1516
};
1617
await Core.start(startOptions, !process.env.SA_TEMPORARY_CORE);
1718
})().catch(error => {

0 commit comments

Comments
 (0)