- kaya-core: WebSocket ABC gains session attribute and accept(headers=...) - kaya-core: AsgiWebSocket injects headers into websocket.accept message - kaya-rsgi: RsgiWebSocket accepts headers param (ignored — Granian's accept() takes no args) - kaya-session: SessionWebSocket wrapper exposes ws.session and injects Set-Cookie on accept() - kaya-session: SessionMixin registers before/after websocket hooks; session loaded at connect, persisted on close if modified - 10 new WV session tests covering read, persist, handshake cookie, regenerate, invalidate, isolation - Example and README updated
kaya-session
Session management for the Kaya web framework.
Provides server-side, identity-agnostic HTTP sessions via a session cookie. The
session data is accessible from request handlers as ctx.session.
Usage
from kaya.core import KayaApp, HttpContext
from kaya.session import SessionMixin, InMemorySessionStore
session = SessionMixin(InMemorySessionStore())
app = KayaApp(mixins=[session])
@app.GET('/')
async def home(ctx: HttpContext):
n = ctx.session.get('visits', 0) + 1
ctx.session['visits'] = n
await ctx.send_str(200, f'visits: {n}')
Sessions are created lazily: a cookie is only set when the handler modifies the session.
SessionMixin is a KayaMixin, so the app stays a KayaApp and both ASGI and
RSGI keep working.
WebSocket sessions
The same session is available in websocket handlers as ws.session:
@app.websocket('/ws/visits')
async def ws_visits(ws: WebSocket):
visits = ws.session.get('visits', 0) + 1
ws.session['visits'] = visits
await ws.accept()
await ws.send_text(f'visits: {visits}')
The session is loaded from the cookie when the connection is opened and
persisted when the connection closes, if it was modified. The session cookie
can only be set or refreshed on the handshake response, so mutate the session
before calling ws.accept() if you want the cookie delivered with the
handshake. Handshake cookies require ASGI spec version 2.1+; RSGI websocket
handshakes cannot carry response headers, so on RSGI the session is loaded and
persisted but the cookie is only set or refreshed by HTTP responses.
Session expiry
The cookie sent to the browser has a Max-Age (default 14 days), but that is
only a client-side hint. The real boundary is the store's server-side TTL,
which the mixin keeps in sync with the cookie Max-Age.
For InMemorySessionStore, a session expires if it is idle for longer than
max_age. Active sessions have their expiry slid forward on every access, so
a user that keeps visiting stays logged in. If the client ignores the cookie's
Max-Age and replays an old cookie value, the store rejects the expired
session and creates a fresh empty one.
Set max_age=None to disable server-side expiry (and the Max-Age cookie
attribute) entirely.
Features
Session: dict-like session object with modification trackingSessionStore: abstract store interfaceInMemorySessionStore: simple in-memory store for development/single-processSessionMixin: composable Kaya mixin managing session cookies and persistence- Session ID regeneration (
session.regenerate_id()) and invalidation (session.invalidate()) for authentication layers - WebSocket support: the session is exposed as
ws.sessionin websocket handlers, loaded at connect time and persisted on close
Notes
InMemorySessionStoredoes not survive process restarts and is not shared across processes. Production deployments should use a store backed by a shared storage system (planned).