@specprotected/spec-proxy-service-worker
Version:
Server Worker API implementation for integrating with Spec Proxy from an Edge Worker
139 lines • 5.47 kB
JavaScript
;
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