fix(PortProxy): Fix and enhance port proxy handling

This commit is contained in:
2025-02-27 14:23:44 +00:00
parent 2080f419cb
commit 5a5686b6b9
4 changed files with 50 additions and 38 deletions

View File

@ -16,12 +16,10 @@ function createTestServer(port: number): Promise<net.Server> {
// Echo the received data back
socket.write(`Echo: ${data.toString()}`);
});
socket.on('error', (error) => {
console.error('[Test Server] Socket error:', error);
});
});
server.listen(port, () => {
console.log(`[Test Server] Listening on port ${port}`);
resolve(server);
@ -39,16 +37,13 @@ function createTestClient(port: number, data: string): Promise<string> {
console.log('[Test Client] Connected to server');
client.write(data);
});
client.on('data', (chunk) => {
response += chunk.toString();
client.end();
});
client.on('end', () => {
resolve(response);
});
client.on('error', (error) => {
reject(error);
});
@ -61,16 +56,18 @@ tap.test('setup port proxy test environment', async () => {
portProxy = new PortProxy({
fromPort: PROXY_PORT,
toPort: TEST_SERVER_PORT,
toHost: 'localhost',
targetIP: 'localhost',
domains: [],
sniEnabled: false,
defaultAllowedIPs: ['127.0.0.1']
defaultAllowedIPs: ['127.0.0.1'],
globalPortRanges: []
});
});
tap.test('should start port proxy', async () => {
await portProxy.start();
expect(portProxy.netServer.listening).toBeTrue();
// Since netServers is private, we cast to any to verify that all created servers are listening.
expect((portProxy as any).netServers.every((server: net.Server) => server.listening)).toBeTrue();
});
tap.test('should forward TCP connections and data to localhost', async () => {
@ -79,14 +76,15 @@ tap.test('should forward TCP connections and data to localhost', async () => {
});
tap.test('should forward TCP connections to custom host', async () => {
// Create a new proxy instance with a custom host
// Create a new proxy instance with a custom host (targetIP)
const customHostProxy = new PortProxy({
fromPort: PROXY_PORT + 1,
toPort: TEST_SERVER_PORT,
toHost: '127.0.0.1',
targetIP: '127.0.0.1',
domains: [],
sniEnabled: false,
defaultAllowedIPs: ['127.0.0.1']
defaultAllowedIPs: ['127.0.0.1'],
globalPortRanges: []
});
await customHostProxy.start();
@ -103,8 +101,8 @@ tap.test('should forward connections based on domain-specific target IP', async
// Create a proxy with domain-specific target IPs
const domainProxy = new PortProxy({
fromPort: PROXY_PORT + 2,
toPort: TEST_SERVER_PORT, // default port
toHost: 'localhost', // default host
toPort: TEST_SERVER_PORT, // default port (for non-port-range handling)
targetIP: 'localhost', // default target IP
domains: [{
domain: 'domain1.test',
allowedIPs: ['127.0.0.1'],
@ -114,24 +112,26 @@ tap.test('should forward connections based on domain-specific target IP', async
allowedIPs: ['127.0.0.1'],
targetIP: 'localhost'
}],
sniEnabled: false, // We'll test without SNI first since this is a TCP proxy test
defaultAllowedIPs: ['127.0.0.1']
sniEnabled: false,
defaultAllowedIPs: ['127.0.0.1'],
globalPortRanges: []
});
await domainProxy.start();
// Test default connection (should use default host)
// Test default connection (should use default targetIP)
const response1 = await createTestClient(PROXY_PORT + 2, TEST_DATA);
expect(response1).toEqual(`Echo: ${TEST_DATA}`);
// Create another proxy with different default host
// Create another proxy with a different default targetIP
const domainProxy2 = new PortProxy({
fromPort: PROXY_PORT + 3,
toPort: TEST_SERVER_PORT,
toHost: '127.0.0.1',
targetIP: '127.0.0.1',
domains: [],
sniEnabled: false,
defaultAllowedIPs: ['127.0.0.1']
defaultAllowedIPs: ['127.0.0.1'],
globalPortRanges: []
});
await domainProxy2.start();
@ -158,7 +158,6 @@ tap.test('should handle multiple concurrent connections', async () => {
tap.test('should handle connection timeouts', async () => {
const client = new net.Socket();
await new Promise<void>((resolve) => {
client.connect(PROXY_PORT, 'localhost', () => {
// Don't send any data, just wait for timeout
@ -171,28 +170,30 @@ tap.test('should handle connection timeouts', async () => {
tap.test('should stop port proxy', async () => {
await portProxy.stop();
expect(portProxy.netServer.listening).toBeFalse();
expect((portProxy as any).netServers.every((server: net.Server) => !server.listening)).toBeTrue();
});
// Cleanup
// Cleanup chained proxies tests
tap.test('should support optional source IP preservation in chained proxies', async () => {
// Test 1: Without IP preservation (default behavior)
const firstProxyDefault = new PortProxy({
fromPort: PROXY_PORT + 4,
toPort: PROXY_PORT + 5,
toHost: 'localhost',
targetIP: 'localhost',
domains: [],
sniEnabled: false,
defaultAllowedIPs: ['127.0.0.1', '::ffff:127.0.0.1']
defaultAllowedIPs: ['127.0.0.1', '::ffff:127.0.0.1'],
globalPortRanges: []
});
const secondProxyDefault = new PortProxy({
fromPort: PROXY_PORT + 5,
toPort: TEST_SERVER_PORT,
toHost: 'localhost',
targetIP: 'localhost',
domains: [],
sniEnabled: false,
defaultAllowedIPs: ['127.0.0.1', '::ffff:127.0.0.1']
defaultAllowedIPs: ['127.0.0.1', '::ffff:127.0.0.1'],
globalPortRanges: []
});
await secondProxyDefault.start();
@ -209,21 +210,23 @@ tap.test('should support optional source IP preservation in chained proxies', as
const firstProxyPreserved = new PortProxy({
fromPort: PROXY_PORT + 6,
toPort: PROXY_PORT + 7,
toHost: 'localhost',
targetIP: 'localhost',
domains: [],
sniEnabled: false,
defaultAllowedIPs: ['127.0.0.1'],
preserveSourceIP: true
preserveSourceIP: true,
globalPortRanges: []
});
const secondProxyPreserved = new PortProxy({
fromPort: PROXY_PORT + 7,
toPort: TEST_SERVER_PORT,
toHost: 'localhost',
targetIP: 'localhost',
domains: [],
sniEnabled: false,
defaultAllowedIPs: ['127.0.0.1'],
preserveSourceIP: true
preserveSourceIP: true,
globalPortRanges: []
});
await secondProxyPreserved.start();
@ -245,9 +248,10 @@ process.on('exit', () => {
if (testServer) {
testServer.close();
}
if (portProxy && portProxy.netServer) {
// Use a cast to access the private property for cleanup.
if (portProxy && (portProxy as any).netServers) {
portProxy.stop();
}
});
export default tap.start();
export default tap.start();