import { ServeHandler, ServerRequest } from "../server.ts";type OriginVerifier = string | RegExp | ((s: string) => boolean);export interface CORSOptions { origin: OriginVerifier | OriginVerifier[]; methods?: string[]; allowedHeaders?: string[]; exposedHeaders?: string[]; withCredentials?: boolean; maxAge?: number;}export function cors({ origin, methods = [], allowedHeaders = [], exposedHeaders = [], withCredentials = false, maxAge = 0,}: CORSOptions): ServeHandler { return (req) => { const requestOrigin = req.headers.get("origin"); if (!requestOrigin) { return; } if (req.method === "OPTIONS") { const isValidOrigin = setAccessControlAllowOrigin( origin, requestOrigin, req, ); if (isValidOrigin === false) return; setAccessControlRequestMethods(methods, req); setAccessControlRequestHeaders(allowedHeaders, req); setAcessControlExposeHeaders(exposedHeaders, req);
req.responseHeaders.set( "access-control-allow-credentials", withCredentials.toString(), ); req.responseHeaders.set("access-control-max-age", `${maxAge}`);
return req.respond({ status: 204 }); } else { setAccessControlAllowOrigin(origin, requestOrigin, req); setAcessControlExposeHeaders(exposedHeaders, req); req.responseHeaders.set( "access-control-allow-credentials", withCredentials.toString(), ); } };}
function verifyOrigin( vel: OriginVerifier | OriginVerifier[], origin: string,): boolean { if (typeof vel === "string") { return vel === origin; } else if (vel instanceof RegExp) { return origin.match(vel) != null; } else if (typeof vel === "function") { return vel(origin); } else { for (const v of vel) { if (verifyOrigin(v, origin)) { return true; } } } return false;}
function setAccessControlAllowOrigin( origin: OriginVerifier | OriginVerifier[], requestOrigin: string, req: ServerRequest,) { if (origin === "*") { req.responseHeaders.set("access-control-allow-origin", "*"); } else if (verifyOrigin(origin, requestOrigin)) { req.responseHeaders.set("access-control-allow-origin", requestOrigin); } else { return false; }}
function setAccessControlRequestMethods(methods: string[], req: ServerRequest) { const requestMethods = req.headers.get("access-control-request-methods"); if (requestMethods && methods.length > 0) { const list = requestMethods.split(",").map((v) => v.trim()); const allowed = list.filter((v) => methods.includes(v)); req.responseHeaders.set( "access-control-allow-methods", allowed.join(", "), ); }}
function setAcessControlExposeHeaders( exposedHeaders: string[], req: ServerRequest,) { if (exposedHeaders.length > 0) { req.responseHeaders.set( "access-control-expose-headers", exposedHeaders.join(", "), ); }}
function setAccessControlRequestHeaders( allowedHeaders: string[], req: ServerRequest,) { const requestHeaders = req.headers.get("access-control-request-headers"); if (requestHeaders && allowedHeaders.length > 0) { const list = requestHeaders.split(",").map((v) => v.trim()); const allowed = list.filter((v) => allowedHeaders.includes(v)); req.responseHeaders.set( "access-control-allow-headers", allowed.join(", "), ); }}