UNPKG

@duongtrungnguyen/nestro

Version:
134 lines 4.89 kB
import { URL } from "url"; import { SSL_PROTOCOL_REGEX, UPGRADE_HEADER_REGEX, WEBSOCKET_UPGRADE_REGEX, SOCKET_IO_PATH_REGEX, WEBSOCKET_UPGRADE_HEADER } from "./constants"; function setupOutgoing(outgoing = {}, options, req, forward) { const targetKey = forward || "target"; const target = options[targetKey]; if (!target) { return outgoing; } outgoing.port = getTargetPort(target); copyTargetProperties(outgoing, target); outgoing.method = req.method || "GET"; outgoing.headers = req.headers; if (SSL_PROTOCOL_REGEX.test(target.protocol || "")) { outgoing.rejectUnauthorized = options.secure !== void 0 ? options.secure : true; } outgoing.localAddress = options.localAddress; configureConnectionHeader(outgoing, req); const originalPath = req.url || "/"; const rewrittenPath = rewritePath(originalPath, req, options); outgoing.path = buildPath(target, rewrittenPath, options); if (options.changeOrigin) { const hostHeader = getHostHeader(outgoing); if (hostHeader) { outgoing.headers.host = hostHeader; } } return outgoing; } function getTargetPort(target) { if (target.port) { return target.port; } if (target.port) { return parseInt(target.port, 10); } return SSL_PROTOCOL_REGEX.test(target.protocol || "") ? 443 : 80; } function copyTargetProperties(outgoing, target) { const props = ["host", "hostname", "socketPath", "pfx", "key", "passphrase", "cert", "ca", "ciphers", "secureProtocol"]; for (const prop of props) { if (target[prop] !== void 0) { outgoing[prop] = target[prop]; } } } function configureConnectionHeader(outgoing, req) { const isWebSocket = req.headers.upgrade && WEBSOCKET_UPGRADE_REGEX.test(req.headers.upgrade.toLowerCase()) && req.headers.connection && UPGRADE_HEADER_REGEX.test(req.headers.connection.toLowerCase()); outgoing.headers = outgoing.headers || {}; if (isWebSocket) { if (!outgoing.headers.connection) { outgoing.headers.connection = "upgrade"; } } else if (!outgoing.agent && !outgoing.headers.connection) { outgoing.headers.connection = "close"; } } function buildPath(target, rewrittenPath, options) { let targetPath = ""; if (options.prependPath !== false) { targetPath = target.path || target.pathname || ""; } const url = new URL(rewrittenPath, "http://localhost"); const pathname = options.toProxy ? rewrittenPath : url.pathname; const queryString = options.toProxy ? "" : url.search; const fullPath = joinPaths(targetPath, pathname); return fullPath + queryString; } function rewritePath(path, req, options) { if (options.ignorePath) return ""; if (!options.pathRewrite) return path; for (const [pattern, replacement] of Object.entries(options.pathRewrite)) { const regex = new RegExp(pattern); if (regex.test(path)) { return typeof replacement === "function" ? replacement(path, req) : path.replace(regex, replacement); } } return path; } function getHostHeader(outgoing) { if (outgoing.port !== 80 && outgoing.port !== 443) { return `${outgoing.hostname || outgoing.host}:${outgoing.port}`; } return outgoing.hostname || outgoing.host; } function joinPaths(...paths) { if (!paths.length) return ""; const filteredPaths = paths.filter(Boolean); if (!filteredPaths.length) return ""; return filteredPaths.join("/").replace(/\/+/g, "/").replace(/http:\/|https:\//g, (match) => match.replace(":/", "://")); } function setupSocket(socket) { socket.setTimeout(0); socket.setNoDelay(true); socket.setKeepAlive(true, 0); return socket; } function hasEncryptedConnection(req) { const socket = req.socket; return Boolean(socket.encrypted || socket.pair); } function urlJoin(...args) { if (!args.length) return ""; const last = args[args.length - 1]; const [path, ...queryParts] = last.split("?"); const paths = [...args.slice(0, -1), path].filter(Boolean); const joinedPath = joinPaths(...paths); return queryParts.length > 0 ? `${joinedPath}?${queryParts.join("?")}` : joinedPath; } function rewriteCookieProperty(header, config, property) { if (Array.isArray(header)) { return header.map((h) => rewriteCookieProperty(h, config, property)); } const pattern = new RegExp(`(;\\s*${property}=)([^;]+)`, "i"); return header.replace(pattern, (match, prefix, previousValue) => { const newValue = previousValue in config ? config[previousValue] : config["*"]; return newValue ? `${prefix}${newValue}` : ""; }); } function isSocketIORequest(req) { return !!req.url && SOCKET_IO_PATH_REGEX.test(req.url); } function isSocketRequest(req) { return !!req.headers.upgrade && req.headers.upgrade.toLowerCase() === WEBSOCKET_UPGRADE_HEADER; } export { hasEncryptedConnection, isSocketIORequest, isSocketRequest, rewriteCookieProperty, setupOutgoing, setupSocket, urlJoin }; //# sourceMappingURL=utils.js.map