Implement proxy chain connection accumulation fix and add comprehensive tests
- Updated socket handling to prevent connection accumulation in chained proxies. - Introduced centralized bidirectional forwarding for consistent socket management. - Enhanced cleanup logic to ensure immediate closure of sockets when one closes. - Added tests to verify connection behavior under various scenarios, including backend failures and rapid reconnections.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
import { HttpProxy } from '../http-proxy/index.js';
|
||||
import { setupBidirectionalForwarding } from '../../core/utils/socket-utils.js';
|
||||
import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
|
||||
import type { IRouteConfig } from './models/route-types.js';
|
||||
|
||||
@@ -123,36 +124,25 @@ export class HttpProxyBridge {
|
||||
proxySocket.write(initialChunk);
|
||||
}
|
||||
|
||||
// Pipe the sockets together
|
||||
socket.pipe(proxySocket);
|
||||
proxySocket.pipe(socket);
|
||||
|
||||
// Handle cleanup
|
||||
let cleanedUp = false;
|
||||
const cleanup = (reason: string) => {
|
||||
if (cleanedUp) return;
|
||||
cleanedUp = true;
|
||||
|
||||
// Remove all event listeners to prevent memory leaks
|
||||
socket.removeAllListeners('end');
|
||||
socket.removeAllListeners('error');
|
||||
proxySocket.removeAllListeners('end');
|
||||
proxySocket.removeAllListeners('error');
|
||||
|
||||
socket.unpipe(proxySocket);
|
||||
proxySocket.unpipe(socket);
|
||||
|
||||
if (!proxySocket.destroyed) {
|
||||
proxySocket.destroy();
|
||||
}
|
||||
|
||||
cleanupCallback(reason);
|
||||
};
|
||||
|
||||
socket.on('end', () => cleanup('socket_end'));
|
||||
socket.on('error', () => cleanup('socket_error'));
|
||||
proxySocket.on('end', () => cleanup('proxy_end'));
|
||||
proxySocket.on('error', () => cleanup('proxy_error'));
|
||||
// Use centralized bidirectional forwarding
|
||||
setupBidirectionalForwarding(socket, proxySocket, {
|
||||
onClientData: (chunk) => {
|
||||
// Update stats if needed
|
||||
if (record) {
|
||||
record.bytesReceived += chunk.length;
|
||||
}
|
||||
},
|
||||
onServerData: (chunk) => {
|
||||
// Update stats if needed
|
||||
if (record) {
|
||||
record.bytesSent += chunk.length;
|
||||
}
|
||||
},
|
||||
onCleanup: (reason) => {
|
||||
cleanupCallback(reason);
|
||||
},
|
||||
enableHalfOpen: false // Close both when one closes (required for proxy chains)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user