Fix connection leak in route-connection-handler by using safe socket creation
The previous fix only addressed ForwardingHandler classes but missed the critical setupDirectConnection() method in route-connection-handler.ts where SmartProxy actually handles connections. This caused active connections to rise indefinitely on ECONNREFUSED errors. Changes: - Import createSocketWithErrorHandler in route-connection-handler.ts - Replace net.connect() with createSocketWithErrorHandler() in setupDirectConnection() - Properly clean up connection records when server connection fails - Add connectionFailed flag to prevent setup of failed connections This ensures connection records are cleaned up immediately when backend connections fail, preventing memory leaks.
This commit is contained in:
parent
900942a263
commit
300ab1a077
@ -458,3 +458,10 @@ const socket = createSocketWithErrorHandler({
|
|||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
No configuration changes needed. The fix is transparent to users.
|
No configuration changes needed. The fix is transparent to users.
|
||||||
|
|
||||||
|
### Important Note
|
||||||
|
The fix was applied in two places:
|
||||||
|
1. **ForwardingHandler classes** (`https-passthrough-handler.ts`, etc.) - These are standalone forwarding utilities
|
||||||
|
2. **SmartProxy route-connection-handler** (`route-connection-handler.ts`) - This is where the actual SmartProxy connection handling happens
|
||||||
|
|
||||||
|
The critical fix for SmartProxy was in `setupDirectConnection()` method in route-connection-handler.ts, which now uses `createSocketWithErrorHandler()` to properly handle connection failures and clean up connection records.
|
@ -9,7 +9,7 @@ import { TlsManager } from './tls-manager.js';
|
|||||||
import { HttpProxyBridge } from './http-proxy-bridge.js';
|
import { HttpProxyBridge } from './http-proxy-bridge.js';
|
||||||
import { TimeoutManager } from './timeout-manager.js';
|
import { TimeoutManager } from './timeout-manager.js';
|
||||||
import { RouteManager } from './route-manager.js';
|
import { RouteManager } from './route-manager.js';
|
||||||
import { cleanupSocket, createIndependentSocketHandlers, setupSocketHandlers } from '../../core/utils/socket-utils.js';
|
import { cleanupSocket, createIndependentSocketHandlers, setupSocketHandlers, createSocketWithErrorHandler } from '../../core/utils/socket-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles new connection processing and setup logic with support for route-based configuration
|
* Handles new connection processing and setup logic with support for route-based configuration
|
||||||
@ -1073,13 +1073,52 @@ export class RouteConnectionHandler {
|
|||||||
record.pendingDataSize = initialChunk.length;
|
record.pendingDataSize = initialChunk.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the target socket
|
// Create the target socket with immediate error handling
|
||||||
const targetSocket = plugins.net.connect(connectionOptions);
|
let targetSocket: plugins.net.Socket;
|
||||||
record.outgoing = targetSocket;
|
|
||||||
record.outgoingStartTime = Date.now();
|
|
||||||
|
|
||||||
// Apply socket optimizations
|
// Flag to track if initial connection failed
|
||||||
targetSocket.setNoDelay(this.settings.noDelay);
|
let connectionFailed = false;
|
||||||
|
|
||||||
|
targetSocket = createSocketWithErrorHandler({
|
||||||
|
port: finalTargetPort,
|
||||||
|
host: finalTargetHost,
|
||||||
|
onError: (error) => {
|
||||||
|
// Mark connection as failed
|
||||||
|
connectionFailed = true;
|
||||||
|
|
||||||
|
// Connection failed - clean up immediately
|
||||||
|
logger.log('error',
|
||||||
|
`Connection setup error for ${connectionId} to ${finalTargetHost}:${finalTargetPort}: ${error.message} (${(error as any).code})`,
|
||||||
|
{
|
||||||
|
connectionId,
|
||||||
|
targetHost: finalTargetHost,
|
||||||
|
targetPort: finalTargetPort,
|
||||||
|
errorMessage: error.message,
|
||||||
|
errorCode: (error as any).code,
|
||||||
|
component: 'route-handler'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Resume the incoming socket to prevent it from hanging
|
||||||
|
socket.resume();
|
||||||
|
|
||||||
|
// Clean up the incoming socket
|
||||||
|
if (!socket.destroyed) {
|
||||||
|
socket.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the connection record
|
||||||
|
this.connectionManager.initiateCleanupOnce(record, `connection_failed_${(error as any).code || 'unknown'}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only proceed with setup if connection didn't fail immediately
|
||||||
|
if (!connectionFailed) {
|
||||||
|
record.outgoing = targetSocket;
|
||||||
|
record.outgoingStartTime = Date.now();
|
||||||
|
|
||||||
|
// Apply socket optimizations
|
||||||
|
targetSocket.setNoDelay(this.settings.noDelay);
|
||||||
|
|
||||||
// Apply keep-alive settings if enabled
|
// Apply keep-alive settings if enabled
|
||||||
if (this.settings.keepAlive) {
|
if (this.settings.keepAlive) {
|
||||||
@ -1346,5 +1385,6 @@ export class RouteConnectionHandler {
|
|||||||
record.tlsHandshakeComplete = true;
|
record.tlsHandshakeComplete = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} // End of if (!connectionFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user