The four packages (AustinHarris.JsonRpc, AustinHarris.JsonRpc.Newtonsoft, AustinHarris.JsonRpc.SystemTextJson,
AustinHarris.JsonRpc.AspNetCore) share one version number and are released together. This file is the record
of what changed in each version; Upgrading from 1.x explains how to move a 1.x server, and
the package pages on NuGet link here.
Versions follow Semantic Versioning for the public API and the documented wire behaviour: a breaking change to either means a new major version.
ServiceBinder.BindInterfaceregisters interface trees atomically, with contract naming, filtering, defaults and ownership-aware disposal (RpcBinding).ServiceBinder.BindMethodregisters any delegate as a method without attributes or a service class.JsonRpcProcessor.ProcessAsyncawaitsTaskandValueTaskmethods with typed result writing, sequential batches and cooperative cancellation;[JsonRpcCancellation]injects the processor's token.RpcContextFlowon[JsonRpcMethod]: the ambient context does not flow across awaits by default (None);Flowopts a method in.- Byte-first pipeline:
ReadOnlySequence<byte>/ReadOnlyMemory<byte>/ReadOnlySpan<byte>in,IBufferWriter<byte>out; parameters bind straight from the request bytes through compiled invokers. The string overloads remain. - Pluggable serializers (
AustinHarris.JsonRpc.Serialization.JsonRpcSerializer): the built-in jsmn serializer is the default; Json.NET and System.Text.Json ship as the.Newtonsoftand.SystemTextJsonpackages. AustinHarris.JsonRpc.AspNetCore:MapJsonRpcendpoint (PipeReaderin,BodyWriterout), a raw KestrelConnectionHandler, DI registration (AddJsonRpcService<T>,AddJsonRpcServicesFromAssembly),EnableAsyncMethodsfor asynchronous HTTP and ordered raw-connection processing.- The request id is available inside a method (
Handler.RpcRequestId,JsonRpcContext.CurrentRequestId, kind and raw bytes), read on demand at no cost to methods that do not ask. Config.SetPreProcessHandler(sessionId, …)andConfig.SetPostProcessHandler(sessionId, …), symmetric with the default-session setters.Config.SetBeforeProcessHandler(sessionId, …)remains as an obsolete alias.ServiceBinder.BindService(sessionId, serviceType, resolve)binds a type whose instance is resolved per call from the RPC context: the seam for container lifetimes, with no container dependency in the core.AddJsonRpcService<T>(ServiceLifetime, sessionId)and the matchingAddJsonRpcServicesFromAssemblyoverload: scoped and transient services are resolved per call fromHttpContext.RequestServices, or from a scope the raw connection handler opens per document and publishes asIServiceProvidersFeature; a batch shares one scope.JsonRpcOptions.ServiceProviderSelectorlocates the provider from a custom context. A lifetime that conflicts with the container's registration, a non-singletonJsonRpcServicesubclass, and aContextFactorywithout a selector are refused at registration or startup.protected JsonRpcService(bool autoBind): a subclass constructed withbase(false)binds itself nowhere, for services that a host or an explicitBindServicecall binds.SECURITY.md(private vulnerability reporting) and this changelog.TestServer_Console --scaleis the release gate for theProcessAsyncpath. It measures the inline rows at 1, 2 and N workers in three paired runs, takes the medians and fails when N/1 is below the threshold.--kestrel [seconds] asyncruns the host withEnableAsyncMethods = true. The README adds--asyncrows forProcessAsyncat 1 and 16 workers. The 1.x string overloads' thread-pool benchmark is now thetmenu entry and no longer the default.
RpcMethod.FromMethodisRpcMethod.FromMethodInfo;RpcInterfaceMethod.MethodisRpcInterfaceMethod.MethodInfo. AMethodInfois always spelled out; "method" means the JSON-RPC method.- The session parameter is spelled
sessionIdon every overload (BindService,Handler.RegisterInstanceand theJsonRpcServiceconstructor usedsessionID). Handler.RegisterFuctionandUnRegisterFunctionare obsolete; useServiceBinder.BindMethodandUnbindMethod(which throw on a duplicate name instead of replacing it).- The core no longer depends on Json.NET.
ProcessAsyncno longer serializes the process on one lock per document. Each thread now caches one async scratch (input copy, reader, staged output) in front of the shared pool, which handles only misses and overflow. At 16 workers, the inline rows went from about 4 M to 22.2 M to 32.1 M RPC/s across the registrations, and the row with a real suspension from 3.9 M to 8.96 M (one run per row, 2026-09-25). The library retains one scratch per thread that has runProcessAsyncplus 64 shared, with buffers at most 64 KiB each.- The request path looks sessions up without creating them. A request for a session id that was never registered answers
-32601for every call and leaves the registry untouched; sessions are created by binding and by the per-sessionConfigsetters. Registration adds the session before publishing the registry version, so a thread that misses its snapshot consults the master registry and cannot answer-32601for a session that exists. - The AspNetCore host binds every registered service,
JsonRpcServicesubclasses included, to its effective session (the registration's session, thenJsonRpcOptions.SessionId, then the default). It no longer skips a subclass on the default session. - The core package's description says "no JSON library dependency" instead of "no dependencies". The session registry uses the framework's
ConcurrentDictionary; theNonBlockingpackage reference is gone, so the core has no dependencies onnet8.0andnet10.0(measured withSessionRegistryBenchmarks: unknown-id lookups and register/destroy cycles got faster, stable lookups and dispatch are unchanged). SMD.Servicesis anSMDServiceCollection; every mutation through it updates the dispatch table at once.SMD.Typesis a process-wide registry.- The
jsonrpcmember is checked (Config.VersionPolicy, defaultLenient): a missing member is accepted,"jsonrpc":"1.0"or a non-string value is-32600. - A parameter value the serializer cannot convert is
-32602with structured data naming the parameter (it was-32603);-32601names the requested method in its data. - Named parameters are checked against the method's parameter list: an unknown or repeated name is
-32602. - Batches: the empty-batch error is
-32600; a batch made only of notifications produces nothing; a batch always answers with an array when it produces at least one response. - Notifications never get a wire response, whatever their outcome.
- Dates and non-finite numbers are written the same way by every serializer (fraction only when non-zero,
Z/offset/nothing byKind;NaNand the infinities as quoted strings).
- The
JsonSerializerSettingsoverloads ofJsonRpcProcessor.Process*(pass a serializer instead; the Newtonsoft package has settings-based helpers). - Json.NET attributes on
JsonRequest,JsonResponseandJsonRpcException. - The 1.x projects that 2.0 did not build:
AustinHarris.JsonRpc.Client,AustinHarris.JsonRpc.AspNet, the Windows Phone 7 client,JsonRpcTestandTestClient. They were .NET Framework 4.0packages.configprojects outside the solution, referencing packages with open advisories; their source is in the git history before 2.0.
- A trailing notification in a batch no longer leaves a dangling comma.
async voidmethods are rejected at registration.- A method registered with
RpcContextFlow.Flowthat suspends under a pre- or post-processing hook and completes on another thread no longer gives the completing thread the starting thread's frame. Previously, the two threads then shared one frame, soRpcContext,RpcRequestIdandRpcSetExceptionon either could read or clear the other's state during concurrent dispatch. The new cross-threadProcessAsynctest found this bug. The frame is now restored through the ambient value alone.
- With
Config.IncludeExceptionDetailsoff (the default), an unhandled exception is answered as-32603withdata: null: the exception's type name and message are no longer sent. Error handlers still receive the exception itself and can author what the client sees. The same applies to an exception thrown while writing a result.ExceptionInfo.ForResponsereturns null when details are off. - The session registry no longer grows from untrusted session ids on the request path (see Changed).
- Targets
netstandard2.0,netstandard2.1,net8.0andnet10.0(drops the end-of-lifenetcoreapp3.1;netstandard2.0still covers it). - Newtonsoft.Json 13.0.4 (fixes GHSA-5crp-9r3c-p9vr in 12.0.3).
- Lock-free session handler registry.
- Packaging moved fully to the SDK-style project files: MIT license expression, README in the package, repository metadata.
- Closes out the .NET Standard work from PR #90 / issue #89.
Earlier versions were published without a changelog; their history is in the repository's commit log.