UNPKG

@push.rocks/smartproxy

Version:

A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.

209 lines (181 loc) 6.67 kB
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, IRustChallengeOptions, 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 buildChallengeOptionsForRust(settings: ISmartProxyOptions): IRustChallengeOptions | undefined { const hasChallengeRoutes = settings.routes.some((route) => Boolean(route.security?.challenge)); if (!hasChallengeRoutes) { return undefined; } const runtimeOptions = settings.challenge || {}; return { cookieSigningKey: runtimeOptions.cookieSigningKey || '', pendingCookieName: runtimeOptions.pendingCookieName || '__smartproxy_challenge_pending', clearanceCookieName: runtimeOptions.clearanceCookieName || '__smartproxy_clearance', reservedPathPrefix: runtimeOptions.reservedPathPrefix || '/.well-known/smartproxy-challenge', relaySocketPath: runtimeOptions.relaySocketPath || '', relayTimeoutMs: runtimeOptions.relayTimeoutMs ?? 5_000, pendingTtlSeconds: runtimeOptions.pendingTtlSeconds ?? 300, }; } export function buildRustProxyOptions( settings: ISmartProxyOptions, routes: IRustRouteConfig[], acmeOverride?: IAcmeOptions, challengeOverride?: IRustChallengeOptions, ): IRustProxyOptions { const acme = acmeOverride !== undefined ? acmeOverride : settings.acme; return { routes, preserveSourceIp: settings.preserveSourceIP, trustedProxyIPs: settings.trustedProxyIPs, 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, securityPolicy: settings.securityPolicy, challenge: challengeOverride || buildChallengeOptionsForRust(settings), acme: serializeAcmeForRust(acme), }; }