Skip to content

Commit 72a201f

Browse files
committed
fix(double agent): clean up backend
1 parent 7f48e6b commit 72a201f

File tree

7 files changed

+67
-16
lines changed

7 files changed

+67
-16
lines changed

double-agent/collect-controller/lib/Server.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -255,12 +255,13 @@ export default class Server {
255255

256256
await this.saveMetaFiles(activeScraper, assignment);
257257
const profilesDir = extractAssignmentProfilesDir(activeScraper, assignment);
258-
await pipeDirToStream(profilesDir, res);
259-
260258
const session = this.collect.getSession(assignment.sessionId);
261-
await this.collect.deleteSession(session);
262-
263-
delete this.activeUsersById[userId];
259+
try {
260+
await pipeDirToStream(profilesDir, res);
261+
} finally {
262+
await this.collect.deleteSession(session);
263+
delete this.activeUsersById[userId];
264+
}
264265
}
265266

266267
private async downloadAll(

double-agent/collect/lib/SessionTracker.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { IAssignmentType } from '@double-agent/collect-controller/interfaces/IAssignment';
22
import * as http from 'http';
33
import * as http2 from 'http2';
4+
import Config from '@double-agent/config';
45
import Session from './Session';
56
import PluginDelegate from './PluginDelegate';
67
import BaseServer from '../servers/BaseServer';
@@ -10,6 +11,7 @@ let sessionIdCounter = 0;
1011
export default class SessionTracker {
1112
private pluginDelegate: PluginDelegate = new PluginDelegate();
1213
private sessions: { [sessionId: string]: Session } = {};
14+
private sessionExpiryById: { [sessionId: string]: NodeJS.Timeout } = {};
1315

1416
public async createSession(
1517
assignmentType: IAssignmentType,
@@ -21,13 +23,18 @@ export default class SessionTracker {
2123
await session.startServers();
2224

2325
this.sessions[sessionId] = session;
26+
this.scheduleSessionExpiry(sessionId);
2427
return session;
2528
}
2629

2730
public getSession(sessionId: string): Session {
2831
return this.sessions[sessionId];
2932
}
3033

34+
public touchSession(sessionId: string): void {
35+
this.scheduleSessionExpiry(sessionId);
36+
}
37+
3138
public getSessionIdFromServerRequest(
3239
server: BaseServer,
3340
req: http.IncomingMessage | http2.Http2ServerRequest,
@@ -48,12 +55,40 @@ export default class SessionTracker {
4855

4956
public async deleteSession(sessionId: string): Promise<void> {
5057
if (!this.sessions[sessionId]) return;
58+
this.clearSessionExpiry(sessionId);
5159
await this.sessions[sessionId].close();
5260
delete this.sessions[sessionId];
5361
}
5462

5563
public async shutdown(): Promise<void> {
5664
await Promise.allSettled(Object.values(this.sessions).map((x) => x.close()));
5765
await Promise.allSettled(this.pluginDelegate.plugins.map((x) => x.stop()));
66+
this.clearAllSessionExpiry();
67+
}
68+
69+
private scheduleSessionExpiry(sessionId: string): void {
70+
const ttlMs = Config.collect.sessionTtlMs;
71+
if (!ttlMs || ttlMs <= 0) return;
72+
this.clearSessionExpiry(sessionId);
73+
const timeout = setTimeout(() => {
74+
console.warn('Session expired due to inactivity/ttl', sessionId);
75+
void this.deleteSession(sessionId);
76+
}, ttlMs);
77+
timeout.unref();
78+
this.sessionExpiryById[sessionId] = timeout;
79+
}
80+
81+
private clearSessionExpiry(sessionId: string): void {
82+
const timeout = this.sessionExpiryById[sessionId];
83+
if (timeout) {
84+
clearTimeout(timeout);
85+
delete this.sessionExpiryById[sessionId];
86+
}
87+
}
88+
89+
private clearAllSessionExpiry(): void {
90+
for (const sessionId of Object.keys(this.sessionExpiryById)) {
91+
this.clearSessionExpiry(sessionId);
92+
}
5893
}
5994
}

double-agent/collect/lib/createHttpRequestHandler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export default function createHttpRequestHandler(
5555
if (!session) {
5656
throw new Error(`Missing session: ${sessionId}`);
5757
}
58+
sessionTracker.touchSession(sessionId);
5859
const { requestDetails } = await extractRequestDetails(server, req, session);
5960
const ctx = new RequestContext(server, req, res, requestUrl, requestDetails, session);
6061
const userAgentId = session.userAgentId;

double-agent/collect/lib/createTlsRequestHandler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default function createTlsRequestHandler(
2424
}
2525

2626
const session = sessionTracker.getSessionFromServerRequest(server, req);
27+
sessionTracker.touchSession(session.id);
2728
const { requestDetails } = await extractRequestDetails(server, req, session);
2829
const ctx = new RequestContext(server, req, res, requestUrl, requestDetails, session);
2930
const handler = server.getHandlerFn(requestUrl.pathname);

double-agent/collect/lib/createWebsocketHandler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export default function createWebsocketHandler(
2020
): Promise<void> {
2121
const { sessionTracker } = detectionContext;
2222
const session = sessionTracker.getSessionFromServerRequest(server, req);
23+
sessionTracker.touchSession(session.id);
2324
const { requestDetails, requestUrl } = await extractRequestDetails(
2425
server,
2526
req,

double-agent/config/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export default class Config {
5555
shouldGenerateProfiles: parseEnvBool(env.GENERATE_PROFILES),
5656
pluginStartingPort: parseEnvInt(env.PLUGIN_STARTING_PORT),
5757
pluginMaxPort: parseEnvInt(env.PLUGIN_MAX_PORT) || 20000,
58+
sessionTtlMs: parseEnvInt(env.COLLECT_SESSION_TTL_MS) ?? 60 * 60 * 1000,
5859

5960
// collect plugins
6061
tcpNetworkDevice: env.TCP_NETWORK_DEVICE,

double-agent/tls-server/child.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ let connectionCount = 0;
1010
let lastConnectionDate: Date;
1111
let activeConnection: { id: number; req: IncomingMessage; res: ServerResponse };
1212
const connections: (typeof activeConnection)[] = [];
13+
let childServer: https.Server;
14+
let isShuttingDown = false;
15+
16+
process.on('disconnect', () => {
17+
shutdown('parent disconnect');
18+
process.exit(0);
19+
});
1320

1421
process.on('message', (message: any) => {
1522
if (message.start) {
@@ -54,7 +61,7 @@ function start(options: { port: number; key?: string; cert?: string }): void {
5461
enableTrace: true,
5562
sessionTimeout: 10,
5663
});
57-
const childServer = https.createServer(options, onConnection);
64+
childServer = https.createServer(options, onConnection);
5865

5966
childServer.on('error', err => {
6067
process.send({ error: err.message });
@@ -65,21 +72,25 @@ function start(options: { port: number; key?: string; cert?: string }): void {
6572
process.send({ started: true });
6673
});
6774

68-
ShutdownHandler.register(() => {
69-
if (childServer) childServer.unref().close();
70-
while (connections.length) {
71-
const connection = connections.pop();
72-
73-
console.log('Force closing active connection during shutdown.');
74-
connection.res.end('Server shutting down');
75-
connection.req.destroy();
76-
}
77-
});
75+
ShutdownHandler.register(() => shutdown('shutdown handler'));
7876
} catch (err) {
7977
console.log(err);
8078
}
8179
}
8280

81+
function shutdown(reason?: string): void {
82+
if (isShuttingDown) return;
83+
isShuttingDown = true;
84+
if (reason) console.log(`TLS child shutdown: ${reason}`);
85+
if (childServer) childServer.unref().close();
86+
while (connections.length) {
87+
const connection = connections.pop();
88+
console.log('Force closing active connection during shutdown.');
89+
connection.res.end('Server shutting down');
90+
connection.req.destroy();
91+
}
92+
}
93+
8394
async function onConnection(req, res): Promise<void> {
8495
lastConnectionDate = new Date();
8596
res.connection.setKeepAlive(false);

0 commit comments

Comments
 (0)