UNPKG

@sentry/core

Version:
136 lines (133 loc) • 5.44 kB
import { getClient } from '../currentScopes.js'; import { URL_PATH } from '@sentry/conventions/attributes'; import { escapeStringForRegex } from '../vendor/escapeStringForRegex.js'; import { makeDsn, dsnToString } from './dsn.js'; import { parseUrl } from './url.js'; const servedTunnels = /* @__PURE__ */ new WeakMap(); const SERVE_TUNNEL_INTEGRATION_NAME = "ServeTunnel"; function setServedTunnel(client, tunnelOptions, forward) { const allowedDsns = resolveAllowedDsns(tunnelOptions.allowedDsns, client.getDsn()); const servedTunnel = { paths: resolveTunnelPaths(tunnelOptions, allowedDsns), basePath: tunnelOptions.basePath, allowedDsns, forward }; servedTunnels.set(client, servedTunnel); return servedTunnel; } function getServeTunnelForwarder(client) { return client.getIntegrationByName?.(SERVE_TUNNEL_INTEGRATION_NAME)?.forward; } function getServedTunnel() { const client = getClient(); if (!client) { return void 0; } const servedTunnel = servedTunnels.get(client); if (servedTunnel) { return servedTunnel; } const tunnelOptions = client.getOptions()._managedTunnel; const forward = getServeTunnelForwarder(client); return tunnelOptions && forward ? setServedTunnel(client, tunnelOptions, forward) : void 0; } function applyTunnelDefaults(tunnel, defaults) { if (!tunnel || typeof tunnel === "string" || tunnel !== true && tunnel.path) { return tunnel; } const { basePath, ...rest } = defaults ?? {}; const normalizedBasePath = normalizeTunnelBasePath(basePath); const resolvedDefaults = normalizedBasePath ? { ...rest, basePath: normalizedBasePath } : rest; if (!Object.keys(resolvedDefaults).length) { return tunnel; } return { ...resolvedDefaults, ...tunnel === true ? {} : tunnel }; } function getTunnelPath(dsn) { const components = dsn ? makeDsn(dsn) : getClient()?.getDsn(); return components ? `/${fnv1a32(dsnToString(components)).toString(36)}` : void 0; } function getTunnelPaths() { return getServedTunnel()?.paths ?? []; } function resolveTunnelPaths(tunnelOptions, allowedDsns) { const fixedPath = tunnelOptions.path?.replace(/\/$/, ""); if (fixedPath) { return [fixedPath]; } const paths = allowedDsns.map((dsn) => getTunnelPath(dsn)); return [...new Set(paths.filter((path) => !!path))]; } function getTunnelIgnoreSpanFilters(tunnelOptions, paths) { if (!paths.length) { return []; } const basePath = escapeStringForRegex(normalizeTunnelBasePath(tunnelOptions.basePath)); const pathPattern = `${basePath}(?:${paths.map(escapeStringForRegex).join("|")})/?`; return [ // oxlint-disable-next-line sdk/no-regexp-constructor -- built from escaped config values, not end user input { attributes: { [URL_PATH]: new RegExp(`^${pathPattern}$`) } }, // oxlint-disable-next-line sdk/no-regexp-constructor -- built from escaped config values, not end user input { attributes: { "http.url": new RegExp(`^[a-z][a-z\\d+.-]*://[^/]+${pathPattern}(?:[?#].*)?$`) } } ]; } function normalizeTunnelBasePath(basePath) { const path = (basePath?.startsWith("./") ? basePath.slice(1) : basePath ?? "").replace(/\/$/, ""); return !path || path.startsWith("/") ? path : `/${path}`; } function resolveTunnelMatch(options) { const servedTunnel = !options && getServedTunnel(); if (servedTunnel) { return servedTunnel; } const allowedDsns = resolveAllowedDsns(options?.allowedDsns, getClient()?.getDsn()); return { paths: resolveTunnelPaths(options ?? {}, allowedDsns), basePath: options?.basePath, allowedDsns }; } function matchesTunnel(request, tunnel) { if (request.method !== "POST" || !request.url) { return false; } const path = stripBasePath(parseUrl(request.url).path || "/", tunnel.basePath); return path !== void 0 && tunnel.paths.includes(path.replace(/\/$/, "")); } function isTunnelRequest(request, options) { return matchesTunnel(request, resolveTunnelMatch(options)); } function isServedTunnelRequest(request) { const servedTunnel = getServedTunnel(); return !!servedTunnel && matchesTunnel(request, servedTunnel); } function handleServedTunnelRequest(request) { const servedTunnel = getServedTunnel(); if (!servedTunnel?.allowedDsns.length || !matchesTunnel(request, servedTunnel)) { return void 0; } return servedTunnel.forward(request, servedTunnel.allowedDsns); } function resolveAllowedDsns(allowedDsns, ownDsn) { const dsns = /* @__PURE__ */ new Set(); for (const components of [...(allowedDsns ?? []).map((dsn) => makeDsn(dsn)), ownDsn]) { if (components) { dsns.add(dsnToString(components)); } } return [...dsns]; } function stripBasePath(pathname, basePath) { const base = normalizeTunnelBasePath(basePath); if (!base) { return pathname; } return pathname === base || pathname.startsWith(`${base}/`) ? pathname.slice(base.length) : void 0; } function fnv1a32(input) { let hash = 2166136261; for (let i = 0; i < input.length; i++) { hash ^= input.charCodeAt(i); hash = Math.imul(hash, 16777619); } return hash >>> 0; } export { SERVE_TUNNEL_INTEGRATION_NAME, applyTunnelDefaults, getServeTunnelForwarder, getTunnelIgnoreSpanFilters, getTunnelPath, getTunnelPaths, handleServedTunnelRequest, isServedTunnelRequest, isTunnelRequest, matchesTunnel, normalizeTunnelBasePath, resolveAllowedDsns, resolveTunnelMatch, resolveTunnelPaths, setServedTunnel }; //# sourceMappingURL=tunnel.js.map