OWIN WebSocket Extension v0.2.0

 

Authors: The OWIN working group

Last Modified: 13 August 2012

Contents

1.      Introduction

2.      Definitions

3.      Scenarios

4.      Keys

5.      Delegate Signature & Usage

1.    Introduction

This document outlines the recommended patterns for using WebSockets through the OWIN interface. It depends on OWIN v0.14.0 (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.

2.    Definitions

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.

3.    Scenarios

There are two main scenarios defined for using WebSockets semantics via OWIN, depending on what support the server provides and what functionality the application requires.

The server SHOULD advertise via keys in the startup Properties dictionary what capabilities it supports.  See the list of recommended Keys below.  This advertisement helps the application build a pipeline to fit its needs.

3.1.  The application needs WebSocket access and the server supports it.

·         When a WebSocket capable server identifies an incoming request as WebSocket compatible (via method, headers, etc.) it SHOULD indicate this to the application by adding the appropriate key and value to the Environment dictionary (see Keys). 

·         Applications that see this key and want to complete the upgrade to a WebSocket can indicate this to the server by returning a 101 status code and adding a WebSocket body delegate to the response Properties dictionary (see Keys, Delegate Signature & Usage). 

o   The normal response body delegate field SHOULD be set to null. 

·         The server then performs the upgrade and invokes the given delegate.

o   The server performing the upgrade is responsible for supplying the necessary WebSocket response headers.

3.2.  The application needs WebSocket access, but the server only supports opaque streams.

·         At startup the application detects from the startup Properties dictionary that the server supports opaque streams and inserts a WebSocket capable middleware.

·         The WebSocket middleware will inspect incoming requests and see if any that are marked as opaque upgradable are also WebSocket compatible.  If so it will add the appropriate WebSocket key to the Environment dictionary (see Keys).

·         The application functions the same as if the server was providing WebSocket support directly.  It inspects the Environment dictionary keys to identify WebSocket capable requests, returns a 101 status code, and provides a WebSocket body delegate.

·         The WebSocket middleware intercepts the response, sets the appropriate WebSocket headers, and configures the response to request an opaque stream upgrade as outlined in the Opaque Stream extension.

·         The server then performs the upgrade to an opaque stream and invokes the WebSocket middleware’s opaque stream body delegate.

o   Servers that do not directly support WebSockets SHOULD ignore the WebSocket body delegate in the response Properties dictionary.

·         The WebSocket middleware wraps the opaque stream and invokes the application’s WebSocket body delegate.

4. Keys

The following keys and values are defined according to the guidelines provided in CommonKeys.html.  Key names and string values MUST be compared as Ordinal, case sensitive.

Key Name

Type

Description

websocket.Support

string

“WebSocketFunc” Used in the startup Properties dictionary to indicate general WebSocket support.  Used in the request Environment dictionary to indicate a WebSocket compatible request.

websocket.Func

Func<...>

Used in the response Properties dictionary to request that the server or middleware upgrade to a WebSocket and invoke this delegate.  See Delegate Signature & Usage.

 

5. Delegate Signature & Usage

The WebSocket body delegate signature is as follows:

using WebSocketFunc =

        Func

        <

            Func // SendAsync

            <

                ArraySegment<byte> /* data */,

                int /* messageType */,

                bool /* endOfMessage */,

                CancellationToken /* cancel */,

                Task

            >,

            Func // ReceiveAsync

            <

                ArraySegment<byte> /* data */,

                CancellationToken /* cancel */,

                Task

                <

                    Tuple

                    <

                        int /* messageType */,

                        bool /* endOfMessage */,

                        int? /* count */,

                        int? /* closeStatus */,

                        string /* closeStatusDescription */

                    >

                >

            >,

            Func // CloseAsync

            <

                int /* closeStatus */,

                string /* closeDescription */,

                CancellationToken /* cancel */,

                Task

            >,

            // Complete

            Task

        >;

 

The application provides this delegate in the response Properties dictionary.  The server or middleware invokes it to provide the application with delegates for SendAsync, ReceiveAsync, and CloseAsync.  The application uses these delegates to communicate via the WebSocket.  The application does not have direct control over frame boundaries, but it does control message boundaries.  On completion (success or fail), the application MUST complete or fail the returned Task.

 

The values of messageType for SendAsync and ReceiveAsync MUST match the opcodes defined in the RFC; 0x1 = Text, 0x2 = Binary, and 0x8 = Close.  Ping and Pong control frames (0x9, 0xA) SHOULD NOT be exposed directly to the application, but should be handled internally.

 

ReceiveAsync will return the current message type, if full message was consumed, and the count of data copied to the given buffer.  The server or middleware MUST perform any necessary un-masking of the data before returning it. If a closed frame is received then count will be null and closeStatus and closeStatusDescription MAY be populated if such values were sent by the client.

 

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 once a close frame is received.  The application MAY abortively terminate the connection at any time by completing the returned task, optionally with an exception.  The application SHOULD NOT invoke any of the delegates once it completes the returned task.