Skip to content

Implement the six maintainer decisions for 2.0 - #151

Merged
Astn merged 1 commit into
masterfrom
maintainer-decisions
Sep 25, 2026
Merged

Astn merged 1 commit into
masterfrom
maintainer-decisions

Conversation

@Astn

@Astn Astn commented Sep 25, 2026

Copy link
Copy Markdown
Owner

Implements the six maintainer decisions that came out of the documentation review (Linear AUS-974 to AUS-979). All of them land before 2.0.0 is pushed to NuGet, so nothing here changes a published package.

What changes

Exception disclosure (AUS-974). With Config.IncludeExceptionDetails off, an unhandled exception, or a result that cannot be written, is answered as -32603 with data: null. The type name and message are no longer sent. Redaction happens when the response is written, after the error handler ran, so a handler still sees the Exception in data and can author what the client gets. ExceptionInfo.ForResponse returns null when details are off. The Convert.ToString fallback in the envelope writer is covered too.

Sessions on the request path (AUS-975). Process/ProcessAsync use a new internal TryGetSessionHandler that never creates a session: last hit, then the thread's snapshot, then the master registry (so a registration that landed after the snapshot is found). An unknown id dispatches through one shared empty handler: -32601 for calls, parse errors, batches and notifications unchanged, no allocation per request. GetSessionHandler still creates, and now adds to the master before bumping the version, so no thread can hold a current-looking snapshot that lacks the entry.

Handler setters (AUS-976). Config.SetPreProcessHandler(sessionId, …) and Config.SetPostProcessHandler(sessionId, …); SetBeforeProcessHandler(sessionId, …) stays as an [Obsolete] alias. The session-less setters say "default session only" in their doc comments.

JsonRpcService(bool autoBind) (AUS-977). base(false) binds nowhere. The AspNetCore binder always binds the resolved instance to its effective session; the skip for subclasses on the default session is gone.

Written commitments (AUS-978). CHANGELOG.md (2.0.0 marked unreleased, 1.3.0 recorded), SECURITY.md, the README "Versioning and support" section, and every csproj's PackageReleaseNotes is a link to the changelog. Private vulnerability reporting is still disabled in the repository settings; SECURITY.md points at it, so please enable it before merging.

Package description and dependency (AUS-979). The core description says "no JSON library dependency". The session registry uses System.Collections.Concurrent.ConcurrentDictionary and the NonBlocking reference is dropped, so the core has no dependencies on net8.0 and net10.0. Measured with the new SessionRegistryBenchmarks (quiet and with a background thread registering and destroying sessions), --job short:

Row NonBlocking ConcurrentDictionary
Lookup_LastHit 2.3 ns 2.4 ns
Lookup_Rotating 14.1 ns 14.9 ns
Lookup_Unknown 100.9 ns 27.5 ns
RegisterLookupDestroy 22.0 µs 3.4 µs
RegisterLookupDestroy, churn 87.7 µs 5.9 µs
Process_Stable 237.7 ns 236.6 ns
Process_Stable, churn (median) 268 ns 263 ns

Tests

1156 tests pass on net8.0 and net10.0. New: redaction on all three serializers including result-serialization failures and the handler still seeing the exception; unknown sessions on both processors (calls, parse errors, batches, notifications) with the registry unchanged; registration after a miss and registration racing requests on four threads; the per-session setters and the alias; autoBind with explicit binding; a base(false) subclass bound only to the configured session by the host. Three existing tests that asserted the old disclosure now assert data: null and read the exception through an error handler or with details on.

benchmarks/charts/render.py --check and site/build.py pass.

- Exception disclosure: with IncludeExceptionDetails off, an unhandled
  exception (or a result that cannot be written) is -32603 with data:null;
  no type name, no message. Redaction happens at write time, after the error
  handler, which still sees the exception. ExceptionInfo.ForResponse returns
  null when details are off.
- Sessions: the request path uses a non-creating TryGetSessionHandler that
  falls through to the master registry on a snapshot miss; unknown ids
  dispatch through one shared empty handler and answer -32601. Registration
  adds to the master before publishing the version. GetSessionHandler still
  creates for binders and Config.
- Config: SetPreProcessHandler(sessionId, ...) and
  SetPostProcessHandler(sessionId, ...); SetBeforeProcessHandler stays as an
  obsolete alias; doc comments say the session-less setters are default
  session only.
- JsonRpcService(bool autoBind); the AspNetCore binder always binds the
  resolved instance to its effective session.
- Written commitments: CHANGELOG.md as the per-version record (NuGet release
  notes link to it), SECURITY.md, README versioning section.
- Core description says no JSON library dependency; the session registry
  uses System.Collections.Concurrent.ConcurrentDictionary and the NonBlocking
  reference is dropped, after SessionRegistryBenchmarks showed unknown-id
  lookups and register/destroy cycles faster and dispatch unchanged.

Tests cover redaction on all three serializers, the error handler seeing the
exception, unknown sessions on both processors, registration racing requests,
the per-session setters, autoBind and the host binding.
@Astn
Astn merged commit 160897a into master Sep 25, 2026
3 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.

1 participant