168 lines
4.7 KiB
TypeScript
168 lines
4.7 KiB
TypeScript
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(); |