class

Kemal::Session::Handler

Inherits HTTP::Handler < Reference < Object

Writes the session cookie once per request, at the last moment it can still be written.

Session mutations only mark the store dirty (Session#touch), so a route that sets twenty keys pays for one encryption instead of twenty. Something has to perform that one write, and this handler is it — mutating a request's session without it in the chain raises HandlerRequired:

use Kemal::Session::Handler.new
Kemal.run

Why it wraps the response output

Set-Cookie is a header, so it has to be decided before the first byte of the body reaches the socket. Crystal serializes the cookie jar into headers on the first unbuffered write and silently ignores cookies added afterwards, so committing after call_next would lose the session for any response big enough to flush mid-route (the output buffer is 8 KB — an ordinary HTML page).

So instead of buffering the body, the handler wraps response.output and commits just before the first write passes through: routes run to completion before Kemal prints their return value, and streaming routes commit at their first print. Responses with no body at all commit when the handler unwinds. Mutating the session after that point raises ResponseAlreadySent instead of quietly dropping the cookie.

NOTE: HTTP::Server::Response#upgrade writes its headers directly rather than through the output, so it is the one path this cannot intercept: a session mutated inside a WebSocket handshake has nowhere to put its cookie. Set the session in an ordinary route before opening the socket.

Instance methods

call(context : HTTP::Server::Context)
Source