chore: initial commit
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import type {ForwardServerOptions, RelayLimits} from '../types.ts';
|
||||
|
||||
export const DEFAULT_LIMITS: Readonly<RelayLimits> = Object.freeze({
|
||||
auth_timeout_ms: 10_000,
|
||||
close_timeout_ms: 5_000,
|
||||
rpc_timeout_ms: 5_000,
|
||||
unused_timeout_ms: 60_000,
|
||||
idle_timeout_ms: 30_000,
|
||||
max_token_age_ms: 300_000,
|
||||
join_window_ms: 60_000,
|
||||
clock_skew_ms: 5_000,
|
||||
max_lease_ms: 300_000,
|
||||
max_token_bytes: 8_192,
|
||||
max_payload_bytes: 8 * 1024 * 1024,
|
||||
max_chat_length: 256,
|
||||
max_connections: 2_000,
|
||||
max_sessions: 500,
|
||||
max_grants_per_session: 32,
|
||||
max_pending_per_web: 16,
|
||||
max_pending_total: 4_096,
|
||||
requests_per_second: 30,
|
||||
events_per_second: 200,
|
||||
max_dedupe_entries: 256,
|
||||
dedupe_ttl_ms: 30_000,
|
||||
max_buffered_packets: 128,
|
||||
});
|
||||
|
||||
export function resolve_options(options: ForwardServerOptions) {
|
||||
if (!options || !Number.isSafeInteger(options.forward_id) || options.forward_id < 0) {
|
||||
throw new Error('forward_id must be a nonnegative safe integer');
|
||||
}
|
||||
if (typeof options.key !== 'string' || options.key.trim().length < 16) {
|
||||
throw new Error('A relay secret of at least 16 characters is required');
|
||||
}
|
||||
if (!Array.isArray(options.allowed_origins) || options.allowed_origins.some(origin => {
|
||||
try {
|
||||
const url = new URL(origin);
|
||||
return !['http:', 'https:'].includes(url.protocol) || url.origin !== origin;
|
||||
} catch { return true; }
|
||||
})) throw new Error('allowed_origins must contain explicit HTTP(S) origins');
|
||||
if (options.allow_no_origin !== undefined && typeof options.allow_no_origin !== 'boolean') {
|
||||
throw new Error('allow_no_origin must be a boolean');
|
||||
}
|
||||
const path = options.path ?? '/api/forward_server';
|
||||
const namespace = options.namespace ?? '/';
|
||||
if (!/^\/[A-Za-z0-9_/-]+$/.test(path) || path === '/' || path.endsWith('/')) {
|
||||
throw new Error('path must be an absolute Socket.IO path without a trailing slash');
|
||||
}
|
||||
if (!/^\/[A-Za-z0-9_/-]*$/.test(namespace) || (namespace !== '/' && namespace.endsWith('/'))) {
|
||||
throw new Error('namespace must be an absolute Socket.IO namespace');
|
||||
}
|
||||
const port = options.port ?? 38858;
|
||||
if (!Number.isInteger(port) || port < 0 || port > 65535) throw new Error('Invalid relay port');
|
||||
if (options.http_server && (options.port !== undefined || options.host !== undefined)) {
|
||||
throw new Error('Borrowed HTTP server cannot specify a relay host or port');
|
||||
}
|
||||
const limits = {...DEFAULT_LIMITS, ...options.limits};
|
||||
for (const [name, value] of Object.entries(limits)) {
|
||||
if (!Object.hasOwn(DEFAULT_LIMITS, name) || !Number.isSafeInteger(value) || value < (name === 'clock_skew_ms' ? 0 : 1)
|
||||
|| value > 2_147_483_647) throw new Error(`Invalid relay limit: ${name}`);
|
||||
}
|
||||
if (limits.join_window_ms > limits.max_token_age_ms) throw new Error('Join window exceeds token lifetime');
|
||||
return {...options, allowed_origins: [...options.allowed_origins], path, namespace, port, limits,
|
||||
allow_no_origin: options.allow_no_origin ?? true};
|
||||
}
|
||||
Reference in New Issue
Block a user