Skip to main content
Version: 4.x

Configuration and limits

Configure DarkWsOptions through AddDarkWs, services.Configure<DarkWsOptions>, configuration binding, or PostConfigure; they run in the standard Options order:

builder.Services.AddDarkWs(options => {
options.MaxMessageSizeBytes = 256 * 1024;
options.MaxConcurrentRequestsPerConnection = 8;
options.ShutdownTimeout = TimeSpan.FromSeconds(5);
});

builder.Services.Configure<DarkWsOptions>(builder.Configuration.GetSection("DarkWs"));

Options are validated when first resolved and on host startup; invalid values throw OptionsValidationException. DarkWS reads them through IOptions<DarkWsOptions>, so values are fixed once they are first resolved; changing configuration at runtime does not reconfigure DarkWS.

Options​

OptionDefaultMeaning
MaxMessageSizeBytes1 MiBLargest complete incoming message, all fragments included
JsonOptionsJsonSerializerDefaults.WebSerialization of envelopes and payloads
MaxConcurrentRequestsPerConnection16Actions running at once on one connection
KeepAliveInterval30 sTransport keep-alive interval
KeepAliveTimeout30 sTransport PONG deadline (.NET 9 and later)
ReceiveIdleTimeout2 minPending read deadline (.NET 8)
SendTimeout30 sSend lock wait plus socket write
BroadcastSendTimeout10 sPer-recipient broadcast write deadline
ShutdownTimeout10 sHandlers, middleware close hooks, and close handshake on shutdown
AuthenticationQueryParametertokenQuery parameter passed to the authenticator on upgrade
InvalidActionErrordarkws:error:invalid-actionCode for unknown actions
InvalidRequestErrordarkws:error:invalid-requestCode for malformed requests and payloads
AuthorizationRequiredErrordarkws:error:authorization-requiredCode for anonymous calls to protected actions
RequestFailedErrordarkws:error:request-failedCode for unexpected failures
AuthenticationFailedErrordarkws:error:authentication-failedKept for source compatibility; not used, since auth: always replies auth:failed

All timeouts must be positive and at most 4294967294 milliseconds. MaxMessageSizeBytes and MaxConcurrentRequestsPerConnection must be positive, JsonOptions must not be null, and AuthenticationQueryParameter and the error codes must not be blank.

Message size​

MaxMessageSizeBytes is checked before JSON parsing and covers all fragments of a message, including authentication commands. A message exactly at the limit is accepted. A larger one closes the connection with status 1009 (Message Too Big) without dispatching the partial message. Raise the limit only if the application really sends large messages; memory per connection grows with it.

Request concurrency and backpressure​

Each connection runs up to MaxConcurrentRequestsPerConnection requests at once. When that many are running, the connection stops reading until one of them finishes. No request is rejected and no busy error is sent; the client's messages wait in the socket and network buffers instead.

  • Text ping, auth:, and logout are read by the same loop, so they wait too. Under sustained load a client's pong timeout can expire.
  • auth: and logout are processed in the order they are read, without waiting for running requests.

When sizing limits, budget memory for running requests, each up to MaxMessageSizeBytes, times the expected number of connections.

DarkWS bounds work inside one connection only. Enforce the total number of connections and per-user or per-IP limits in the host or reverse proxy.

Liveness​

DarkWS detects dead peers differently per runtime:

  • .NET 9 and 10: the server sends transport PINGs every KeepAliveInterval and aborts the connection when no PONG arrives within KeepAliveTimeout.
  • .NET 8: every pending socket read is bounded by ReceiveIdleTimeout, reset by each received fragment. Idle clients must send application traffic within that time. The DarkWS clients send text ping every 30 seconds by default. Transport PONGs do not count. The timer runs only while a read is pending, so it is paused while a saturated connection stops reading.

The .NET client also detects a dead server with its own pong timeout.

Sending and shutdown​

  • SendTimeout covers waiting for the connection's send lock and writing to the socket. On expiry the socket is aborted.
  • Broadcasts use the shorter BroadcastSendTimeout per recipient, so a slow client cannot hold up a broadcast for long.
  • On shutdown the connection leaves storage immediately and handler tokens are cancelled. ShutdownTimeout is one shared deadline for pending handlers, middleware close hooks, and the close handshake; if the close handshake does not finish in time, the socket is aborted.

JSON​

JsonOptions is shared by all handlers. It uses web defaults: camelCase application DTO property names and case-insensitive reading. The envelope names id, action, data, and error are fixed by JsonPropertyName attributes and do not change with the naming policy. Clients must match the DTO schema inside data. The Redis envelope uses independent serializer settings.

Logging​

  • Successful actions and malformed client requests are logged at Debug.
  • Unexpected handler failures and exceptions thrown by the authenticator during auth: are warnings.
  • Handler exception messages are logged, never sent to clients.