188 lines
5.5 KiB
TypeScript
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),
|
|
};
|
|
}
|