@specprotected/spec-proxy-aws-edge-lambda
Version:
Spec Proxy integration with AWS Edge@Lambda
179 lines • 6.92 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.specProxyProcessResponse = exports.specProxyProcessRequest = void 0;
const cookie_1 = require("cookie");
const node_fetch_1 = __importStar(require("node-fetch"));
const uuid_1 = require("uuid");
const url_1 = require("url");
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 SPEC_INTERNAL = "spec-internal.com";
async function specProxyProcessRequest(event, config = {}) {
const request = event.Records[0].cf.request;
const host = request.headers[HEADER_HOST][0].value;
const headers = request.headers;
const uri = request.uri;
const query = request.querystring ? `?${request.querystring}` : "";
const url = new url_1.URL(`https://${host}${uri}${query}`);
const bodyData = request.body?.data
? Buffer.from(request.body.data, "base64").toString("utf-8")
: undefined;
if (config.disableSpecProxy) {
return request;
}
let originalHost = url.hostname;
let specUrl = new url_1.URL(url.toString());
specUrl.hostname = `${host}.${SPEC_INTERNAL}`;
if (!shouldHandleRequest(url, headers, config)) {
return request;
}
if (config.customerKey) {
request.headers[`${SPEC_HEADER_CUSTOMER_KEY}`] = [
{ key: `${SPEC_HEADER_CUSTOMER_KEY}`, value: config.customerKey },
];
}
if (config.inlineMode ||
(!config.disableSpecTraffic &&
specUrl.pathname.startsWith(SPEC_PATH_PREFIX))) {
request.origin = {
custom: {
domainName: specUrl.hostname,
protocol: "https",
port: 443,
path: "",
sslProtocols: ["TLSv1.2"],
readTimeout: 5,
keepaliveTimeout: 5,
customHeaders: {},
},
};
request.headers["host"] = [{ key: "host", value: specUrl.hostname }];
request.headers[`${SPEC_HEADER_FORWARD_ORIGIN}`] = [
{ key: `${SPEC_HEADER_FORWARD_ORIGIN}`, value: originalHost },
];
return request;
}
else {
const body = bodyData;
const specHeaders = new node_fetch_1.Headers();
for (const key in headers) {
specHeaders.append(key, headers[key][0].value);
}
specHeaders.set("Host", specUrl.hostname);
const requestInit = {
body,
headers: specHeaders,
method: request.method,
};
const specRequest = new node_fetch_1.Request(specUrl.toString(), requestInit);
await (0, node_fetch_1.default)(specRequest);
return request;
}
}
exports.specProxyProcessRequest = specProxyProcessRequest;
function specProxyProcessResponse(event, config = {}) {
const response = event.Records[0].cf.response;
const headers = response.headers;
const cookieHeader = headers.hasOwnProperty(HEADER_COOKIE)
? headers[HEADER_COOKIE][0].value
: "";
const request = event.Records[0].cf.request;
const host = request.headers[HEADER_HOST][0].value;
const uri = request.uri;
const url = new url_1.URL(`https://${host}${uri}`);
if (config.disableSpecProxy ||
config.inlineMode === true ||
!shouldHandleRequest(url, headers, config)) {
return response;
}
let cookies = {};
if (cookieHeader.length) {
cookies = (0, cookie_1.parse)(cookieHeader);
}
if (!cookies[SPEC_COOKIE_ID]) {
let specId = (0, uuid_1.v4)();
let setCookie = (0, cookie_1.serialize)(SPEC_COOKIE_ID, specId, {
maxAge: 320000000,
domain: extractTopLevelDomain(request.headers) || undefined,
path: "/",
sameSite: "none",
secure: true,
});
let set_cookie_headers = response.headers[HEADER_SET_COOKIE];
let set_cookie_header = { key: HEADER_SET_COOKIE, value: setCookie };
if (set_cookie_headers) {
set_cookie_headers.push(set_cookie_header);
}
else {
set_cookie_headers = [set_cookie_header];
}
response.headers[HEADER_SET_COOKIE] = set_cookie_headers;
}
return response;
}
exports.specProxyProcessResponse = specProxyProcessResponse;
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;
}
const headerVal = headers.hasOwnProperty(HEADER_X_FORWARDED_FOR)
? headers[HEADER_X_FORWARDED_FOR][0].value
: "99";
let ip_octet_sum = headerVal
.split(".")
.map((octet) => parseInt(octet))
.reduce((acc, n) => acc + n, 0);
if (isNaN(ip_octet_sum)) {
ip_octet_sum = 99;
}
return ip_octet_sum % 100 < config.percentageOfIPs;
}
function extractTopLevelDomain(headers) {
let host = headers.hasOwnProperty(HEADER_HOST)
? headers[HEADER_HOST][0].value
: "";
if (host.length) {
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 (matches?.groups?.domain) {
host = matches.groups.domain;
}
}
return host;
}
//# sourceMappingURL=index.js.map