feat(smart-proxy): add typed Rust config serialization and regex header contract coverage

This commit is contained in:
2026-04-13 23:21:54 +00:00
parent af132f40fc
commit b5b4c608f0
14 changed files with 987 additions and 143 deletions
+192
View File
@@ -0,0 +1,192 @@
import { expect, tap } from '@git.zone/tstest/tapbundle';
import type { ISmartProxyOptions } from '../ts/proxies/smart-proxy/models/interfaces.js';
import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js';
import { RoutePreprocessor } from '../ts/proxies/smart-proxy/route-preprocessor.js';
import { buildRustProxyOptions } from '../ts/proxies/smart-proxy/utils/rust-config.js';
tap.test('Rust contract - preprocessor serializes regex headers for Rust', async () => {
const route: IRouteConfig = {
name: 'contract-route',
match: {
ports: [443, { from: 8443, to: 8444 }],
domains: ['api.example.com', '*.example.com'],
transport: 'udp',
protocol: 'http3',
headers: {
'Content-Type': /^application\/json$/i,
},
},
action: {
type: 'forward',
targets: [{
match: {
ports: [443],
path: '/api/*',
method: ['GET'],
headers: {
'X-Env': /^(prod|stage)$/,
},
},
host: ['backend-a', 'backend-b'],
port: 'preserve',
sendProxyProtocol: true,
backendTransport: 'tcp',
}],
tls: {
mode: 'terminate',
certificate: 'auto',
},
sendProxyProtocol: true,
udp: {
maxSessionsPerIP: 321,
quic: {
enableHttp3: true,
},
},
},
security: {
ipAllowList: [{
ip: '10.0.0.0/8',
domains: ['api.example.com'],
}],
},
};
const preprocessor = new RoutePreprocessor();
const [rustRoute] = preprocessor.preprocessForRust([route]);
expect(rustRoute.match.headers?.['Content-Type']).toEqual('/^application\\/json$/i');
expect(rustRoute.match.transport).toEqual('udp');
expect(rustRoute.match.protocol).toEqual('http3');
expect(rustRoute.action.targets?.[0].match?.headers?.['X-Env']).toEqual('/^(prod|stage)$/');
expect(rustRoute.action.targets?.[0].port).toEqual('preserve');
expect(rustRoute.action.targets?.[0].backendTransport).toEqual('tcp');
expect(rustRoute.action.sendProxyProtocol).toBeTrue();
expect(rustRoute.action.udp?.maxSessionsPerIp).toEqual(321);
});
tap.test('Rust contract - preprocessor converts dynamic targets to relay-safe payloads', async () => {
const route: IRouteConfig = {
name: 'dynamic-contract-route',
match: {
ports: 8080,
},
action: {
type: 'forward',
targets: [{
host: () => 'dynamic-backend.internal',
port: () => 9443,
}],
},
};
const preprocessor = new RoutePreprocessor();
const [rustRoute] = preprocessor.preprocessForRust([route]);
expect(rustRoute.action.type).toEqual('socket-handler');
expect(rustRoute.action.targets?.[0].host).toEqual('localhost');
expect(rustRoute.action.targets?.[0].port).toEqual(0);
expect(preprocessor.getOriginalRoute('dynamic-contract-route')).toEqual(route);
});
tap.test('Rust contract - top-level config keeps shared SmartProxy settings', async () => {
const settings: ISmartProxyOptions = {
routes: [{
name: 'top-level-contract-route',
match: {
ports: 443,
domains: 'api.example.com',
},
action: {
type: 'forward',
targets: [{
host: 'backend.internal',
port: 8443,
}],
tls: {
mode: 'terminate',
certificate: 'auto',
},
},
}],
preserveSourceIP: true,
proxyIPs: ['10.0.0.1'],
acceptProxyProtocol: true,
sendProxyProtocol: true,
noDelay: true,
keepAlive: true,
keepAliveInitialDelay: 1500,
maxPendingDataSize: 4096,
disableInactivityCheck: true,
enableKeepAliveProbes: true,
enableDetailedLogging: true,
enableTlsDebugLogging: true,
enableRandomizedTimeouts: true,
connectionTimeout: 5000,
initialDataTimeout: 7000,
socketTimeout: 9000,
inactivityCheckInterval: 1100,
maxConnectionLifetime: 13000,
inactivityTimeout: 15000,
gracefulShutdownTimeout: 17000,
maxConnectionsPerIP: 20,
connectionRateLimitPerMinute: 30,
keepAliveTreatment: 'extended',
keepAliveInactivityMultiplier: 2,
extendedKeepAliveLifetime: 19000,
metrics: {
enabled: true,
sampleIntervalMs: 250,
retentionSeconds: 60,
},
acme: {
enabled: true,
email: 'ops@example.com',
environment: 'staging',
useProduction: false,
skipConfiguredCerts: true,
renewThresholdDays: 14,
renewCheckIntervalHours: 12,
autoRenew: true,
port: 80,
},
};
const preprocessor = new RoutePreprocessor();
const routes = preprocessor.preprocessForRust(settings.routes);
const config = buildRustProxyOptions(settings, routes);
expect(config.preserveSourceIp).toBeTrue();
expect(config.proxyIps).toEqual(['10.0.0.1']);
expect(config.acceptProxyProtocol).toBeTrue();
expect(config.sendProxyProtocol).toBeTrue();
expect(config.noDelay).toBeTrue();
expect(config.keepAlive).toBeTrue();
expect(config.keepAliveInitialDelay).toEqual(1500);
expect(config.maxPendingDataSize).toEqual(4096);
expect(config.disableInactivityCheck).toBeTrue();
expect(config.enableKeepAliveProbes).toBeTrue();
expect(config.enableDetailedLogging).toBeTrue();
expect(config.enableTlsDebugLogging).toBeTrue();
expect(config.enableRandomizedTimeouts).toBeTrue();
expect(config.connectionTimeout).toEqual(5000);
expect(config.initialDataTimeout).toEqual(7000);
expect(config.socketTimeout).toEqual(9000);
expect(config.inactivityCheckInterval).toEqual(1100);
expect(config.maxConnectionLifetime).toEqual(13000);
expect(config.inactivityTimeout).toEqual(15000);
expect(config.gracefulShutdownTimeout).toEqual(17000);
expect(config.maxConnectionsPerIp).toEqual(20);
expect(config.connectionRateLimitPerMinute).toEqual(30);
expect(config.keepAliveTreatment).toEqual('extended');
expect(config.keepAliveInactivityMultiplier).toEqual(2);
expect(config.extendedKeepAliveLifetime).toEqual(19000);
expect(config.metrics?.sampleIntervalMs).toEqual(250);
expect(config.acme?.email).toEqual('ops@example.com');
expect(config.acme?.environment).toEqual('staging');
expect(config.acme?.skipConfiguredCerts).toBeTrue();
expect(config.acme?.renewThresholdDays).toEqual(14);
});
export default tap.start();