UNPKG

@specprotected/spec-proxy-service-worker

Version:

Server Worker API implementation for integrating with Spec Proxy from an Edge Worker

139 lines 5.47 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.specProxyProcessRequest = specProxyProcessRequest; exports.specProxyProcessResponse = specProxyProcessResponse; exports.specProxyProcess = specProxyProcess; exports.specMakeRequestWithFallback = specMakeRequestWithFallback; const cookie_1 = require("cookie"); const SPEC_PATH_PREFIX = "/spec_traffic"; const SPEC_HEADER_FORWARD_ORIGIN = "x-spec-forward-origin"; const SPEC_COOKIE_ID = "x-spec-id"; const SPEC_HEADER_CUSTOMER_KEY = "x-spec-customer-authorization"; const HEADER_SET_COOKIE = "set-cookie"; const HEADER_COOKIE = "cookie"; const HEADER_HOST = "host"; const HEADER_X_FORWARDED_FOR = "x-forwarded-for"; const HEADER_SPEC_ACTIVITY = "x-atvak-activity-count"; function specProxyProcessRequest(event, config = {}, requestConstructor = Request) { if (config.disableSpecProxy) { return event.request; } if (!shouldHandleRequest(new URL(event.request.url), event.request.headers, config)) { return event.request; } let request = event.request; let specUrl = new URL(request.url); let originalHost = specUrl.hostname; specUrl.hostname = config.domainOverride ? `${config.domainOverride}.spec-internal.com` : `${originalHost}.spec-internal.com`; let specHeaders = new Headers(request.headers); if (config.customerKey) { specHeaders.set(SPEC_HEADER_CUSTOMER_KEY, config.customerKey); } if (config.inlineMode || (!config.disableSpecTraffic && specUrl.pathname.startsWith(SPEC_PATH_PREFIX))) { specHeaders.set(SPEC_HEADER_FORWARD_ORIGIN, originalHost); return new requestConstructor(specUrl.toString(), { body: request.body, headers: specHeaders, method: request.method, redirect: request.redirect, }, request, config); } else { let teedBody = [null, null]; if (request.body !== null && request.body !== undefined) { teedBody = request.body.tee(); } const specRequest = new requestConstructor(specUrl.toString(), { body: teedBody[0], headers: specHeaders, method: request.method, redirect: request.redirect, }, request, config); event.waitUntil(fetch(specRequest)); return new requestConstructor(request.url, { body: teedBody[1], headers: request.headers, method: request.method, redirect: request.redirect, }, request, config); } } function specProxyProcessResponse(request, response, config = {}) { if (config.disableSpecProxy || config.inlineMode === true || !shouldHandleRequest(new URL(request.url), request.headers, config)) { return response; } let cookies = {}; if (request.headers) { cookies = (0, cookie_1.parse)(request.headers.get(HEADER_COOKIE) || ""); } if (!cookies[SPEC_COOKIE_ID]) { let specId = crypto.randomUUID(); let setCookie = (0, cookie_1.serialize)(SPEC_COOKIE_ID, specId, { maxAge: 320000000, domain: extractTopLevelDomain(request.headers) || undefined, path: "/", sameSite: "none", secure: true, }); response = new Response(response.body, response); response.headers.append(HEADER_SET_COOKIE, setCookie); } return response; } async function specProxyProcess(event, config, requestConstructor) { let request = specProxyProcessRequest(event, config, requestConstructor); let response = await specMakeRequestWithFallback(event, request, config); return specProxyProcessResponse(request, response, config); } async function specMakeRequestWithFallback(event, request, config) { if (config.inlineMode) { let response = await fetch(request); if (response.headers.get(HEADER_SPEC_ACTIVITY)) { let newResponse = new Response(response.body, response); newResponse.headers.delete(HEADER_SPEC_ACTIVITY); return newResponse; } return await fetch(event.request); } else { return await fetch(request); } } function shouldHandleRequest(url, headers, config) { if (!config.disableSpecTraffic && url.pathname.startsWith(SPEC_PATH_PREFIX)) { return true; } if (config.percentageOfIPs === undefined || config.percentageOfIPs >= 100) { return true; } else if (config.percentageOfIPs <= 0) { return false; } let ip_octet_sum = (headers.get(HEADER_X_FORWARDED_FOR) || "99") .split(".") .map((octet) => parseInt(octet)) .reduce((acc, n) => acc + n); if (isNaN(ip_octet_sum)) { ip_octet_sum = 99; } return ip_octet_sum % 100 < config.percentageOfIPs; } function extractTopLevelDomain(headers) { var _a; let host = headers.get(HEADER_HOST); if (host) { const domain_extract = /^(.*?\.)?(?<domain>[^.]+\.(com|co|org|edu|net|int|gov|mil|uk|co\.uk|ac\.uk|gov\.uk|ltd\.uk|me\.uk|net\.uk|nhs\.uk|org\.uk|plc\.uk|police\.uk))(\.spec-internal\.com)?$/; let matches = domain_extract.exec(host); if ((_a = matches === null || matches === void 0 ? void 0 : matches.groups) === null || _a === void 0 ? void 0 : _a.domain) { host = matches.groups.domain; } } return host; } //# sourceMappingURL=index.js.map