184 lines
6.0 KiB
TypeScript
184 lines
6.0 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 = '';
|
|
|
|
// Create a SmartProxy instance first
|
|
const proxy = new SmartProxy({
|
|
useHttpProxy: [8081], // Use different port to avoid conflicts
|
|
httpProxyPort: 8847, // Use different port to avoid conflicts
|
|
routes: [{
|
|
name: 'test-http-forward',
|
|
match: { ports: 8081 },
|
|
action: {
|
|
type: 'forward',
|
|
target: { host: 'localhost', port: 8181 }
|
|
}
|
|
}]
|
|
});
|
|
|
|
// Add detailed logging to the existing proxy instance
|
|
proxy.settings.enableDetailedLogging = true;
|
|
|
|
// Override the HttpProxy initialization to avoid actual HttpProxy setup
|
|
const mockHttpProxy = { available: true };
|
|
proxy['httpProxyBridge'].initialize = async () => {
|
|
console.log('Mock: HttpProxyBridge initialized');
|
|
};
|
|
proxy['httpProxyBridge'].start = async () => {
|
|
console.log('Mock: HttpProxyBridge started');
|
|
};
|
|
proxy['httpProxyBridge'].stop = async () => {
|
|
console.log('Mock: HttpProxyBridge stopped');
|
|
};
|
|
|
|
await proxy.start();
|
|
|
|
// Mock the HttpProxy forwarding AFTER start to ensure it's not overridden
|
|
const originalForward = (proxy as any).httpProxyBridge.forwardToHttpProxy;
|
|
(proxy as any).httpProxyBridge.forwardToHttpProxy = async function(...args: any[]) {
|
|
forwardedToHttpProxy = true;
|
|
connectionPath = 'httpproxy';
|
|
console.log('Mock: Connection forwarded to HttpProxy with args:', args[0], 'on port:', args[2]?.localPort);
|
|
// Just close the connection for the test
|
|
args[1].end(); // socket.end()
|
|
};
|
|
|
|
const originalGetHttpProxy = proxy['httpProxyBridge'].getHttpProxy;
|
|
proxy['httpProxyBridge'].getHttpProxy = () => {
|
|
console.log('Mock: getHttpProxy called, returning:', mockHttpProxy);
|
|
return mockHttpProxy;
|
|
};
|
|
|
|
// Make a connection to port 8080
|
|
const client = new net.Socket();
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
client.connect(8081, 'localhost', () => {
|
|
console.log('Client connected to proxy on port 8081');
|
|
// Send a non-TLS HTTP request
|
|
client.write('GET / HTTP/1.1\r\nHost: test.local\r\n\r\n');
|
|
// Add a small delay to ensure data is sent
|
|
setTimeout(() => resolve(), 50);
|
|
});
|
|
|
|
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();
|
|
|
|
// Wait a bit to ensure port is released
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
// Restore original method
|
|
(proxy as any).httpProxyBridge.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: [8082], // Use different port to avoid conflicts
|
|
httpProxyPort: 8848, // Use different port to avoid conflicts
|
|
routes: [{
|
|
name: 'test-route',
|
|
match: {
|
|
ports: 8082
|
|
},
|
|
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 HttpProxyBridge methods
|
|
proxy['httpProxyBridge'].initialize = async () => {
|
|
console.log('Mock: HttpProxyBridge initialized');
|
|
};
|
|
proxy['httpProxyBridge'].start = async () => {
|
|
console.log('Mock: HttpProxyBridge started');
|
|
};
|
|
proxy['httpProxyBridge'].stop = async () => {
|
|
console.log('Mock: HttpProxyBridge stopped');
|
|
};
|
|
|
|
// 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(8082, 'localhost', () => {
|
|
console.log('Connected to proxy');
|
|
client.write('GET / HTTP/1.1\r\nHost: test.local\r\n\r\n');
|
|
// Add a small delay to ensure data is sent
|
|
setTimeout(() => resolve(), 50);
|
|
});
|
|
|
|
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());
|
|
});
|
|
|
|
// Wait a bit to ensure port is released
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
// Restore original method
|
|
proxy['httpProxyBridge'].forwardToHttpProxy = originalForward;
|
|
});
|
|
|
|
export default tap.start(); |