Refactor socket handling in forwarding handlers to use centralized utilities and remove deprecated functions
This commit is contained in:
parent
fb147148ef
commit
47508eb1eb
@ -640,4 +640,25 @@ export function setupBidirectionalForwarding(
|
|||||||
- **Neutral**: Half-open connections still available when needed (opt-in)
|
- **Neutral**: Half-open connections still available when needed (opt-in)
|
||||||
|
|
||||||
### 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.
|
@ -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
|
||||||
|
@ -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) => {
|
||||||
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
if (!forwardingSetup) {
|
||||||
remoteAddress,
|
// If forwarding not set up yet, emit disconnected and cleanup
|
||||||
reason
|
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
||||||
});
|
remoteAddress,
|
||||||
dataBuffer = Buffer.alloc(0);
|
reason
|
||||||
connectionEstablished = false;
|
});
|
||||||
});
|
dataBuffer = Buffer.alloc(0);
|
||||||
|
connectionEstablished = false;
|
||||||
|
|
||||||
|
if (!tlsSocket.destroyed) {
|
||||||
|
tlsSocket.destroy();
|
||||||
|
}
|
||||||
|
if (backendSocket && !backendSocket.destroyed) {
|
||||||
|
backendSocket.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If forwarding is setup, setupBidirectionalForwarding will handle cleanup
|
||||||
|
};
|
||||||
|
|
||||||
// Set up error handling with our cleanup utility
|
setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls');
|
||||||
setupSocketHandlers(tlsSocket, handleClose, 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) => {
|
||||||
|
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
||||||
|
remoteAddress,
|
||||||
|
reason
|
||||||
|
});
|
||||||
|
dataBuffer = Buffer.alloc(0);
|
||||||
|
connectionEstablished = false;
|
||||||
|
forwardingSetup = false;
|
||||||
|
},
|
||||||
|
enableHalfOpen: false // Close both when one closes
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the cleanup handler with the backend socket
|
// Additional error logging for backend socket
|
||||||
const newHandleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
|
|
||||||
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
|
||||||
remoteAddress,
|
|
||||||
reason
|
|
||||||
});
|
|
||||||
dataBuffer = Buffer.alloc(0);
|
|
||||||
connectionEstablished = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set up handlers for backend socket
|
|
||||||
setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend');
|
|
||||||
|
|
||||||
backendSocket.on('error', (error) => {
|
backendSocket.on('error', (error) => {
|
||||||
this.emit(ForwardingHandlerEvents.ERROR, {
|
if (!connectionEstablished) {
|
||||||
remoteAddress,
|
// Connection failed during setup
|
||||||
error: `Target connection error: ${error.message}`
|
this.emit(ForwardingHandlerEvents.ERROR, {
|
||||||
});
|
remoteAddress,
|
||||||
|
error: `Target connection error: ${error.message}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// If connected, setupBidirectionalForwarding handles cleanup
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -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) => {
|
||||||
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
if (!isConnectedToBackend) {
|
||||||
remoteAddress,
|
// If backend not connected yet, just emit disconnected event
|
||||||
reason
|
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
||||||
});
|
remoteAddress,
|
||||||
});
|
reason
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cleanup TLS socket if needed
|
||||||
|
if (!tlsSocket.destroyed) {
|
||||||
|
tlsSocket.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If connected to backend, setupBidirectionalForwarding will handle cleanup
|
||||||
|
};
|
||||||
|
|
||||||
// Set up error handling with our cleanup utility
|
setupSocketHandlers(tlsSocket, tlsCleanupHandler, undefined, 'tls');
|
||||||
setupSocketHandlers(tlsSocket, handleClose, 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) => {
|
||||||
});
|
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
||||||
|
remoteAddress,
|
||||||
// Update the cleanup handler with the backend socket
|
reason
|
||||||
const newHandleClose = createSocketCleanupHandler(tlsSocket, backendSocket, (reason) => {
|
});
|
||||||
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
|
},
|
||||||
remoteAddress,
|
enableHalfOpen: false // Close both when one closes
|
||||||
reason
|
});
|
||||||
|
|
||||||
|
// Set timeout for backend socket
|
||||||
|
backendSocket!.setTimeout(timeout);
|
||||||
|
|
||||||
|
backendSocket!.on('timeout', () => {
|
||||||
|
this.emit(ForwardingHandlerEvents.ERROR, {
|
||||||
|
remoteAddress,
|
||||||
|
error: 'Backend connection timeout'
|
||||||
|
});
|
||||||
|
// Let setupBidirectionalForwarding handle the cleanup
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set up handlers for backend socket
|
// Handle backend connection errors
|
||||||
setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend');
|
|
||||||
|
|
||||||
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}`
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
if (!isConnectedToBackend) {
|
||||||
// Set timeout for backend socket
|
// Connection failed, clean up TLS socket
|
||||||
backendSocket.setTimeout(timeout);
|
if (!tlsSocket.destroyed) {
|
||||||
|
tlsSocket.destroy();
|
||||||
backendSocket.on('timeout', () => {
|
}
|
||||||
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
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user