150 lines
4.6 KiB
TypeScript
150 lines
4.6 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import { SmartProxy } from '../ts/index.js';
|
|
import * as net from 'net';
|
|
|
|
// Test that verifies HTTP connections on ports configured in useHttpProxy are properly forwarded
|
|
tap.test('should detect and forward non-TLS connections on HttpProxy ports', async (tapTest) => {
|
|
// Track whether the connection was forwarded to HttpProxy
|
|
let forwardedToHttpProxy = false;
|
|
let connectionPath = '';
|
|
|
|
// Mock the HttpProxy forwarding
|
|
const originalForward = SmartProxy.prototype['httpProxyBridge'].prototype.forwardToHttpProxy;
|
|
SmartProxy.prototype['httpProxyBridge'].prototype.forwardToHttpProxy = function(...args: any[]) {
|
|
forwardedToHttpProxy = true;
|
|
connectionPath = 'httpproxy';
|
|
console.log('Mock: Connection forwarded to HttpProxy');
|
|
// Just close the connection for the test
|
|
args[1].end(); // socket.end()
|
|
};
|
|
|
|
// Create a SmartProxy with useHttpProxy configured
|
|
const proxy = new SmartProxy({
|
|
useHttpProxy: [8080],
|
|
httpProxyPort: 8844,
|
|
enableDetailedLogging: true,
|
|
routes: [{
|
|
name: 'test-route',
|
|
match: {
|
|
ports: 8080
|
|
},
|
|
action: {
|
|
type: 'forward',
|
|
target: { host: 'localhost', port: 8181 }
|
|
}
|
|
}]
|
|
});
|
|
|
|
// Override the HttpProxy initialization to avoid actual HttpProxy setup
|
|
proxy['httpProxyBridge'].getHttpProxy = () => ({} as any);
|
|
|
|
await proxy.start();
|
|
|
|
// Make a connection to port 8080
|
|
const client = new net.Socket();
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
client.connect(8080, 'localhost', () => {
|
|
console.log('Client connected to proxy on port 8080');
|
|
// Send a non-TLS HTTP request
|
|
client.write('GET / HTTP/1.1\r\nHost: test.local\r\n\r\n');
|
|
resolve();
|
|
});
|
|
|
|
client.on('error', reject);
|
|
});
|
|
|
|
// Give it a moment to process
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
// Verify the connection was forwarded to HttpProxy
|
|
expect(forwardedToHttpProxy).toEqual(true);
|
|
expect(connectionPath).toEqual('httpproxy');
|
|
|
|
client.destroy();
|
|
await proxy.stop();
|
|
|
|
// Restore original method
|
|
SmartProxy.prototype['httpProxyBridge'].prototype.forwardToHttpProxy = originalForward;
|
|
});
|
|
|
|
// Test that verifies the fix detects non-TLS connections
|
|
tap.test('should properly detect non-TLS connections on HttpProxy ports', async (tapTest) => {
|
|
const targetPort = 8182;
|
|
let receivedConnection = false;
|
|
|
|
// Create a target server that never receives the connection (because it goes to HttpProxy)
|
|
const targetServer = net.createServer((socket) => {
|
|
receivedConnection = true;
|
|
socket.end();
|
|
});
|
|
|
|
await new Promise<void>((resolve) => {
|
|
targetServer.listen(targetPort, () => {
|
|
console.log(`Target server listening on port ${targetPort}`);
|
|
resolve();
|
|
});
|
|
});
|
|
|
|
// Mock HttpProxyBridge to track forwarding
|
|
let httpProxyForwardCalled = false;
|
|
|
|
const proxy = new SmartProxy({
|
|
useHttpProxy: [8080],
|
|
httpProxyPort: 8844,
|
|
routes: [{
|
|
name: 'test-route',
|
|
match: {
|
|
ports: 8080
|
|
},
|
|
action: {
|
|
type: 'forward',
|
|
target: { host: 'localhost', port: targetPort }
|
|
}
|
|
}]
|
|
});
|
|
|
|
// Override the forwardToHttpProxy method to track calls
|
|
const originalForward = proxy['httpProxyBridge'].forwardToHttpProxy;
|
|
proxy['httpProxyBridge'].forwardToHttpProxy = async function(...args: any[]) {
|
|
httpProxyForwardCalled = true;
|
|
console.log('HttpProxy forward called with connectionId:', args[0]);
|
|
// Just end the connection
|
|
args[1].end();
|
|
};
|
|
|
|
// Mock getHttpProxy to return a truthy value
|
|
proxy['httpProxyBridge'].getHttpProxy = () => ({} as any);
|
|
|
|
await proxy.start();
|
|
|
|
// Make a non-TLS connection
|
|
const client = new net.Socket();
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
client.connect(8080, 'localhost', () => {
|
|
console.log('Connected to proxy');
|
|
client.write('GET / HTTP/1.1\r\nHost: test.local\r\n\r\n');
|
|
resolve();
|
|
});
|
|
|
|
client.on('error', () => resolve()); // Ignore errors since we're ending the connection
|
|
});
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
// Verify that HttpProxy was called, not direct connection
|
|
expect(httpProxyForwardCalled).toEqual(true);
|
|
expect(receivedConnection).toEqual(false); // Target should not receive direct connection
|
|
|
|
client.destroy();
|
|
await proxy.stop();
|
|
await new Promise<void>((resolve) => {
|
|
targetServer.close(() => resolve());
|
|
});
|
|
|
|
// Restore original method
|
|
proxy['httpProxyBridge'].forwardToHttpProxy = originalForward;
|
|
});
|
|
|
|
tap.start(); |