This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch feature/WW-3576-session-map-staleness
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 04d8d18a58758228a6d4f2c93312f06067296849
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sun Oct 18 10:26:34 2026 +0200

    WW-3576 docs: detach the request SessionMap at request end
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../specs/2026-10-18-WW-3576-session-map-staleness-design.md     | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git 
a/docs/superpowers/specs/2026-10-18-WW-3576-session-map-staleness-design.md 
b/docs/superpowers/specs/2026-10-18-WW-3576-session-map-staleness-design.md
index faa2ff1be..abf454b6c 100644
--- a/docs/superpowers/specs/2026-10-18-WW-3576-session-map-staleness-design.md
+++ b/docs/superpowers/specs/2026-10-18-WW-3576-session-map-staleness-design.md
@@ -61,7 +61,14 @@ when that session dies (request threads), and one map that 
is stale *by design*
 - **Invalidation is detected, not hidden:** an `IllegalStateException` from 
the pinned session means it
   was invalidated (by another request or directly); the map un-pins it and 
answers as "no session"
   (`null` / `false` / empty set / no-op). A later `put` obtains a session 
through
-  `request.getSession(true)` — the same path, and the same trust in the 
request, as a first `put` today.
+  `request.getSession(true)` — **only while the request is live**.
+- **Detached at request end (decided after the final review):** the request 
cleanup in the dispatcher
+  marks the request's `SessionMap` detached. A detached map never calls its 
request again: with a live
+  pinned session it keeps working (async `Callable`s, stored `SessionAware` 
fields); a `put` with no live
+  pinned session throws `IllegalStateException`. Reason: a background thread 
(sandbox escape, custom
+  `BackgroundProcess`) reaching `request.getSession(true)` after the request 
was recycled would write
+  into another user's session. This also closes the same path for a 
never-pinned map used after its
+  request ended, which predates this change. Detach is package-private.
 - The pinned reference is read once per call into a local (no 
check-then-reread), so a concurrent
   `invalidate()` on the same map cannot NPE; how it is published (`volatile`) 
is the builder's.
 - **No `entries` cache:** `entrySet()` is built fresh on every call; an 
entry's `setValue` writes

Reply via email to