This document describes the current Watson 7 WebSocket surface.
Current implementation scope:
- HTTP/1.1 WebSockets only
- Watson-owned handshake and routing
- Watson-owned
WebSocketSession - whole-message receive semantics
- session enumeration and disconnect APIs
Not currently implemented:
- HTTP/2 WebSockets
- HTTP/3 WebSockets
- public raw-socket access
- public subprotocol negotiation configuration
Enable WebSockets through WebserverSettings.WebSockets.Enable, then register routes with server.WebSocket(...).
using WatsonWebserver;
using WatsonWebserver.Core;
using WatsonWebserver.Core.WebSockets;
WebserverSettings settings = new WebserverSettings("127.0.0.1", 9000);
settings.WebSockets.Enable = true;
Webserver server = new Webserver(settings, DefaultRoute);
server.WebSocket("/chat", HandleSocketAsync);
server.WebSocket("/chat/{room}", HandleSocketAsync);Handler shape:
Task HandleSocketAsync(HttpContextBase context, WebSocketSession session)Watson allows the same path to be registered for both HTTP and WebSocket handling.
server.Get("/chat", async req => new { Mode = "http" });
server.WebSocket("/chat", HandleSocketAsync);Dispatch rule:
- ordinary HTTP requests follow normal HTTP route matching
- WebSocket upgrade requests are matched against websocket routes first
WebSocketSession is the Watson-owned session abstraction.
Useful members:
Guid Idbool IsConnectedWebSocketState StateWebSocketCloseStatus? CloseStatusstring CloseStatusDescriptionstring Subprotocolstring RemoteIpint RemotePortWebSocketRequestDescriptor Requestobject MetadataWebSocketSessionStatistics Statistics
session.Request retains reduced immutable handshake data:
PathNormalizedPathQueryHeadersRequestedVersionRequestedSubprotocolsRemoteIpRemotePort
Use ReceiveAsync() to receive a single whole message:
WebSocketMessage message = await session.ReceiveAsync(ctx.Token);
if (message != null && message.MessageType == WebSocketMessageType.Text)
{
await session.SendTextAsync("echo:" + message.Text, ctx.Token);
}Use ReadMessagesAsync() for continuous consumption:
await foreach (WebSocketMessage message in session.ReadMessagesAsync(ctx.Token))
{
if (message.MessageType == WebSocketMessageType.Text)
{
await session.SendTextAsync("echo:" + message.Text, ctx.Token);
}
}Behavior:
- receive is whole-message, not frame-level
- fragmented frames are reassembled before delivery
- only one active receive operation is allowed per session
- oversized messages are rejected according to
MaxMessageSize
Text:
await session.SendTextAsync("hello", ctx.Token);Binary:
await session.SendBinaryAsync(bytes, ctx.Token);
await session.SendBinaryAsync(new ArraySegment<byte>(bytes), ctx.Token);Behavior:
- sends are serialized per session
- counters are updated on successful send
await session.CloseAsync(WebSocketCloseStatus.NormalClosure, "done", ctx.Token);Watson also closes sessions when:
- the route completes while still connected
- the server stops
- a route handler throws
- an oversized message is received
List sessions:
IEnumerable<WebSocketSession> sessions = server.ListWebSocketSessions();Check connectivity:
bool connected = server.IsWebSocketSessionConnected(guid);Disconnect by id:
await server.DisconnectWebSocketSessionAsync(
guid,
WebSocketCloseStatus.NormalClosure,
"disconnect");Observability events are exposed on server.Events:
WebSocketSessionStartedWebSocketSessionEndedWebSocketHandshakeFailed
Example:
server.Events.WebSocketSessionStarted += (sender, args) =>
{
Console.WriteLine("WS started " + args.Session.Id + " " + args.Session.Request.Path);
};WebSocket settings live under WebserverSettings.WebSockets.
Common settings:
EnableMaxMessageSizeReceiveBufferSizeCloseHandshakeTimeoutMsAllowClientSuppliedGuidClientGuidHeaderNameSupportedVersionsEnableHttp1
Current defaults:
Enable = falseMaxMessageSize = 16777216ReceiveBufferSize = 65536CloseHandshakeTimeoutMs = 5000AllowClientSuppliedGuid = falseClientGuidHeaderName = "x-guid"SupportedVersions = ["13"]EnableHttp1 = true
- Current support is HTTP/1.1 only
- HTTP/2 and HTTP/3 WebSockets are planned follow-up work
- Public subprotocol negotiation support is not yet available
- No public raw
System.Net.WebSockets.WebSocketescape hatch exists