OWIN WebSocket Extension v0.4.0
Authors: The OWIN working group
Copyright: OWIN Contributors
License: Creative Commons Attribution 3.0 Unported License
Last Modified: 1 October 2012
1. Introduction
2. Definitions
3. Detection
3.1. Startup
3.2. Per Request
4. Accept
5. Consumption
6. Delegate Signature
7. Usage
This document outlines the recommended patterns for using WebSockets through the OWIN interface. It depends on OWIN v1.0.0-RC0 (but may work with other versions), the OWIN opaque streams extension v0.2.0, and uses the extension key mechanics as outlined in CommonKeys.html.
WebSocket - Defined in RFC 6455, a web socket is a bidirectional, framed communication channel that uses HTTP for its initial handshake.
Opaque Stream - Some servers allow a request to take direct ownership of the underlying connection after the initial HTTP handshake. This gives the application access to a bidirectional opaque stream and the application is then responsible for employing its own protocol (e.g. WebSockets) to communicate with the client.
In order for an application to use WebSockets it MUST first detect support for the extension.
At startup the server SHOULD specify in the Properties “server.Capabilities” dictionary if WebSockets is supported (see the table below). If the application does not see support for WebSockets but does see support for Opaque Streams, it MAY insert a middleware to add WebSocket support.
Key Name |
Description |
“websocket.Version” |
The string value “1.0” indicating version 1.0 of the OWIN WebSocket extension. |
The WebSocket capable server or middleware inspects each request as it arrives to determine if it is WebSocket compatible (e.g. verb, headers, etc.). If so, it marks the request with a specific environment key (see the table below).
Key Name |
Description |
“websocket.Accept” |
A WebSocketAccept Action added by the server if the current request supports WebSockets. See Delegate Signature |
When an application sees a request that is marked as WebSocket capable and decides to accept to a WebSocket, it indicates this to the server or middleware by invoking the provided WebSocketAccept Action with parameters and a WebSocketFunc callback. This Action MUST immediately set the response status code to 101 and validate any input parameters and request state. The application then completes the AppFunc Task and allows the request pipeline to unwind.
Before invoking the WebSocketAccept Action the application MAY set the Sec-WebSocket-Protocol response header OR specify the “subprotocol” property in the parameters dictionary (see the table below), according to RFC 6455 Section 4.2.2 Step 5.5.
When the pipeline has unwound to the WebSocket server or middleware it SHOULD perform the requested accept. The WebSocket server or middleware MUST provide the necessary WebSocket response headers when performing the accept. Once the accept is complete the request has left HTTP processing and transitioned to WebSocket processing. The original Environment dictionary and its content are presumed invalid and a new one MUST be provided to the WebSocketFunc callback with the keys listed as required in the table below (see Consumption).
If the accept fails then the server or middleware SHOULD terminate the request. There is no guarantee that the WebSocketFunc provided by the application can or will be invoked in these scenarios, but the “owin.CallCancelled” CancellationToken MUST be signaled if the callback won’t be.
The WebSocketAccept parameters dictionary MAY be null if there are no parameters to pass in. If not null, it MUST be mutable and use ordinal key comparison.
Accept Parameter Key Name |
Description |
“websocket.SubProtocol” |
The negotiated protocol according to RFC 6455 Section 4.2.2 Step 5.5 |
In addition to these keys the host, server, middleware, application, etc. may add arbitrary data associated with the WebSocket Accept to the parameters dictionary. Guidelines for additional keys and a list of commonly defined keys can be found in CommonKeys.html.
When the server or middleware invoke the WebSocketFunc it provides a new WebSocket Environment dictionary (see Delegate Signature). The WebSocket Environment dictionary has the same requirements as the OWIN request Environment dictionary, namely that it MUST be non-null, mutable, use ordinal key comparison, and MUST contain the keys and values where listed as required in the table below.
Required? |
Key Name |
Description |
Yes |
“websocket.SendAsync” |
A Func used to send data. See Delegate Signature. |
Yes |
“websocket.ReceiveAsync” |
A Func used to receive data. See Delegate Signature. |
Yes |
“websocket.CloseAsync” |
A Func used to close the output channel. See Delegate Signature. |
Yes |
“websocket.Version” |
The string value “1.0” indicating version 1.0 of this OWIN WebSockets extension. |
Yes |
“websocket.CallCancelled” |
A CancellationToken provided by the server to signal that the WebSocket has been canceled/aborted. |
No |
“websocket.ClientCloseStatus” |
An optional int that may be set by the server or middleware when a close frame is received. |
No |
“websocket.ClientCloseDescription” |
An optional string that may be set by the server or middleware when a close frame is received. |
In addition to these keys the host, server, middleware, application, etc. may add arbitrary data associated with the WebSocket context to the environment dictionary. Guidelines for additional keys and a list of commonly defined keys can be found in CommonKeys.html.
The WebSocket signatures are follows:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Owin.WebSockets
{
using WebSocketAccept =
Action
<
IDictionary<string, object>, // WebSocket Accept parameters
Func // WebSocketFunc callback
<
IDictionary<string, object>, // WebSocket environment
Task // Complete
>
>;
using WebSocketFunc =
Func
<
IDictionary<string, object>, // WebSocket Environment
Task // Complete
>;
using WebSocketSendAsync =
Func
<
ArraySegment<byte> /* data */,
int /* messageType */,
bool /* endOfMessage */,
CancellationToken /* cancel */,
Task
>;
using WebSocketReceiveAsync =
Func
<
ArraySegment<byte> /* data */,
CancellationToken /* cancel */,
Task
<
Tuple
<
int /* messageType */,
bool /* endOfMessage */,
int /* count */
>
>
>;
using WebSocketReceiveTuple =
Tuple
<
int /* messageType */,
bool /* endOfMessage */,
int /* count */
>;
using WebSocketCloseAsync =
Func
<
int /* closeStatus */,
string /* closeDescription */,
CancellationToken /* cancel */,
Task
>;
}
Provided by the server in the original environment dictionary for a WebSocket compatible request. This is invoked by the application to request the upgrade to WebSockets. The parameters dictionary may be null, but the WebSocketFunc callback MUST NOT be null. Invoking this will cause the response status code to be set to 101.
This is the WebSocket application entry point. The server provides an IDictionary containing request information and the Send, Receive, and Close Funcs. On completion (success or fail), the application MUST complete or fail the returned Task or throw a synchronous exception.
The application uses the WebSocketSendAsync Func to send data to the client. The application does not have direct control over frame boundaries, but it does control message boundaries. The values of messageType MUST match the opcodes defined in the RFC; E.g. 0x1 = Text, 0x2 = Binary, and 0x8 = Close. If SendAsync is used to send a close frame, the ArraySegment must either refer to a null or empty segment, or must contain the close status code and message as defined in RFC 6455 section 5.5.1. Ping and Pong control frames (0x9, 0xA) MAY be sent, but the underlying server or middleware MUST silently discard them if it does not permit them.
ReceiveAsync will return the current message type, if the full message was consumed, and the count of data copied to the given buffer. The values of messageType MUST match the opcodes defined in the RFC; 0x1 = Text, 0x2 = Binary, and 0x8 = Close. The server or middleware MUST perform any necessary un-masking of the data before returning it. If a close frame is received the count will be 0 and any close status or description will be set in the websocket environment dictionary under “websocket.ClientCloseStatus” and “webscoket.ClientCloseDescription” if such values were sent by the client. Close data MUST NOT be copied to the user’s buffer. The server or middleware MUST NOT deliver Ping and Pong control frames (0x9, 0xA) to the application, they MUST be handled internally.
Applications SHOULD follow the RFC guidelines regarding CloseAsync (section 5.5.1). The application SHOULD invoke CloseAsync when it is done sending outgoing data. The application MUST NOT attempt to send additional data after sending a close frame. The application MUST NOT attempt any further receives after a close frame is received. The application MAY abortively terminate the connection at any time by completing the WebSocketFunc return Task, optionally with an exception. The application SHOULD NOT invoke any of the delegates after it completes the returned task.