Cookie jar Proxy allocates a fresh Cookie object on every property access; parseCookie is unconditionally async even without signing
Repo: elysiajs/elysia
Location: src/cookies.ts:355-368 (Proxy get trap in createCookieJar) and src/cookies.ts:373 (async parseCookie), reached per request from src/compose.ts:743 and src/dynamic-handle.ts:512
Severity: medium · Confidence: 0.78
Type: hot-path-by-call-frequency
Description
createCookieJar in src/cookies.ts:348-371 returns a Proxy whose get trap constructs a new Cookie plus an Object.assign({}, initial, store[key]) on EVERY property access — reading the same cookie k times in one request allocates k Cookie instances with no memoization. This is a cross-module hot path: the AOT code generated by composeHandler (src/compose.ts:743, 'c.cookie=await parseCookie(...)') and the dynamic handler (src/dynamic-handle.ts:512) both route every cookie-touching request through it. Additionally parseCookie (src/cookies.ts:373) is declared async although await is only needed for signed cookies (unsignCookie/WebCrypto), so every cookie-enabled route pays a promise allocation and microtask hop per request even in the common unsigned case, and dynamic-handle's response-validation path Object.entries(context.cookie) (dynamic-handle.ts:609) triggers one allocation per cookie through the same trap.
Benchmark
Per-request time grows ~O(N) with the number of reads of the SAME single cookie inside one handler, because each Proxy get allocates a new Cookie + Object.assign; with a cached Cookie instance the curve would be nearly flat (only loop overhead). Expect ratio at N=500 to track the O(N) prediction (~50x) rather than O(1).
Observed complexity: O(N)
N_reads=10: 7.04ms (ratio 1.00), N_reads=50: 16.54ms (ratio 2.35), N_reads=100: 28.76ms (ratio 4.09), N_reads=500: 137.38ms (ratio 19.52). O(N) prediction at N=500 would be 50x; observed is 19.52x — clearly non-flat but sub-linear due to V8 optimization.
benchmark confirmed
Cookie jar Proxy allocates a fresh Cookie object on every property access; parseCookie is unconditionally async even without signing
Repo:
elysiajs/elysiaLocation: src/cookies.ts:355-368 (Proxy get trap in createCookieJar) and src/cookies.ts:373 (async parseCookie), reached per request from src/compose.ts:743 and src/dynamic-handle.ts:512
Severity: medium · Confidence: 0.78
Type: hot-path-by-call-frequency
Description
createCookieJar in src/cookies.ts:348-371 returns a Proxy whose get trap constructs a new Cookie plus an Object.assign({}, initial, store[key]) on EVERY property access — reading the same cookie k times in one request allocates k Cookie instances with no memoization. This is a cross-module hot path: the AOT code generated by composeHandler (src/compose.ts:743, 'c.cookie=await parseCookie(...)') and the dynamic handler (src/dynamic-handle.ts:512) both route every cookie-touching request through it. Additionally parseCookie (src/cookies.ts:373) is declared async although await is only needed for signed cookies (unsignCookie/WebCrypto), so every cookie-enabled route pays a promise allocation and microtask hop per request even in the common unsigned case, and dynamic-handle's response-validation path Object.entries(context.cookie) (dynamic-handle.ts:609) triggers one allocation per cookie through the same trap.
Benchmark
Per-request time grows ~O(N) with the number of reads of the SAME single cookie inside one handler, because each Proxy get allocates a new Cookie + Object.assign; with a cached Cookie instance the curve would be nearly flat (only loop overhead). Expect ratio at N=500 to track the O(N) prediction (~50x) rather than O(1).
Observed complexity: O(N)
benchmark confirmed