Adds requeststate.go and threads a *requestState through the
ServeHTTP -> processAuthorizedRequestRS -> forwardAuthorized path.
rs is allocated once at the top of ServeHTTP, populates SessionData
field snapshots under a SINGLE sd.sessionMutex.RLock, and caches the
MetadataSnapshot. Downstream handlers read the cached fields instead
of calling session.GetX() / t.metadataSnap() repeatedly.
Why
---
Under Yaegi each method dispatch (including RWMutex.RLock) costs
~1-5ms of interpreter overhead. SessionData getters each take an
RLock on sd.sessionMutex; the previous hot path called 5-7 of them
per request (GetAuthenticated, GetAccessToken, GetIDToken,
GetRefreshToken, GetUserIdentifier, plus the same set again inside
processAuthorizedRequest). With one batched RLock + cached fields,
that drops to a single RLock for the whole handler chain.
This is scoped — not a wholesale architectural refactor:
* requestState is per-request (alloc at ServeHTTP entry, dropped on
return). It is NOT a shared cache and never escapes the request.
* The original processAuthorizedRequest is kept unchanged for any
callers we don't migrate this round (bearer path, callback
handlers, expired-token handlers). New code path is the RS-aware
processAuthorizedRequestRS, which middleware.ServeHTTP now uses for
the happy authenticated-and-not-needing-refresh case.
* Cross-request caches (tokenCache, JWKCache, sessionEntries,
sessionInvalidationCache) are unchanged. rs is additive, not a
replacement.
What this does NOT change
-------------------------
* The refresh path still calls session.GetX() in middleware.go
(handleExpiredToken, refreshToken, defaultInitiateAuthentication)
because those flows can mutate session state and a stale rs would
be wrong.
* validateStandardTokens still has its own session.GetX() calls.
Deep plumbing into the token-verification path is a follow-up.
* No semantic changes to authentication, refresh, or session
lifecycle — only the read path is optimised.
All tests pass with -race; golangci-lint clean.