feat(PortProxy): Enhance PortProxy with advanced connection cleanup and logging
This commit is contained in:
		| @@ -3,6 +3,6 @@ | ||||
|  */ | ||||
| export const commitinfo = { | ||||
|   name: '@push.rocks/smartproxy', | ||||
|   version: '3.19.0', | ||||
|   version: '3.20.0', | ||||
|   description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, and dynamic routing with authentication options.' | ||||
| } | ||||
|   | ||||
| @@ -92,6 +92,7 @@ interface IConnectionRecord { | ||||
|   outgoing: plugins.net.Socket | null; | ||||
|   incomingStartTime: number; | ||||
|   outgoingStartTime?: number; | ||||
|   outgoingClosedTime?: number; | ||||
|   lockedDomain?: string; // New field to lock this connection to the initial SNI | ||||
|   connectionClosed: boolean; | ||||
|   cleanupTimer?: NodeJS.Timeout; // Timer to force cleanup after max lifetime/inactivity | ||||
| @@ -157,6 +158,33 @@ export class PortProxy { | ||||
|     this.terminationStats[side][reason] = (this.terminationStats[side][reason] || 0) + 1; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Cleans up a connection record if not already cleaned up. | ||||
|    * Destroys both incoming and outgoing sockets, clears timers, and removes the record. | ||||
|    * Logs the cleanup event. | ||||
|    */ | ||||
|   private cleanupConnection(record: IConnectionRecord, special: boolean = false): void { | ||||
|     if (!record.connectionClosed) { | ||||
|       record.connectionClosed = true; | ||||
|       if (record.cleanupTimer) { | ||||
|         clearTimeout(record.cleanupTimer); | ||||
|       } | ||||
|       if (!record.incoming.destroyed) { | ||||
|         record.incoming.destroy(); | ||||
|       } | ||||
|       if (record.outgoing && !record.outgoing.destroyed) { | ||||
|         record.outgoing.destroy(); | ||||
|       } | ||||
|       this.connectionRecords.delete(record); | ||||
|       const remoteIP = record.incoming.remoteAddress || 'unknown'; | ||||
|       if (special) { | ||||
|         console.log(`Special parity cleanup: Connection from ${remoteIP} cleaned up due to duration difference.`); | ||||
|       } else { | ||||
|         console.log(`Connection from ${remoteIP} terminated. Active connections: ${this.connectionRecords.size}`); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private getTargetIP(domainConfig: IDomainConfig): string { | ||||
|     if (domainConfig.targetIPs && domainConfig.targetIPs.length > 0) { | ||||
|       const currentIndex = this.domainTargetIndices.get(domainConfig) || 0; | ||||
| @@ -185,18 +213,9 @@ export class PortProxy { | ||||
|       let incomingTerminationReason: string | null = null; | ||||
|       let outgoingTerminationReason: string | null = null; | ||||
|  | ||||
|       // Ensure cleanup happens only once for the entire connection record. | ||||
|       const cleanupOnce = async () => { | ||||
|         if (!connectionRecord.connectionClosed) { | ||||
|           connectionRecord.connectionClosed = true; | ||||
|           if (connectionRecord.cleanupTimer) { | ||||
|             clearTimeout(connectionRecord.cleanupTimer); | ||||
|           } | ||||
|           if (!socket.destroyed) socket.destroy(); | ||||
|           if (connectionRecord.outgoing && !connectionRecord.outgoing.destroyed) connectionRecord.outgoing.destroy(); | ||||
|           this.connectionRecords.delete(connectionRecord); | ||||
|           console.log(`Connection from ${remoteIP} terminated. Active connections: ${this.connectionRecords.size}`); | ||||
|         } | ||||
|       // Local cleanup function that delegates to the class method. | ||||
|       const cleanupOnce = () => { | ||||
|         this.cleanupConnection(connectionRecord); | ||||
|       }; | ||||
|  | ||||
|       // Helper to reject an incoming connection. | ||||
| @@ -244,6 +263,8 @@ export class PortProxy { | ||||
|         } else if (side === 'outgoing' && outgoingTerminationReason === null) { | ||||
|           outgoingTerminationReason = 'normal'; | ||||
|           this.incrementTerminationStat('outgoing', 'normal'); | ||||
|           // Record the time when outgoing socket closed. | ||||
|           connectionRecord.outgoingClosedTime = Date.now(); | ||||
|         } | ||||
|         cleanupOnce(); | ||||
|       }; | ||||
| @@ -473,7 +494,7 @@ export class PortProxy { | ||||
|       this.netServers.push(server); | ||||
|     } | ||||
|  | ||||
|     // Log active connection count and longest running durations every 10 seconds. | ||||
|     // Log active connection count, longest running durations, and run parity checks every 10 seconds. | ||||
|     this.connectionLogger = setInterval(() => { | ||||
|       const now = Date.now(); | ||||
|       let maxIncoming = 0; | ||||
| @@ -483,6 +504,12 @@ export class PortProxy { | ||||
|         if (record.outgoingStartTime) { | ||||
|           maxOutgoing = Math.max(maxOutgoing, now - record.outgoingStartTime); | ||||
|         } | ||||
|         // Parity check: if outgoing socket closed and incoming remains active for >1 minute, trigger special cleanup. | ||||
|         if (record.outgoingClosedTime && !record.incoming.destroyed && (now - record.outgoingClosedTime > 60000)) { | ||||
|           const remoteIP = record.incoming.remoteAddress || 'unknown'; | ||||
|           console.log(`Parity check triggered: Incoming socket for ${remoteIP} has been active >1 minute after outgoing closed.`); | ||||
|           this.cleanupConnection(record, true); | ||||
|         } | ||||
|       } | ||||
|       console.log( | ||||
|         `(Interval Log) Active connections: ${this.connectionRecords.size}. ` + | ||||
|   | ||||
		Reference in New Issue
	
	Block a user