Enhance socket cleanup and management for improved connection handling

- Refactor cleanupSocket function to support options for immediate destruction, allowing drain, and grace periods.
- Introduce createIndependentSocketHandlers for better management of half-open connections between client and server sockets.
- Update various handlers (HTTP, HTTPS passthrough, HTTPS terminate) to utilize new cleanup and socket management functions.
- Implement custom timeout handling in socket setup to prevent immediate closure during keep-alive connections.
- Add tests for long-lived connections and half-open connection scenarios to ensure stability and reliability.
- Adjust connection manager to handle socket cleanup based on activity status, improving resource management.
This commit is contained in:
2025-06-01 12:27:15 +00:00
parent 265b80ee04
commit ad80798210
13 changed files with 728 additions and 1223 deletions

View File

@ -49,7 +49,12 @@ export class HttpForwardingHandler extends ForwardingHandler {
});
};
setupSocketHandlers(socket, handleClose, 'http');
// Use custom timeout handler that doesn't close the socket
setupSocketHandlers(socket, handleClose, () => {
// For HTTP, we can be more aggressive with timeouts since connections are shorter
// But still don't close immediately - let the connection finish naturally
console.warn(`HTTP socket timeout from ${remoteAddress}`);
}, 'http');
socket.on('error', (error) => {
this.emit(ForwardingHandlerEvents.ERROR, {

View File

@ -2,7 +2,7 @@ import * as plugins from '../../plugins.js';
import { ForwardingHandler } from './base-handler.js';
import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
import { createSocketCleanupHandler, setupSocketHandlers, pipeSockets } from '../../core/utils/socket-utils.js';
import { createIndependentSocketHandlers, setupSocketHandlers } from '../../core/utils/socket-utils.js';
/**
* Handler for HTTPS passthrough (SNI forwarding without termination)
@ -55,19 +55,32 @@ export class HttpsPassthroughHandler extends ForwardingHandler {
let bytesSent = 0;
let bytesReceived = 0;
// Create cleanup handler with our utility
const handleClose = createSocketCleanupHandler(clientSocket, serverSocket, (reason) => {
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
remoteAddress,
bytesSent,
bytesReceived,
reason
});
});
// Create independent handlers for half-open connection support
const { cleanupClient, cleanupServer } = createIndependentSocketHandlers(
clientSocket,
serverSocket,
(reason) => {
this.emit(ForwardingHandlerEvents.DISCONNECTED, {
remoteAddress,
bytesSent,
bytesReceived,
reason
});
}
);
// Setup error and close handlers for both sockets
setupSocketHandlers(serverSocket, handleClose, 'server');
setupSocketHandlers(clientSocket, handleClose, 'client');
// Setup handlers with custom timeout handling that doesn't close connections
const timeout = this.getTimeout();
setupSocketHandlers(clientSocket, cleanupClient, (socket) => {
// Just reset timeout, don't close
socket.setTimeout(timeout);
}, 'client');
setupSocketHandlers(serverSocket, cleanupServer, (socket) => {
// Just reset timeout, don't close
socket.setTimeout(timeout);
}, 'server');
// Forward data from client to server
clientSocket.on('data', (data) => {
@ -117,8 +130,7 @@ export class HttpsPassthroughHandler extends ForwardingHandler {
});
});
// Set timeouts
const timeout = this.getTimeout();
// Set initial timeouts - they will be reset on each timeout event
clientSocket.setTimeout(timeout);
serverSocket.setTimeout(timeout);
}
@ -128,7 +140,7 @@ export class HttpsPassthroughHandler extends ForwardingHandler {
* @param req The HTTP request
* @param res The HTTP response
*/
public handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void {
public handleHttpRequest(_req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void {
// HTTPS passthrough doesn't support HTTP requests
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('HTTP not supported for this domain');

View File

@ -112,7 +112,7 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
});
// Set up error handling with our cleanup utility
setupSocketHandlers(tlsSocket, handleClose, 'tls');
setupSocketHandlers(tlsSocket, handleClose, undefined, 'tls');
// Set timeout
const timeout = this.getTimeout();
@ -167,7 +167,7 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
});
// Set up handlers for backend socket
setupSocketHandlers(backendSocket, newHandleClose, 'backend');
setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend');
backendSocket.on('error', (error) => {
this.emit(ForwardingHandlerEvents.ERROR, {

View File

@ -106,7 +106,7 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
});
// Set up error handling with our cleanup utility
setupSocketHandlers(tlsSocket, handleClose, 'tls');
setupSocketHandlers(tlsSocket, handleClose, undefined, 'tls');
// Set timeout
const timeout = this.getTimeout();
@ -151,7 +151,7 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
});
// Set up handlers for backend socket
setupSocketHandlers(backendSocket, newHandleClose, 'backend');
setupSocketHandlers(backendSocket, newHandleClose, undefined, 'backend');
backendSocket.on('error', (error) => {
this.emit(ForwardingHandlerEvents.ERROR, {