fix(PortProxy): Refactor connection tracking to utilize unified records in PortProxy

This commit is contained in:
Philipp Kunz 2025-02-23 17:38:22 +00:00
parent 3de35f3b2c
commit a53e6f1019
3 changed files with 43 additions and 33 deletions

View File

@ -1,5 +1,12 @@
# Changelog # Changelog
## 2025-02-23 - 3.10.4 - fix(PortProxy)
Refactor connection tracking to utilize unified records in PortProxy
- Implemented a unified record system for tracking incoming and outgoing connections.
- Replaced individual connection tracking sets with a Set of IConnectionRecord.
- Improved logging of connection activities and statistics.
## 2025-02-23 - 3.10.3 - fix(PortProxy) ## 2025-02-23 - 3.10.3 - fix(PortProxy)
Refactor and optimize PortProxy for improved readability and maintainability Refactor and optimize PortProxy for improved readability and maintainability

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartproxy', name: '@push.rocks/smartproxy',
version: '3.10.3', version: '3.10.4',
description: 'a proxy for handling high workloads of proxying' description: 'a proxy for handling high workloads of proxying'
} }

View File

@ -79,12 +79,19 @@ function extractSNI(buffer: Buffer): string | undefined {
return undefined; return undefined;
} }
interface IConnectionRecord {
incoming: plugins.net.Socket;
outgoing: plugins.net.Socket | null;
incomingStartTime: number;
outgoingStartTime?: number;
connectionClosed: boolean;
}
export class PortProxy { export class PortProxy {
netServer: plugins.net.Server; netServer: plugins.net.Server;
settings: IProxySettings; settings: IProxySettings;
private activeConnections: Set<plugins.net.Socket> = new Set(); // Unified record tracking each connection pair.
private incomingConnectionTimes: Map<plugins.net.Socket, number> = new Map(); private connectionRecords: Set<IConnectionRecord> = new Set();
private outgoingConnectionTimes: Map<plugins.net.Socket, number> = new Map();
private connectionLogger: NodeJS.Timeout | null = null; private connectionLogger: NodeJS.Timeout | null = null;
private terminationStats: { private terminationStats: {
@ -140,29 +147,26 @@ export class PortProxy {
this.netServer = plugins.net.createServer((socket: plugins.net.Socket) => { this.netServer = plugins.net.createServer((socket: plugins.net.Socket) => {
const remoteIP = socket.remoteAddress || ''; const remoteIP = socket.remoteAddress || '';
this.activeConnections.add(socket); const connectionRecord: IConnectionRecord = {
this.incomingConnectionTimes.set(socket, Date.now()); incoming: socket,
console.log(`New connection from ${remoteIP}. Active connections: ${this.activeConnections.size}`); outgoing: null,
incomingStartTime: Date.now(),
connectionClosed: false,
};
this.connectionRecords.add(connectionRecord);
console.log(`New connection from ${remoteIP}. Active connections: ${this.connectionRecords.size}`);
let initialDataReceived = false; let initialDataReceived = false;
let incomingTerminationReason: string | null = null; let incomingTerminationReason: string | null = null;
let outgoingTerminationReason: string | null = null; let outgoingTerminationReason: string | null = null;
let targetSocket: plugins.net.Socket | null = null;
let connectionClosed = false;
// Ensure cleanup happens only once. // Ensure cleanup happens only once for the entire connection record.
const cleanupOnce = () => { const cleanupOnce = () => {
if (!connectionClosed) { if (!connectionRecord.connectionClosed) {
connectionClosed = true; connectionRecord.connectionClosed = true;
cleanUpSockets(socket, targetSocket || undefined); cleanUpSockets(connectionRecord.incoming, connectionRecord.outgoing || undefined);
this.incomingConnectionTimes.delete(socket); this.connectionRecords.delete(connectionRecord);
if (targetSocket) { console.log(`Connection from ${remoteIP} terminated. Active connections: ${this.connectionRecords.size}`);
this.outgoingConnectionTimes.delete(targetSocket);
}
if (this.activeConnections.has(socket)) {
this.activeConnections.delete(socket);
console.log(`Connection from ${remoteIP} terminated. Active connections: ${this.activeConnections.size}`);
}
} }
}; };
@ -242,10 +246,10 @@ export class PortProxy {
connectionOptions.localAddress = remoteIP.replace('::ffff:', ''); connectionOptions.localAddress = remoteIP.replace('::ffff:', '');
} }
targetSocket = plugins.net.connect(connectionOptions); const targetSocket = plugins.net.connect(connectionOptions);
if (targetSocket) { connectionRecord.outgoing = targetSocket;
this.outgoingConnectionTimes.set(targetSocket, Date.now()); connectionRecord.outgoingStartTime = Date.now();
}
console.log( console.log(
`Connection established: ${remoteIP} -> ${targetHost}:${this.settings.toPort}` + `Connection established: ${remoteIP} -> ${targetHost}:${this.settings.toPort}` +
`${serverName ? ` (SNI: ${serverName})` : ''}` `${serverName ? ` (SNI: ${serverName})` : ''}`
@ -314,20 +318,19 @@ export class PortProxy {
); );
}); });
// Log active connection count, longest running connection durations, // Every 10 seconds log active connection count and longest running durations.
// and termination statistics every 10 seconds.
this.connectionLogger = setInterval(() => { this.connectionLogger = setInterval(() => {
const now = Date.now(); const now = Date.now();
let maxIncoming = 0; let maxIncoming = 0;
for (const startTime of this.incomingConnectionTimes.values()) {
maxIncoming = Math.max(maxIncoming, now - startTime);
}
let maxOutgoing = 0; let maxOutgoing = 0;
for (const startTime of this.outgoingConnectionTimes.values()) { for (const record of this.connectionRecords) {
maxOutgoing = Math.max(maxOutgoing, now - startTime); maxIncoming = Math.max(maxIncoming, now - record.incomingStartTime);
if (record.outgoingStartTime) {
maxOutgoing = Math.max(maxOutgoing, now - record.outgoingStartTime);
}
} }
console.log( console.log(
`(Interval Log) Active connections: ${this.activeConnections.size}. ` + `(Interval Log) Active connections: ${this.connectionRecords.size}. ` +
`Longest running incoming: ${plugins.prettyMs(maxIncoming)}, outgoing: ${plugins.prettyMs(maxOutgoing)}. ` + `Longest running incoming: ${plugins.prettyMs(maxIncoming)}, outgoing: ${plugins.prettyMs(maxOutgoing)}. ` +
`Termination stats (incoming): ${JSON.stringify(this.terminationStats.incoming)}, ` + `Termination stats (incoming): ${JSON.stringify(this.terminationStats.incoming)}, ` +
`(outgoing): ${JSON.stringify(this.terminationStats.outgoing)}` `(outgoing): ${JSON.stringify(this.terminationStats.outgoing)}`