278 lines
6.7 KiB
TypeScript
278 lines
6.7 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import * as net from 'net';
|
|
import * as tls from 'tls';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { SmartProxy } from '../ts/proxies/smart-proxy/smart-proxy.js';
|
|
import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js';
|
|
|
|
// Setup test infrastructure
|
|
const testCertPath = path.join(process.cwd(), 'test', 'helpers', 'test-cert.pem');
|
|
const testKeyPath = path.join(process.cwd(), 'test', 'helpers', 'test-key.pem');
|
|
|
|
let testServer: net.Server;
|
|
let tlsTestServer: tls.Server;
|
|
let smartProxy: SmartProxy;
|
|
|
|
tap.test('setup test servers', async () => {
|
|
// Create TCP test server
|
|
testServer = net.createServer((socket) => {
|
|
socket.write('Connected to TCP test server\n');
|
|
socket.on('data', (data) => {
|
|
socket.write(`TCP Echo: ${data}`);
|
|
});
|
|
});
|
|
|
|
await new Promise<void>((resolve) => {
|
|
testServer.listen(7001, '127.0.0.1', () => {
|
|
console.log('TCP test server listening on port 7001');
|
|
resolve();
|
|
});
|
|
});
|
|
|
|
// Create TLS test server for SNI testing
|
|
tlsTestServer = tls.createServer(
|
|
{
|
|
cert: fs.readFileSync(testCertPath),
|
|
key: fs.readFileSync(testKeyPath),
|
|
},
|
|
(socket) => {
|
|
socket.write('Connected to TLS test server\n');
|
|
socket.on('data', (data) => {
|
|
socket.write(`TLS Echo: ${data}`);
|
|
});
|
|
}
|
|
);
|
|
|
|
await new Promise<void>((resolve) => {
|
|
tlsTestServer.listen(7002, '127.0.0.1', () => {
|
|
console.log('TLS test server listening on port 7002');
|
|
resolve();
|
|
});
|
|
});
|
|
});
|
|
|
|
tap.test('should forward TCP connections correctly', async () => {
|
|
// Create SmartProxy with forward route
|
|
smartProxy = new SmartProxy({
|
|
enableDetailedLogging: true,
|
|
routes: [
|
|
{
|
|
id: 'tcp-forward',
|
|
name: 'TCP Forward Route',
|
|
match: {
|
|
ports: 8080,
|
|
},
|
|
action: {
|
|
type: 'forward',
|
|
target: {
|
|
host: '127.0.0.1',
|
|
port: 7001,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
await smartProxy.start();
|
|
|
|
// Test TCP forwarding
|
|
const client = await new Promise<net.Socket>((resolve, reject) => {
|
|
const socket = net.connect(8080, '127.0.0.1', () => {
|
|
console.log('Connected to proxy');
|
|
resolve(socket);
|
|
});
|
|
socket.on('error', reject);
|
|
});
|
|
|
|
// Test data transmission
|
|
await new Promise<void>((resolve) => {
|
|
client.on('data', (data) => {
|
|
const response = data.toString();
|
|
console.log('Received:', response);
|
|
expect(response).toContain('Connected to TCP test server');
|
|
client.end();
|
|
resolve();
|
|
});
|
|
|
|
client.write('Hello from client');
|
|
});
|
|
|
|
await smartProxy.stop();
|
|
});
|
|
|
|
tap.test('should handle TLS passthrough correctly', async () => {
|
|
// Create SmartProxy with TLS passthrough route
|
|
smartProxy = new SmartProxy({
|
|
enableDetailedLogging: true,
|
|
routes: [
|
|
{
|
|
id: 'tls-passthrough',
|
|
name: 'TLS Passthrough Route',
|
|
match: {
|
|
ports: 8443,
|
|
domains: 'test.example.com',
|
|
},
|
|
action: {
|
|
type: 'forward',
|
|
tls: {
|
|
mode: 'passthrough',
|
|
},
|
|
target: {
|
|
host: '127.0.0.1',
|
|
port: 7002,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
await smartProxy.start();
|
|
|
|
// Test TLS passthrough
|
|
const client = await new Promise<tls.TLSSocket>((resolve, reject) => {
|
|
const socket = tls.connect(
|
|
{
|
|
port: 8443,
|
|
host: '127.0.0.1',
|
|
servername: 'test.example.com',
|
|
rejectUnauthorized: false,
|
|
},
|
|
() => {
|
|
console.log('Connected via TLS');
|
|
resolve(socket);
|
|
}
|
|
);
|
|
socket.on('error', reject);
|
|
});
|
|
|
|
// Test data transmission over TLS
|
|
await new Promise<void>((resolve) => {
|
|
client.on('data', (data) => {
|
|
const response = data.toString();
|
|
console.log('TLS Received:', response);
|
|
expect(response).toContain('Connected to TLS test server');
|
|
client.end();
|
|
resolve();
|
|
});
|
|
|
|
client.write('Hello from TLS client');
|
|
});
|
|
|
|
await smartProxy.stop();
|
|
});
|
|
|
|
tap.test('should handle SNI-based forwarding', async () => {
|
|
// Create SmartProxy with multiple domain routes
|
|
smartProxy = new SmartProxy({
|
|
enableDetailedLogging: true,
|
|
routes: [
|
|
{
|
|
id: 'domain-a',
|
|
name: 'Domain A Route',
|
|
match: {
|
|
ports: 8443,
|
|
domains: 'a.example.com',
|
|
},
|
|
action: {
|
|
type: 'forward',
|
|
tls: {
|
|
mode: 'passthrough',
|
|
},
|
|
target: {
|
|
host: '127.0.0.1',
|
|
port: 7002,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
id: 'domain-b',
|
|
name: 'Domain B Route',
|
|
match: {
|
|
ports: 8443,
|
|
domains: 'b.example.com',
|
|
},
|
|
action: {
|
|
type: 'forward',
|
|
tls: {
|
|
mode: 'passthrough',
|
|
},
|
|
target: {
|
|
host: '127.0.0.1',
|
|
port: 7002,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
await smartProxy.start();
|
|
|
|
// Test domain A (TLS passthrough)
|
|
const clientA = await new Promise<tls.TLSSocket>((resolve, reject) => {
|
|
const socket = tls.connect(
|
|
{
|
|
port: 8443,
|
|
host: '127.0.0.1',
|
|
servername: 'a.example.com',
|
|
rejectUnauthorized: false,
|
|
},
|
|
() => {
|
|
console.log('Connected to domain A');
|
|
resolve(socket);
|
|
}
|
|
);
|
|
socket.on('error', reject);
|
|
});
|
|
|
|
await new Promise<void>((resolve) => {
|
|
clientA.on('data', (data) => {
|
|
const response = data.toString();
|
|
console.log('Domain A response:', response);
|
|
expect(response).toContain('Connected to TLS test server');
|
|
clientA.end();
|
|
resolve();
|
|
});
|
|
|
|
clientA.write('Hello from domain A');
|
|
});
|
|
|
|
// Test domain B should also use TLS since it's on port 8443
|
|
const clientB = await new Promise<tls.TLSSocket>((resolve, reject) => {
|
|
const socket = tls.connect(
|
|
{
|
|
port: 8443,
|
|
host: '127.0.0.1',
|
|
servername: 'b.example.com',
|
|
rejectUnauthorized: false,
|
|
},
|
|
() => {
|
|
console.log('Connected to domain B');
|
|
resolve(socket);
|
|
}
|
|
);
|
|
socket.on('error', reject);
|
|
});
|
|
|
|
await new Promise<void>((resolve) => {
|
|
clientB.on('data', (data) => {
|
|
const response = data.toString();
|
|
console.log('Domain B response:', response);
|
|
// Should be forwarded to TLS server
|
|
expect(response).toContain('Connected to TLS test server');
|
|
clientB.end();
|
|
resolve();
|
|
});
|
|
|
|
clientB.write('Hello from domain B');
|
|
});
|
|
|
|
await smartProxy.stop();
|
|
});
|
|
|
|
tap.test('cleanup', async () => {
|
|
testServer.close();
|
|
tlsTestServer.close();
|
|
});
|
|
|
|
export default tap.start(); |