import { classifyHost, hostAllowed } from "./auth.js"; // Affinity key, feeding placement preference ONLY — never identity, access, // or rate decisions. Unset trustedProxies keys on the observed peer and // yields no affinity when X-Forwarded-For is present (an uninterpretable // proxy chain); "*" believes XFF outright (leftmost hop); a proxy list // takes the rightmost hop not from a trusted proxy. export const createAffinityKey = ( trustedProxiesOption: string[] | "*" | undefined, ) => { const trustedProxies = ( typeof trustedProxiesOption === "object" ? trustedProxiesOption : [] ).map((raw) => { const rule = classifyHost(raw); if (rule.kind !== "ip" && rule.kind !== "cidr") { throw new Error( `trustedProxies entries must be IPs or CIDRs: ${JSON.stringify(raw)}`, ); } return rule; }); const canonicalIp = (raw: string): string => raw.startsWith("::ffff:") ? raw.slice(7) : raw; const trustedIp = (ip: string): boolean => trustedProxies.length > 0 && hostAllowed(trustedProxies, ip); return ( req: Request, client: { address?: string | undefined }, ): string | null => { const mode = trustedProxiesOption; const peer = canonicalIp(client.address ?? ""); const hops = (req.headers.get("x-forwarded-for") ?? "") .split(",") .map((hop) => canonicalIp(hop.trim())) .filter((hop) => hop !== ""); if (mode === undefined) return hops.length === 0 ? peer : null; if (mode === "*") return hops[0] ?? peer; if (!trustedIp(peer)) return peer; for (let i = hops.length - 1; i >= 0; i--) { if (!trustedIp(hops[i]!)) return hops[i]!; } return peer; }; };