feat(smart-proxy): add socket-handler relay, fast-path port-only forwarding, metrics and bridge improvements, and various TS/Rust integration fixes

This commit is contained in:
2026-02-09 16:25:33 +00:00
parent 41efdb47f8
commit f7605e042e
17 changed files with 724 additions and 300 deletions

View File

@@ -1,13 +1,37 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as plugins from '../ts/plugins.js';
import * as http from 'http';
import { SmartProxy, SocketHandlers } from '../ts/index.js';
/**
* Helper to make HTTP requests using Node's http module (unlike fetch/undici,
* http.request doesn't keep the event loop alive via a connection pool).
*/
function httpRequest(url: string, options: { method?: string; headers?: Record<string, string> } = {}): Promise<{ status: number; headers: http.IncomingHttpHeaders; body: string }> {
return new Promise((resolve, reject) => {
const parsed = new URL(url);
const req = http.request({
hostname: parsed.hostname,
port: parsed.port,
path: parsed.pathname + parsed.search,
method: options.method || 'GET',
headers: options.headers,
}, (res) => {
let body = '';
res.on('data', (chunk: Buffer) => { body += chunk.toString(); });
res.on('end', () => resolve({ status: res.statusCode!, headers: res.headers, body }));
});
req.on('error', reject);
req.end();
});
}
tap.test('should handle HTTP requests on port 80 for ACME challenges', async (tools) => {
tools.timeout(10000);
// Track HTTP requests that are handled
const handledRequests: any[] = [];
const settings = {
routes: [
{
@@ -24,7 +48,7 @@ tap.test('should handle HTTP requests on port 80 for ACME challenges', async (to
method: req.method,
headers: req.headers
});
// Simulate ACME challenge response
const token = req.url?.split('/').pop() || '';
res.header('Content-Type', 'text/plain');
@@ -34,40 +58,31 @@ tap.test('should handle HTTP requests on port 80 for ACME challenges', async (to
}
]
};
const proxy = new SmartProxy(settings);
// Mock NFTables manager
(proxy as any).nftablesManager = {
ensureNFTablesSetup: async () => {},
stop: async () => {}
};
await proxy.start();
// Make an HTTP request to the challenge endpoint
const response = await fetch('http://localhost:18080/.well-known/acme-challenge/test-token', {
method: 'GET'
});
const response = await httpRequest('http://localhost:18080/.well-known/acme-challenge/test-token');
// Verify response
expect(response.status).toEqual(200);
const body = await response.text();
expect(body).toEqual('challenge-response-for-test-token');
expect(response.body).toEqual('challenge-response-for-test-token');
// Verify request was handled
expect(handledRequests.length).toEqual(1);
expect(handledRequests[0].path).toEqual('/.well-known/acme-challenge/test-token');
expect(handledRequests[0].method).toEqual('GET');
await proxy.stop();
});
tap.test('should parse HTTP headers correctly', async (tools) => {
tools.timeout(10000);
const capturedContext: any = {};
const settings = {
routes: [
{
@@ -92,36 +107,30 @@ tap.test('should parse HTTP headers correctly', async (tools) => {
}
]
};
const proxy = new SmartProxy(settings);
// Mock NFTables manager
(proxy as any).nftablesManager = {
ensureNFTablesSetup: async () => {},
stop: async () => {}
};
await proxy.start();
// Make request with custom headers
const response = await fetch('http://localhost:18081/test', {
const response = await httpRequest('http://localhost:18081/test', {
method: 'POST',
headers: {
'X-Custom-Header': 'test-value',
'User-Agent': 'test-agent'
}
});
expect(response.status).toEqual(200);
const body = await response.json();
const body = JSON.parse(response.body);
// Verify headers were parsed correctly
expect(capturedContext.headers['x-custom-header']).toEqual('test-value');
expect(capturedContext.headers['user-agent']).toEqual('test-agent');
expect(capturedContext.method).toEqual('POST');
expect(capturedContext.path).toEqual('/test');
await proxy.stop();
});
export default tap.start();
export default tap.start();