Refactor socket handling in forwarding handlers to use centralized utilities and remove deprecated functions

This commit is contained in:
Philipp Kunz 2025-06-01 15:35:45 +00:00
parent fb147148ef
commit 47508eb1eb
4 changed files with 124 additions and 113 deletions

View File

@ -641,3 +641,24 @@ export function setupBidirectionalForwarding(
### Migration Notes ### Migration Notes
No configuration changes needed. The fix applies to all proxy chains automatically. No configuration changes needed. The fix applies to all proxy chains automatically.
## Socket Cleanup Handler Deprecation (v19.5.15+)
### Issue
The deprecated `createSocketCleanupHandler()` function was still being used in forwarding handlers, despite being marked as deprecated.
### Solution
Updated all forwarding handlers to use the new centralized socket utilities:
1. **Replaced `createSocketCleanupHandler()`** with `setupBidirectionalForwarding()` in:
- `https-terminate-to-https-handler.ts`
- `https-terminate-to-http-handler.ts`
2. **Removed deprecated function** from `socket-utils.ts`
### Benefits
- Consistent socket handling across all handlers
- Proper cleanup in proxy chains (no half-open connections by default)
- Better backpressure handling with the centralized implementation
- Reduced code duplication
### Migration Notes
No user-facing changes. All forwarding handlers now use the same robust socket handling as the main SmartProxy connection handler.

View File

@ -67,37 +67,6 @@ export function cleanupSocket(
}); });
} }
/**
* Create a cleanup handler for paired sockets (client and server)
* @param clientSocket The client socket
* @param serverSocket The server socket (optional)
* @param onCleanup Optional callback when cleanup is done
* @returns A cleanup function that can be called multiple times safely
* @deprecated Use createIndependentSocketHandlers for better half-open support
*/
export function createSocketCleanupHandler(
clientSocket: plugins.net.Socket | plugins.tls.TLSSocket,
serverSocket?: plugins.net.Socket | plugins.tls.TLSSocket | null,
onCleanup?: (reason: string) => void
): (reason: string) => void {
let cleanedUp = false;
return (reason: string) => {
if (cleanedUp) return;
cleanedUp = true;
// Cleanup both sockets (old behavior - too aggressive)
cleanupSocket(clientSocket, 'client', { immediate: true });
if (serverSocket) {
cleanupSocket(serverSocket, 'server', { immediate: true });
}
// Call cleanup callback if provided
if (onCleanup) {
onCleanup(reason);
}
};
}
/** /**
* Create independent cleanup handlers for paired sockets that support half-open connections * Create independent cleanup handlers for paired sockets that support half-open connections
@ -278,19 +247,6 @@ export function setupBidirectionalForwarding(
return { cleanupClient, cleanupServer }; return { cleanupClient, cleanupServer };
} }
/**
* Pipe two sockets together with proper cleanup on either end
* @param socket1 First socket
* @param socket2 Second socket
*/
export function pipeSockets(
socket1: plugins.net.Socket | plugins.tls.TLSSocket,
socket2: plugins.net.Socket | plugins.tls.TLSSocket
): void {
socket1.pipe(socket2);
socket2.pipe(socket1);
}
/** /**
* Create a socket with immediate error handling to prevent crashes * Create a socket with immediate error handling to prevent crashes
* @param options Socket creation options * @param options Socket creation options

View File

@ -2,7 +2,7 @@ import * as plugins from '../../plugins.js';
import { ForwardingHandler } from './base-handler.js'; import { ForwardingHandler } from './base-handler.js';
import type { IForwardConfig } from '../config/forwarding-types.js'; import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
import { createSocketCleanupHandler, setupSocketHandlers, createSocketWithErrorHandler } from '../../core/utils/socket-utils.js'; import { setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
/** /**
* Handler for HTTPS termination with HTTP backend * Handler for HTTPS termination with HTTP backend
@ -100,19 +100,30 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
let backendSocket: plugins.net.Socket | null = null; let backendSocket: plugins.net.Socket | null = null;
let dataBuffer = Buffer.alloc(0); let dataBuffer = Buffer.alloc(0);
let connectionEstablished = false; let connectionEstablished = false;
let forwardingSetup = false;
// Create cleanup handler for all sockets // Set up initial error handling for TLS socket
const handleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => { const tlsCleanupHandler = (reason: string) => {
if (!forwardingSetup) {
// If forwarding not set up yet, emit disconnected and cleanup
this.emit(ForwardingHandlerEvents.DISCONNECTED, { this.emit(ForwardingHandlerEvents.DISCONNECTED, {
remoteAddress, remoteAddress,
reason reason
}); });
dataBuffer = Buffer.alloc(0); dataBuffer = Buffer.alloc(0);
connectionEstablished = false; connectionEstablished = false;
});
// Set up error handling with our cleanup utility if (!tlsSocket.destroyed) {
setupSocketHandlers(tlsSocket, handleClose, undefined, 'tls'); tlsSocket.destroy();
}
if (backendSocket && !backendSocket.destroyed) {
backendSocket.destroy();
}
}
// If forwarding is setup, setupBidirectionalForwarding will handle cleanup
};
setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls');
// Set timeout // Set timeout
const timeout = this.getTimeout(); const timeout = this.getTimeout();
@ -123,7 +134,7 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
remoteAddress, remoteAddress,
error: 'TLS connection timeout' error: 'TLS connection timeout'
}); });
handleClose('timeout'); tlsCleanupHandler('timeout');
}); });
// Handle TLS data // Handle TLS data
@ -172,30 +183,33 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
dataBuffer = Buffer.alloc(0); dataBuffer = Buffer.alloc(0);
} }
// Set up bidirectional data flow // Now set up bidirectional forwarding with proper cleanup
tlsSocket.pipe(backendSocket!); forwardingSetup = true;
backendSocket!.pipe(tlsSocket); setupBidirectionalForwarding(tlsSocket, backendSocket!, {
} onCleanup: (reason) => {
});
// Update the cleanup handler with the backend socket
const newHandleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
this.emit(ForwardingHandlerEvents.DISCONNECTED, { this.emit(ForwardingHandlerEvents.DISCONNECTED, {
remoteAddress, remoteAddress,
reason reason
}); });
dataBuffer = Buffer.alloc(0); dataBuffer = Buffer.alloc(0);
connectionEstablished = false; connectionEstablished = false;
forwardingSetup = false;
},
enableHalfOpen: false // Close both when one closes
});
}
}); });
// Set up handlers for backend socket // Additional error logging for backend socket
setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend');
backendSocket.on('error', (error) => { backendSocket.on('error', (error) => {
if (!connectionEstablished) {
// Connection failed during setup
this.emit(ForwardingHandlerEvents.ERROR, { this.emit(ForwardingHandlerEvents.ERROR, {
remoteAddress, remoteAddress,
error: `Target connection error: ${error.message}` error: `Target connection error: ${error.message}`
}); });
}
// If connected, setupBidirectionalForwarding handles cleanup
}); });
} }
}); });

View File

@ -2,7 +2,7 @@ import * as plugins from '../../plugins.js';
import { ForwardingHandler } from './base-handler.js'; import { ForwardingHandler } from './base-handler.js';
import type { IForwardConfig } from '../config/forwarding-types.js'; import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js'; import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
import { createSocketCleanupHandler, setupSocketHandlers, createSocketWithErrorHandler } from '../../core/utils/socket-utils.js'; import { setupSocketHandlers, createSocketWithErrorHandler, setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
/** /**
* Handler for HTTPS termination with HTTPS backend * Handler for HTTPS termination with HTTPS backend
@ -96,17 +96,26 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
// Variable to track backend socket // Variable to track backend socket
let backendSocket: plugins.tls.TLSSocket | null = null; let backendSocket: plugins.tls.TLSSocket | null = null;
let isConnectedToBackend = false;
// Create cleanup handler for both sockets // Set up initial error handling for TLS socket
const handleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => { const tlsCleanupHandler = (reason: string) => {
if (!isConnectedToBackend) {
// If backend not connected yet, just emit disconnected event
this.emit(ForwardingHandlerEvents.DISCONNECTED, { this.emit(ForwardingHandlerEvents.DISCONNECTED, {
remoteAddress, remoteAddress,
reason reason
}); });
});
// Set up error handling with our cleanup utility // Cleanup TLS socket if needed
setupSocketHandlers(tlsSocket, handleClose, undefined, 'tls'); if (!tlsSocket.destroyed) {
tlsSocket.destroy();
}
}
// If connected to backend, setupBidirectionalForwarding will handle cleanup
};
setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls');
// Set timeout // Set timeout
const timeout = this.getTimeout(); const timeout = this.getTimeout();
@ -117,7 +126,7 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
remoteAddress, remoteAddress,
error: 'TLS connection timeout' error: 'TLS connection timeout'
}); });
handleClose('timeout'); tlsCleanupHandler('timeout');
}); });
// Get the target from configuration // Get the target from configuration
@ -131,44 +140,55 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
// In a real implementation, we would configure TLS options // In a real implementation, we would configure TLS options
rejectUnauthorized: false // For testing only, never use in production rejectUnauthorized: false // For testing only, never use in production
}, () => { }, () => {
isConnectedToBackend = true;
this.emit(ForwardingHandlerEvents.DATA_FORWARDED, { this.emit(ForwardingHandlerEvents.DATA_FORWARDED, {
direction: 'outbound', direction: 'outbound',
target: `${target.host}:${target.port}`, target: `${target.host}:${target.port}`,
tls: true tls: true
}); });
// Set up bidirectional data flow // Set up bidirectional forwarding with proper cleanup
tlsSocket.pipe(backendSocket!); setupBidirectionalForwarding(tlsSocket, backendSocket!, {
backendSocket!.pipe(tlsSocket); onCleanup: (reason) => {
});
// Update the cleanup handler with the backend socket
const newHandleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
this.emit(ForwardingHandlerEvents.DISCONNECTED, { this.emit(ForwardingHandlerEvents.DISCONNECTED, {
remoteAddress, remoteAddress,
reason reason
}); });
},
enableHalfOpen: false // Close both when one closes
}); });
// Set up handlers for backend socket // Set timeout for backend socket
setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend'); backendSocket!.setTimeout(timeout);
backendSocket!.on('timeout', () => {
this.emit(ForwardingHandlerEvents.ERROR, {
remoteAddress,
error: 'Backend connection timeout'
});
// Let setupBidirectionalForwarding handle the cleanup
});
});
// Handle backend connection errors
backendSocket.on('error', (error) => { backendSocket.on('error', (error) => {
this.emit(ForwardingHandlerEvents.ERROR, { this.emit(ForwardingHandlerEvents.ERROR, {
remoteAddress, remoteAddress,
error: `Backend connection error: ${error.message}` error: `Backend connection error: ${error.message}`
}); });
});
// Set timeout for backend socket if (!isConnectedToBackend) {
backendSocket.setTimeout(timeout); // Connection failed, clean up TLS socket
if (!tlsSocket.destroyed) {
backendSocket.on('timeout', () => { tlsSocket.destroy();
this.emit(ForwardingHandlerEvents.ERROR, { }
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
remoteAddress, remoteAddress,
error: 'Backend connection timeout' reason: `backend_connection_failed: ${error.message}`
}); });
newHandleClose('backend_timeout'); }
// If connected, let setupBidirectionalForwarding handle cleanup
}); });
}; };