ReviewOS

stacks/stx

ctx.setCookie() from a handler emits nothing: cookiesToSet is drained before the handler runs

#1944
Closed glennmichael123 opened this 10 days ago ·
10 days ago

createContext in ssr.js gives handlers a setCookie(name, value, options). It appends to a local cookiesToSet array. That array is copied into responseHeaders as the last statement of createContext, before any handler has run:

setCookie(name, value, options = {}) {
  cookiesToSet.push(serializeCookie(name, value, options))
},
...
};                                   // <- ctx object literal ends here

if (cookiesToSet.length > 0)
  cookiesToSet.forEach((cookie) => {
    responseHeaders.append('Set-Cookie', cookie)
  })

return ctx                           // <- handlers run after this

Anything a handler pushes lands in an array that is never read again, so ctx.setCookie(...) from a route handler emits no Set-Cookie.

Why it looks like it works

The session cookie is pushed inside createContext itself, before the drain:

let sessionId = cookies[sessionCookieName]
if (!sessionId) {
  sessionId = generateSessionId()
  cookiesToSet.push(serializeCookie(sessionCookieName, sessionId, { httpOnly: true, ... }))
}

So sessions work, and cookies appear on responses, and the one call that is actually broken is the one an application makes. A smoke test that checks "does the server set cookies" passes.

Suggested fix

Drain at response-build time rather than at context-build time. Either append directly in setCookie:

setCookie(name, value, options = {}) {
  responseHeaders.append('Set-Cookie', serializeCookie(name, value, options))
},

which makes the array unnecessary, or keep the array and flush it after the handler chain resolves, where the Response is assembled.

The direct-append version looks strictly better here: responseHeaders is already a live Headers on the context, and Headers.append is what the existing drain calls anyway, so there is no ordering left to get wrong.

stx#1927 fixed the same class of problem on the page-action path (an action's cookies had no way out) and shipped in 0.2.177. This is the sibling case on the SSR handler path.

Environment

@stacksjs/stx 0.2.198, bun 1.3.14, macOS 15.

Sign in to comment on this issue.