feat(PortProxy): Introduce max connection lifetime feature
This commit is contained in:
parent
be31a9b553
commit
23253a2731
@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-02-26 - 3.14.0 - feat(PortProxy)
|
||||||
|
Introduce max connection lifetime feature
|
||||||
|
|
||||||
|
- Added an optional maxConnectionLifetime setting for PortProxy.
|
||||||
|
- Forces cleanup of long-lived connections based on inactivity or lifetime limit.
|
||||||
|
|
||||||
## 2025-02-25 - 3.13.0 - feat(core)
|
## 2025-02-25 - 3.13.0 - feat(core)
|
||||||
Add support for tagging iptables rules with comments and cleaning them up on process exit
|
Add support for tagging iptables rules with comments and cleaning them up on process exit
|
||||||
|
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/smartproxy',
|
name: '@push.rocks/smartproxy',
|
||||||
version: '3.13.0',
|
version: '3.14.0',
|
||||||
description: 'A robust and versatile proxy package designed to handle high workloads, offering features like SSL redirection, port proxying, WebSocket support, and customizable routing and authentication.'
|
description: 'A robust and versatile proxy package designed to handle high workloads, offering features like SSL redirection, port proxying, WebSocket support, and customizable routing and authentication.'
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ export interface IPortProxySettings extends plugins.tls.TlsOptions {
|
|||||||
sniEnabled?: boolean;
|
sniEnabled?: boolean;
|
||||||
defaultAllowedIPs?: string[];
|
defaultAllowedIPs?: string[];
|
||||||
preserveSourceIP?: boolean;
|
preserveSourceIP?: boolean;
|
||||||
|
maxConnectionLifetime?: number; // New option (in milliseconds) to force cleanup of long-lived connections
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,6 +86,7 @@ interface IConnectionRecord {
|
|||||||
incomingStartTime: number;
|
incomingStartTime: number;
|
||||||
outgoingStartTime?: number;
|
outgoingStartTime?: number;
|
||||||
connectionClosed: boolean;
|
connectionClosed: boolean;
|
||||||
|
cleanupTimer?: NodeJS.Timeout; // Timer to force cleanup after max lifetime/inactivity
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PortProxy {
|
export class PortProxy {
|
||||||
@ -102,10 +104,11 @@ export class PortProxy {
|
|||||||
outgoing: {},
|
outgoing: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(settings: IPortProxySettings) {
|
constructor(settingsArg: IPortProxySettings) {
|
||||||
this.settings = {
|
this.settings = {
|
||||||
...settings,
|
...settingsArg,
|
||||||
toHost: settings.toHost || 'localhost',
|
toHost: settingsArg.toHost || 'localhost',
|
||||||
|
maxConnectionLifetime: settingsArg.maxConnectionLifetime || 10000,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,6 +167,9 @@ export class PortProxy {
|
|||||||
const cleanupOnce = () => {
|
const cleanupOnce = () => {
|
||||||
if (!connectionRecord.connectionClosed) {
|
if (!connectionRecord.connectionClosed) {
|
||||||
connectionRecord.connectionClosed = true;
|
connectionRecord.connectionClosed = true;
|
||||||
|
if (connectionRecord.cleanupTimer) {
|
||||||
|
clearTimeout(connectionRecord.cleanupTimer);
|
||||||
|
}
|
||||||
cleanUpSockets(connectionRecord.incoming, connectionRecord.outgoing || undefined);
|
cleanUpSockets(connectionRecord.incoming, connectionRecord.outgoing || undefined);
|
||||||
this.connectionRecords.delete(connectionRecord);
|
this.connectionRecords.delete(connectionRecord);
|
||||||
console.log(`Connection from ${remoteIP} terminated. Active connections: ${this.connectionRecords.size}`);
|
console.log(`Connection from ${remoteIP} terminated. Active connections: ${this.connectionRecords.size}`);
|
||||||
@ -262,6 +268,7 @@ export class PortProxy {
|
|||||||
socket.pipe(targetSocket);
|
socket.pipe(targetSocket);
|
||||||
targetSocket.pipe(socket);
|
targetSocket.pipe(socket);
|
||||||
|
|
||||||
|
// Attach error and close handlers.
|
||||||
socket.on('error', handleError('incoming'));
|
socket.on('error', handleError('incoming'));
|
||||||
targetSocket.on('error', handleError('outgoing'));
|
targetSocket.on('error', handleError('outgoing'));
|
||||||
socket.on('close', handleClose('incoming'));
|
socket.on('close', handleClose('incoming'));
|
||||||
@ -284,6 +291,40 @@ export class PortProxy {
|
|||||||
});
|
});
|
||||||
socket.on('end', handleClose('incoming'));
|
socket.on('end', handleClose('incoming'));
|
||||||
targetSocket.on('end', handleClose('outgoing'));
|
targetSocket.on('end', handleClose('outgoing'));
|
||||||
|
|
||||||
|
// If maxConnectionLifetime is set, initialize a cleanup timer that will be reset on data flow.
|
||||||
|
if (this.settings.maxConnectionLifetime) {
|
||||||
|
let incomingActive = false;
|
||||||
|
let outgoingActive = false;
|
||||||
|
const resetCleanupTimer = () => {
|
||||||
|
if (this.settings.maxConnectionLifetime) {
|
||||||
|
if (connectionRecord.cleanupTimer) {
|
||||||
|
clearTimeout(connectionRecord.cleanupTimer);
|
||||||
|
}
|
||||||
|
connectionRecord.cleanupTimer = setTimeout(() => {
|
||||||
|
console.log(`Connection from ${remoteIP} exceeded max lifetime with inactivity (${this.settings.maxConnectionLifetime}ms), forcing cleanup.`);
|
||||||
|
cleanupOnce();
|
||||||
|
}, this.settings.maxConnectionLifetime);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start the cleanup timer.
|
||||||
|
resetCleanupTimer();
|
||||||
|
|
||||||
|
// Listen for data events on both sides and reset the timer when both are active.
|
||||||
|
socket.on('data', () => {
|
||||||
|
incomingActive = true;
|
||||||
|
if (incomingActive && outgoingActive) {
|
||||||
|
resetCleanupTimer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
targetSocket.on('data', () => {
|
||||||
|
outgoingActive = true;
|
||||||
|
if (incomingActive && outgoingActive) {
|
||||||
|
resetCleanupTimer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.settings.sniEnabled) {
|
if (this.settings.sniEnabled) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user