@@ -15,6 +15,10 @@ export interface HandleTunnelRequestOptions {
1515 allowedDsns ?: Array < string > ;
1616}
1717
18+ // Servers derive the paths on every POST, so parse and hash each DSN once. This also keeps an invalid DSN in
19+ // `allowedDsns` from logging on every request. Keys come from config, never from requests, so the map stays small.
20+ const tunnelPathCache = new Map < string , string | undefined > ( ) ;
21+
1822/**
1923 * Splits the `tunnel` option a server SDK's `init` received. A URL stays the transport tunnel for the server's own
2024 * envelopes. `true` or {@link TunnelOptions} enables serving the managed tunnel instead.
@@ -37,9 +41,17 @@ export function resolveServerTunnelOption(tunnel: string | true | TunnelOptions
3741 * between them. The path is a hash so it carries nothing an ad-blocker filter list could match on.
3842 */
3943export function getTunnelPath ( dsn : DsnLike ) : string | undefined {
44+ if ( typeof dsn === 'string' && tunnelPathCache . has ( dsn ) ) {
45+ return tunnelPathCache . get ( dsn ) ;
46+ }
47+
4048 const components = makeDsn ( dsn ) ;
49+ const path = components ? `/${ fnv1a ( dsnToString ( components ) ) . toString ( 36 ) } ` : undefined ;
50+ if ( typeof dsn === 'string' ) {
51+ tunnelPathCache . set ( dsn , path ) ;
52+ }
4153
42- return components ? `/ ${ fnv1a ( dsnToString ( components ) ) . toString ( 36 ) } ` : undefined ;
54+ return path ;
4355}
4456
4557/**
@@ -193,7 +205,12 @@ function stripBasePath(pathname: string, basePath: string | undefined): string {
193205 return pathname === base || pathname . startsWith ( `${ base } /` ) ? pathname . slice ( base . length ) : pathname ;
194206}
195207
196- /** 32-bit FNV-1a. Synchronous, so the browser can derive the path before its client is constructed. */
208+ /**
209+ * 32-bit FNV-1a, because the browser needs the path synchronously in `init` (`crypto.subtle.digest` is async) and it
210+ * runs in every runtime without dependencies. It needn't be cryptographic since the DSN is public.
211+ *
212+ * Browser and server SDKs compute the path separately, so changing the algorithm breaks mixed SDK versions.
213+ */
197214function fnv1a ( input : string ) : number {
198215 let hash = 0x811c9dc5 ;
199216 for ( let i = 0 ; i < input . length ; i ++ ) {
0 commit comments