Part of the ongoing work on EmbedIO v4.0, as documented in #546, is a near-complete refactor of WebSockets support.
Easier reception callbacks with automatic edge-case handling
Up to EmbedIO v3, in order to subclass WebSocketModule you had to override this method:
/// <summary>
/// Called when this WebSocket server receives a full message (EndOfMessage) from a client.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="buffer">The buffer.</param>
/// <param name="result">The result.</param>
/// <returns>A <see cref="Task"/> representing the ongoing operation.</returns>
protected abstract Task OnMessageReceivedAsync(IWebSocketContext context, byte[] buffer, IWebSocketReceiveResult result);
In the overridden method, first you would first have to assess the type of data received. Then, in the case of textual data, you had to convert buffer to a string. Furthermore, the result parameter was quite confusing: apparently you could receive a partial message, or even a close request. OK, you couldn't actually, but then why did you need an IWebSocketReceiveResult interface where a bool (or an enum with two possible values) was enough?
Now you have to override one or both of these methods instead:
/// <summary>
/// Called when this WebSocket server receives a full message (EndOfMessage) from a client.
/// </summary>
/// <param name="context">An instance of <see cref="IWebSocketContext"/>.</param>
/// <param name="text">The text contained in the message.</param>
/// <returns>A <see cref="Task"/> representing the ongoing operation.</returns>
protected virtual Task OnTextMessageReceivedAsync(IWebSocketContext context, string text)
=> throw new WebSocketException(CloseStatusCode.UnsupportedData, "This endpoint does not accept text messages.");
/// <summary>
/// Called when this WebSocket server receives a full message (EndOfMessage) from a client.
/// </summary>
/// <param name="context">An instance of <see cref="IWebSocketContext"/>.</param>
/// <param name="data">The binary data contained in the message.</param>
/// <returns>A <see cref="Task"/> representing the ongoing operation.</returns>
protected virtual Task OnBinaryMessageReceivedAsync(IWebSocketContext context, byte[] data)
=> throw new WebSocketException(CloseStatusCode.UnsupportedData, "This endpoint does not accept binary messages.");
Textual data is converted to string for you; the connection is closed with an appropriate status code, as per RFC6455, if the received data is not valid UTF-8 text.
If you override only one of the two methods above, the reception of a kind of data you don't expect causes the connection to be closed, as per RFC6455, with a status code of 1003 (the value of CloseStatusCode.UnsupportedData).
The "frame" reception callback has been removed
Method WebSocketModule.OnFrameReceivedAsync has been removed because it complicated message reception code to support a feature that hardly any application needs. WebSocket applications should never rely on internals details of the protocol implementation anyway: the unit of data exchanged on a WebSocket is the message, not the frame.
In addition, OnFrameReceivedAsync had never worked as advertised anyway when using HttpListenerMode.Microsoft. Their WebSocket class does not yield single frames; instead, it aggregates them into complete messages and may then yield partial messages according to the size of the buffer allocated for reception.
WebSocketException now goes both ways
WebSocketException used to be thrown by EmbedIO to signal an anomalous condition or the attempt to perform an operation on a WebSocket that was not supported (typically writing to a closed socket).
Now you may also throw a WebSocketException from a callback method (OnBinaryMessageReceivedAsync, OnTextMessageReceivedAsync, OnClientConnectedAsync) to command the connection to be closed. This lets you write simpler code, as previously you had to call Close and return.
Internal changes
Code has been optimized to perform as few allocations as possible.
Support for data compression in WebSockets (not part of RFC6455) has been removed. It had never been enabled anyway.
The message reception loop has been unified between HTTP listener modes. One consequence is the creation of one less parallel Task for each WebSocket connection.
Comments are very welcome. Expect to be able to try the modified WebSocket support in v4.0-preview.1 soon.
Part of the ongoing work on EmbedIO v4.0, as documented in #546, is a near-complete refactor of WebSockets support.
Easier reception callbacks with automatic edge-case handling
Up to EmbedIO v3, in order to subclass
WebSocketModule
you had to override this method:In the overridden method, first you would first have to assess the type of data received. Then, in the case of textual data, you had to convert
buffer
to a string. Furthermore, theresult
parameter was quite confusing: apparently you could receive a partial message, or even a close request. OK, you couldn't actually, but then why did you need anIWebSocketReceiveResult
interface where abool
(or an enum with two possible values) was enough?Now you have to override one or both of these methods instead:
Textual data is converted to
string
for you; the connection is closed with an appropriate status code, as per RFC6455, if the received data is not valid UTF-8 text.If you override only one of the two methods above, the reception of a kind of data you don't expect causes the connection to be closed, as per RFC6455, with a status code of 1003 (the value of
CloseStatusCode.UnsupportedData
).The "frame" reception callback has been removed
Method
WebSocketModule.OnFrameReceivedAsync
has been removed because it complicated message reception code to support a feature that hardly any application needs. WebSocket applications should never rely on internals details of the protocol implementation anyway: the unit of data exchanged on a WebSocket is the message, not the frame.In addition,
OnFrameReceivedAsync
had never worked as advertised anyway when usingHttpListenerMode.Microsoft
. TheirWebSocket
class does not yield single frames; instead, it aggregates them into complete messages and may then yield partial messages according to the size of the buffer allocated for reception.WebSocketException
now goes both waysWebSocketException
used to be thrown by EmbedIO to signal an anomalous condition or the attempt to perform an operation on a WebSocket that was not supported (typically writing to a closed socket).Now you may also throw a
WebSocketException
from a callback method (OnBinaryMessageReceivedAsync
,OnTextMessageReceivedAsync
,OnClientConnectedAsync
) to command the connection to be closed. This lets you write simpler code, as previously you had to callClose
and return.Internal changes
Code has been optimized to perform as few allocations as possible.
Support for data compression in WebSockets (not part of RFC6455) has been removed. It had never been enabled anyway.
The message reception loop has been unified between HTTP listener modes. One consequence is the creation of one less parallel
Task
for each WebSocket connection.Comments are very welcome. Expect to be able to try the modified WebSocket support in v4.0-preview.1 soon.