Skip to content

Keep the current session per request, not per store - #2

Merged
eliasjpr merged 2 commits into
masterfrom
claude/azu-crystal-framework-plan-phqt6e
Oct 22, 2026
Merged

eliasjpr merged 2 commits into
masterfrom
claude/azu-crystal-framework-plan-phqt6e

Conversation

@eliasjpr

Copy link
Copy Markdown
Contributor

Summary

Phase 0, item 12 of the plan (first part): requests shared one session through the store.

A Store is shared by every request but held the session in a single current_session field. Reproduced with a failing spec:

  1. Request 1 carries Alice's session cookie, so the store's current_session becomes Alice's.
  2. Request 2 has no cookie. load_from finds nothing to load, so current_session stays Alice's, and set_cookies answers with a Set-Cookie carrying Alice's session id.

So a visitor with no cookie was handed the previous user's session. Concurrent requests overwrote each other's session the same way.

Fix

  • Store keeps the current session per fiber (current_session, current_session=, release). The four stores no longer define their own field.
  • SessionHandler calls release when the request ends (ensure), so a keep-alive connection's next request starts fresh, unless its cookie names a stored session.

Specs

spec/request_isolation_spec.cr has two examples:

  • a visitor without a cookie does not get the previous visitor's session id (red before the change);
  • two concurrent requests, forced to interleave, each see their own session.

Full suite: 395 examples, 0 failures on Crystal 1.22.0. crystal tool format --check is clean.

Not in this PR

  • @flash is also one instance shared by all requests, so flash messages can reach another user. It needs to live in the session data, which changes the Flash API.
  • CookieStore#cookies is shared per-request state of the same kind.
  • regenerate_id on authentication, refusing the default secret outside development, and a real client binding (validate_session_binding is a stub) remain from the plan.

Generated by Claude Code

A store is shared by every request but held the session in one field.
A visitor with no cookie therefore kept the previous request's session
and was sent that session's id as a cookie, which hands over the
previous user's session. Concurrent requests overwrote each other the
same way.

Store now keeps the current session per fiber and SessionHandler
releases it when the request ends, so each request starts from a fresh
session unless its cookie names a stored one.

Flash messages and the cookie store's request cookies are still shared
state and are not changed here.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HT5gvYGR83U2M4DyA6NA6f
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Review found that the getter inserted a session for any fiber that read
it and only SessionHandler released, so fibers that read the session
outside a request (a spawned task, a WebSocket channel) were kept for
good. Inserting now drops entries of fibers that have finished.

A fiber spawned during a request still starts with a blank session; the
store documents that it must capture the session before spawn.

The release spec now materializes the session before raising, so it
fails without the ensure, and the cookie spec asserts the cookie exists.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HT5gvYGR83U2M4DyA6NA6f
@eliasjpr
eliasjpr merged commit 7bb5c56 into master Oct 22, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants