import { tap, expect } from '@git.zone/tstest/tapbundle'; import { RouteConnectionHandler } from '../ts/proxies/smart-proxy/route-connection-handler.js'; import { ISmartProxyOptions } from '../ts/proxies/smart-proxy/models/interfaces.js'; import * as net from 'net'; // Direct test of the fix in RouteConnectionHandler tap.test('should detect and forward non-TLS connections on useHttpProxy ports', async (tapTest) => { // Create mock objects const mockSettings: ISmartProxyOptions = { useHttpProxy: [8080], httpProxyPort: 8844, routes: [{ name: 'test-route', match: { ports: 8080 }, action: { type: 'forward', target: { host: 'localhost', port: 8181 } } }] }; let httpProxyForwardCalled = false; let directConnectionCalled = false; // Create mocks for dependencies const mockHttpProxyBridge = { getHttpProxy: () => ({ available: true }), forwardToHttpProxy: async (...args: any[]) => { console.log('Mock: forwardToHttpProxy called'); httpProxyForwardCalled = true; } }; // Mock connection manager const mockConnectionManager = { createConnection: (socket: any) => ({ id: 'test-connection', localPort: 8080, remoteIP: '127.0.0.1', isTLS: false }), initiateCleanupOnce: () => {}, cleanupConnection: () => {} }; // Mock route manager that returns a matching route const mockRouteManager = { findMatchingRoute: (criteria: any) => ({ route: mockSettings.routes[0] }) }; // Create route connection handler instance const handler = new RouteConnectionHandler( mockSettings, mockConnectionManager as any, {} as any, // security manager {} as any, // tls manager mockHttpProxyBridge as any, {} as any, // timeout manager mockRouteManager as any ); // Override setupDirectConnection to track if it's called handler['setupDirectConnection'] = (...args: any[]) => { console.log('Mock: setupDirectConnection called'); directConnectionCalled = true; }; // Test: Create a mock socket representing non-TLS connection on port 8080 const mockSocket = new net.Socket(); mockSocket.localPort = 8080; mockSocket.remoteAddress = '127.0.0.1'; // Simulate the handler processing the connection handler.handleConnection(mockSocket); // Simulate receiving non-TLS data mockSocket.emit('data', Buffer.from('GET / HTTP/1.1\r\nHost: test.local\r\n\r\n')); // Give it a moment to process await new Promise(resolve => setTimeout(resolve, 100)); // Verify that the connection was forwarded to HttpProxy, not direct connection expect(httpProxyForwardCalled).toEqual(true); expect(directConnectionCalled).toEqual(false); mockSocket.destroy(); }); // Test that verifies TLS connections still work normally tap.test('should handle TLS connections normally', async (tapTest) => { const mockSettings: ISmartProxyOptions = { useHttpProxy: [443], httpProxyPort: 8844, routes: [{ name: 'tls-route', match: { ports: 443 }, action: { type: 'forward', target: { host: 'localhost', port: 8443 }, tls: { mode: 'terminate' } } }] }; let httpProxyForwardCalled = false; const mockHttpProxyBridge = { getHttpProxy: () => ({ available: true }), forwardToHttpProxy: async (...args: any[]) => { httpProxyForwardCalled = true; } }; const mockConnectionManager = { createConnection: (socket: any) => ({ id: 'test-tls-connection', localPort: 443, remoteIP: '127.0.0.1', isTLS: true, tlsHandshakeComplete: false }), initiateCleanupOnce: () => {}, cleanupConnection: () => {} }; const mockTlsManager = { isTlsHandshake: (chunk: Buffer) => true, isClientHello: (chunk: Buffer) => true, extractSNI: (chunk: Buffer) => 'test.local' }; const mockRouteManager = { findMatchingRoute: (criteria: any) => ({ route: mockSettings.routes[0] }) }; const handler = new RouteConnectionHandler( mockSettings, mockConnectionManager as any, {} as any, mockTlsManager as any, mockHttpProxyBridge as any, {} as any, mockRouteManager as any ); const mockSocket = new net.Socket(); mockSocket.localPort = 443; mockSocket.remoteAddress = '127.0.0.1'; handler.handleConnection(mockSocket); // Simulate TLS handshake const tlsHandshake = Buffer.from([0x16, 0x03, 0x01, 0x00, 0x05]); mockSocket.emit('data', tlsHandshake); await new Promise(resolve => setTimeout(resolve, 100)); // TLS connections with 'terminate' mode should go to HttpProxy expect(httpProxyForwardCalled).toEqual(true); mockSocket.destroy(); }); tap.start();