154 lines
4.7 KiB
TypeScript
154 lines
4.7 KiB
TypeScript
import type * as plugins from '../plugins.js';
|
|
|
|
/**
|
|
* Configuration for HTTP/3 (QUIC) route augmentation.
|
|
* HTTP/3 is enabled by default on all qualifying HTTPS routes.
|
|
*/
|
|
export interface IHttp3Config {
|
|
/** Enable HTTP/3 augmentation on qualifying routes (default: true) */
|
|
enabled?: boolean;
|
|
/** QUIC-specific settings applied to all augmented routes */
|
|
quicSettings?: {
|
|
/** QUIC connection idle timeout in ms (default: 30000) */
|
|
maxIdleTimeout?: number;
|
|
/** Max concurrent bidirectional streams per connection (default: 100) */
|
|
maxConcurrentBidiStreams?: number;
|
|
/** Max concurrent unidirectional streams per connection (default: 100) */
|
|
maxConcurrentUniStreams?: number;
|
|
/** Initial congestion window size in bytes */
|
|
initialCongestionWindow?: number;
|
|
};
|
|
/** Alt-Svc header settings */
|
|
altSvc?: {
|
|
/** Port advertised in Alt-Svc header (default: same as listening port) */
|
|
port?: number;
|
|
/** Max age for Alt-Svc advertisement in seconds (default: 86400) */
|
|
maxAge?: number;
|
|
};
|
|
/** UDP session settings */
|
|
udpSettings?: {
|
|
/** Idle timeout for UDP sessions in ms (default: 60000) */
|
|
sessionTimeout?: number;
|
|
/** Max concurrent UDP sessions per source IP (default: 1000) */
|
|
maxSessionsPerIP?: number;
|
|
/** Max accepted datagram size in bytes (default: 65535) */
|
|
maxDatagramSize?: number;
|
|
};
|
|
}
|
|
|
|
type TPortRange = plugins.smartproxy.IRouteConfig['match']['ports'];
|
|
|
|
/**
|
|
* Check whether a TPortRange includes port 443.
|
|
*/
|
|
function portRangeIncludes443(ports: TPortRange): boolean {
|
|
if (typeof ports === 'number') return ports === 443;
|
|
if (Array.isArray(ports)) {
|
|
return ports.some((p) => {
|
|
if (typeof p === 'number') return p === 443;
|
|
return p.from <= 443 && p.to >= 443;
|
|
});
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check if a route name indicates an email route that should not get HTTP/3.
|
|
*/
|
|
function isEmailRoute(route: plugins.smartproxy.IRouteConfig): boolean {
|
|
const name = route.name?.toLowerCase() || '';
|
|
return (
|
|
name.startsWith('smtp-') ||
|
|
name.startsWith('submission-') ||
|
|
name.startsWith('smtps-') ||
|
|
name.startsWith('email-')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Determine if a route qualifies for HTTP/3 augmentation.
|
|
*/
|
|
export function routeQualifiesForHttp3(
|
|
route: plugins.smartproxy.IRouteConfig,
|
|
globalConfig: IHttp3Config,
|
|
): boolean {
|
|
// Check global enable + per-route override
|
|
const globalEnabled = globalConfig.enabled !== false; // default true
|
|
const perRouteOverride = route.action.options?.http3;
|
|
|
|
// If per-route explicitly set, use that; otherwise use global
|
|
const shouldAugment =
|
|
perRouteOverride !== undefined ? perRouteOverride : globalEnabled;
|
|
if (!shouldAugment) return false;
|
|
|
|
// Must be forward type
|
|
if (route.action.type !== 'forward') return false;
|
|
|
|
// Must include port 443
|
|
if (!portRangeIncludes443(route.match.ports)) return false;
|
|
|
|
// Must have TLS
|
|
if (!route.action.tls) return false;
|
|
|
|
// Skip email routes
|
|
if (isEmailRoute(route)) return false;
|
|
|
|
// Skip if already configured with transport 'all' or 'udp'
|
|
if (route.match.transport === 'all' || route.match.transport === 'udp') return false;
|
|
|
|
// Skip if already has QUIC config
|
|
if (route.action.udp?.quic) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Augment a single route with HTTP/3 fields.
|
|
* Returns a new route object (does not mutate the original).
|
|
*/
|
|
export function augmentRouteWithHttp3(
|
|
route: plugins.smartproxy.IRouteConfig,
|
|
config: IHttp3Config,
|
|
): plugins.smartproxy.IRouteConfig {
|
|
if (!routeQualifiesForHttp3(route, config)) {
|
|
return route;
|
|
}
|
|
|
|
return {
|
|
...route,
|
|
match: {
|
|
...route.match,
|
|
transport: 'all' as const,
|
|
},
|
|
action: {
|
|
...route.action,
|
|
udp: {
|
|
...(route.action.udp || {}),
|
|
sessionTimeout: config.udpSettings?.sessionTimeout,
|
|
maxSessionsPerIP: config.udpSettings?.maxSessionsPerIP,
|
|
maxDatagramSize: config.udpSettings?.maxDatagramSize,
|
|
quic: {
|
|
enableHttp3: true,
|
|
maxIdleTimeout: config.quicSettings?.maxIdleTimeout,
|
|
maxConcurrentBidiStreams: config.quicSettings?.maxConcurrentBidiStreams,
|
|
maxConcurrentUniStreams: config.quicSettings?.maxConcurrentUniStreams,
|
|
altSvcPort: config.altSvc?.port,
|
|
altSvcMaxAge: config.altSvc?.maxAge ?? 86400,
|
|
initialCongestionWindow: config.quicSettings?.initialCongestionWindow,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Augment all qualifying routes in an array.
|
|
* Returns a new array (does not mutate originals).
|
|
*/
|
|
export function augmentRoutesWithHttp3(
|
|
routes: plugins.smartproxy.IRouteConfig[],
|
|
config: IHttp3Config,
|
|
): plugins.smartproxy.IRouteConfig[] {
|
|
return routes.map((route) => augmentRouteWithHttp3(route, config));
|
|
}
|