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
## 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)
Refactor and optimize PortProxy for improved readability and maintainability

View File

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

View File

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