Files
smartproxy/ts/proxies/smart-proxy/utils/rust-config.ts
T

188 lines
5.5 KiB
TypeScript

import type { IAcmeOptions, ISmartProxyOptions } from '../models/interfaces.js';
import type { IRouteAction, IRouteConfig, IRouteMatch, IRouteTarget, ITargetMatch } from '../models/route-types.js';
import type {
IRustAcmeOptions,
IRustDefaultConfig,
IRustProxyOptions,
IRustRouteAction,
IRustRouteConfig,
IRustRouteMatch,
IRustRouteTarget,
IRustTargetMatch,
IRustRouteUdp,
TRustHeaderMatchers,
} from '../models/rust-types.js';
const SUPPORTED_REGEX_FLAGS = new Set(['i', 'm', 's', 'u', 'g']);
export function serializeHeaderMatchValue(value: string | RegExp): string {
if (typeof value === 'string') {
return value;
}
const unsupportedFlags = Array.from(new Set(value.flags)).filter((flag) => !SUPPORTED_REGEX_FLAGS.has(flag));
if (unsupportedFlags.length > 0) {
throw new Error(
`Header RegExp uses unsupported flags for Rust serialization: ${unsupportedFlags.join(', ')}`
);
}
return `/${value.source}/${value.flags}`;
}
export function serializeHeaderMatchers(headers?: Record<string, string | RegExp>): TRustHeaderMatchers | undefined {
if (!headers) {
return undefined;
}
return Object.fromEntries(
Object.entries(headers).map(([key, value]) => [key, serializeHeaderMatchValue(value)])
);
}
export function serializeTargetMatchForRust(match?: ITargetMatch): IRustTargetMatch | undefined {
if (!match) {
return undefined;
}
return {
...match,
headers: serializeHeaderMatchers(match.headers),
};
}
export function serializeRouteMatchForRust(match: IRouteMatch): IRustRouteMatch {
return {
...match,
headers: serializeHeaderMatchers(match.headers),
};
}
export function serializeRouteTargetForRust(target: IRouteTarget): IRustRouteTarget {
if (typeof target.host !== 'string' && !Array.isArray(target.host)) {
throw new Error('Route target host must be serialized before sending to Rust');
}
if (typeof target.port !== 'number' && target.port !== 'preserve') {
throw new Error('Route target port must be serialized before sending to Rust');
}
return {
...target,
host: target.host,
port: target.port,
match: serializeTargetMatchForRust(target.match),
};
}
function serializeUdpForRust(udp?: IRouteAction['udp']): IRustRouteUdp | undefined {
if (!udp) {
return undefined;
}
const { maxSessionsPerIP, ...rest } = udp;
return {
...rest,
maxSessionsPerIp: maxSessionsPerIP,
};
}
export function serializeRouteActionForRust(action: IRouteAction): IRustRouteAction {
const {
socketHandler: _socketHandler,
datagramHandler: _datagramHandler,
forwardingEngine: _forwardingEngine,
nftables: _nftables,
targets,
udp,
...rest
} = action;
return {
...rest,
targets: targets?.map((target) => serializeRouteTargetForRust(target)),
udp: serializeUdpForRust(udp),
};
}
export function serializeRouteForRust(route: IRouteConfig): IRustRouteConfig {
return {
...route,
match: serializeRouteMatchForRust(route.match),
action: serializeRouteActionForRust(route.action),
};
}
function serializeAcmeForRust(acme?: IAcmeOptions): IRustAcmeOptions | undefined {
if (!acme) {
return undefined;
}
return {
enabled: acme.enabled,
email: acme.email,
environment: acme.environment,
accountEmail: acme.accountEmail,
port: acme.port,
useProduction: acme.useProduction,
renewThresholdDays: acme.renewThresholdDays,
autoRenew: acme.autoRenew,
skipConfiguredCerts: acme.skipConfiguredCerts,
renewCheckIntervalHours: acme.renewCheckIntervalHours,
};
}
function serializeDefaultsForRust(defaults?: ISmartProxyOptions['defaults']): IRustDefaultConfig | undefined {
if (!defaults) {
return undefined;
}
const { preserveSourceIP, ...rest } = defaults;
return {
...rest,
preserveSourceIp: preserveSourceIP,
};
}
export function buildRustProxyOptions(
settings: ISmartProxyOptions,
routes: IRustRouteConfig[],
acmeOverride?: IAcmeOptions,
): IRustProxyOptions {
const acme = acmeOverride !== undefined ? acmeOverride : settings.acme;
return {
routes,
preserveSourceIp: settings.preserveSourceIP,
proxyIps: settings.proxyIPs,
acceptProxyProtocol: settings.acceptProxyProtocol,
sendProxyProtocol: settings.sendProxyProtocol,
defaults: serializeDefaultsForRust(settings.defaults),
connectionTimeout: settings.connectionTimeout,
initialDataTimeout: settings.initialDataTimeout,
socketTimeout: settings.socketTimeout,
inactivityCheckInterval: settings.inactivityCheckInterval,
maxConnectionLifetime: settings.maxConnectionLifetime,
inactivityTimeout: settings.inactivityTimeout,
gracefulShutdownTimeout: settings.gracefulShutdownTimeout,
noDelay: settings.noDelay,
keepAlive: settings.keepAlive,
keepAliveInitialDelay: settings.keepAliveInitialDelay,
maxPendingDataSize: settings.maxPendingDataSize,
disableInactivityCheck: settings.disableInactivityCheck,
enableKeepAliveProbes: settings.enableKeepAliveProbes,
enableDetailedLogging: settings.enableDetailedLogging,
enableTlsDebugLogging: settings.enableTlsDebugLogging,
enableRandomizedTimeouts: settings.enableRandomizedTimeouts,
maxConnectionsPerIp: settings.maxConnectionsPerIP,
connectionRateLimitPerMinute: settings.connectionRateLimitPerMinute,
keepAliveTreatment: settings.keepAliveTreatment,
keepAliveInactivityMultiplier: settings.keepAliveInactivityMultiplier,
extendedKeepAliveLifetime: settings.extendedKeepAliveLifetime,
metrics: settings.metrics,
acme: serializeAcmeForRust(acme),
};
}