Home > Users Guide > WebSockets

See Also

WebSockets

WebSockets is a technology providing interactive communication between a server and client. It is an IETF standard defined by RFC 6455.

Normal HTML connections follow a request/response paradigm and do not easily support asynchronous communications or unsolicited data pushed from the server to the client. WebSockets solves this by supporting bidirectional, full-duplex communications over persistent connections. A WebSocket connection is established over a standard HTTP connection and is then upgraded without impacting the original connection. This means it will work with existing networking infrastructure including firewalls and proxies.

WebSockets is currently supported in the current releases of all major browsers, including: Chrome, Firefox, IE, Opera and Safari.

Appweb Implementation

Appweb implements WebSockets as an optional pipeline filter called WebSockFilter. The filter implements the WebSockets protocol, handshaking and provides a C language API. The webSock filter observes the WebSockets HTTP headers and manages the connection handshaking with the client. It also manages the framing and encoding/decoding of message data.

The webSock filter is configured into the pipeline using the AddFilter directive.

WebSocket Handshake

A WebSocket connection begins life as a normal HTTP request and is upgraded to the WebSocket protocol. The WebSocketFilter is activated by a set of WebSocket HTTP headers from the client that describes a desired WebSocket connection. Here is a typical client HTTP request requiring a WebSocket upgrade:

GET /websock/proto/msg HTTP/1.1
Host: example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Protocol: chat, better-chat
Sec-WebSocket-Key: 50cLrugr7h3yAbe5Kpc52Q==
Sec-WebSocket-Version: 13
Origin: http://example.com

The WebSocket filter constructs a handshake response that includes an accepted key and selected protocol. For example:

HTTP/1.1 101 Switching Protocols
Server: Embedthis-http/4.1.0
Date: Sat, 06 Oct 2013 05:10:15 GMT
Connection: Upgrade
Upgrade: WebSocket
Sec-WebSocket-Accept: 58ij/Yod1NTjzqcyjkZbZk6V6v0=
Sec-WebSocket-Protocol: chat
X-Inactivity-Timeout: 600
X-Request-Timeout: 600

Once the handshake message has been sent, the server is free to send messages to the client. Once the client receives the handshake, it can send messages to the server. Either side can send at anytime thereafter. Communications are thus full-duplex.

Message Types

WebSockets supports several message types:

Text Messages

Text messages must be valid UTF-8 strings. The receiving peer will validate and reject non-conforming strings. However, Appweb can be configured to accept invalid UTF-8 strings via the ignoreEncodingErrors appweb directive.

Binary Messages

Binary message allow the transmission of any content. Messages can be an arbitrary length up to the maximum specified by the LimitWebSocketsMessage appweb.conf directive. Messages are broken into frames of no more than the length specified by the LimitWebSocketsFrame directive. The LimitWebSocketsPacket directive defines what is the largest packet that will be passed on a single read to the user callback receiving incoming WebSocket messages.

Close Message

Ordinarily, WebSocket communications are terminated by sending a Close message. The close message includes a status code and an optional reason string to explain why the connection is being closed. The reason string must be less than 124 bytes in length so as to fit into a single WebSocket frame.

Ping/Pong Messages

To keep a communications channel alive, it is sometimes necessary to send regular messages to indicate the channel is still being used. Some servers, browsers or proxies may close an idle connection. The Ping/Pong WebSockets messages are designed to send non-application level traffic that will prevent the channel from being prematurely closed.

A ping message may be sent by either side and the peer will reply with pong message response. The pong message is generated internally by the WebSockets layer and is similarly consumed by the WebSockets layer at the peer. The application layer may initiate the peer message, but it will never see the pong response.

Appweb has a WebSocketsPing appweb.conf directive that can be used to automatically send ping messages at a specified interval. Note: use of this directive defeats all Appweb timeouts for an idle WebSockets connection.

Timeouts

The standard Appweb request and inactivity timeouts can be used for WebSocket communications. Typically, a route will be defined in the appweb.conf file for WebSockets and it will include appropriate request and inactivity timeout directives. The RequestTimeout gives the total time a WebSocket connection may remain open. The InactivityTimeout specifies the maximum time a WebSocket connection may be completely idle. Note that ping/pong messages will reset an inactivity timeout.

Configuration

It is necessary to create a dedicated Route for WebSockets communications. This will define a unique URI prefix for WebSocket communications and configure the WebSocket filter. Any number of WebSocket routes can be defined. For example:

<Route ^/websock/{controller}$>
    WebSocketsProtocol  chat            # Use the chat application protocol
    AddFilter           webSocketFilter # Add the WebSockets filter
    AddHandler          espHandler      # Run an ESP controller
    Source              test.c          # Code is in test.c
    Target              run $1          # Use {controller}
    RequestTimeout      2hrs            # Maximum connection time
    InactivityTimeout   5mins           # Maximum idle time
</Route>

This creates a route for URIs beginning with "/websock" for WebSockets communications. It uses an ESP controller to respond to incoming messages. See below for sample ESP WebSocket code.

WebSockets Directives

The following directives are supported for controlling WebSocket communications within a route.

DirectivePurpose
IgnoreEncodingErrorsIgnore UTF-8 text message encoding errors
InactivityTimeoutMaximum idle time before closing a connection
RequestTimeoutMaximum duration before closing a connection
LimitWebSocketsMaximum number of simultaneous WebSocket sessions
LimitWebSocketsFrameMaximum WebSockets message frame size
LimitWebSocketsMessageMaximum WebSockets message size
LimitWebSocketsPacketMaximum WebSockets message size passed to the callback in one transaction.
WebSocketsProtocolApplication level protocol supported by this route
WebSocketsPingFrequency to generate a ping message

WebSockets APIs

Appweb provides a simple but powerful API for interaction with WebSockets.

DirectivePurpose
httpGetReadCountGet the size of the next WebSockets message to read
httpGetWebSocketCloseReasonGet the close reason supplied by the peer
httpGetWebSocketProtocol Get the selected web socket protocol selected by the server
httpReadRead a message
httpSendSend a UTF-8 text message to the web socket peer
httpSendBlockSend a message of a given type to the web socket peer
httpSendCloseSend a close message to the web socket peer
httpSetConnNotifierDefine a connection callback notifier function
httpSetWebSocketProtocolsSet the list of application-level protocols supported by the client. This is a client-only API.
httpWebSocketOrderlyClosedTest if a close was orderly

Using WebSockets API with ESP

ESP is an ideal web framework for use with WebSockets. ESP provides a very low latency connection between a client request and execution of C functions. A dedicated ESP controller can be created for the WebSocket that responds to the incoming WebSocket request. The controller then defines a connection callback that is notified when incoming WebSockets messages arrive. This callback will also be invoked for close or error events. For example:

#include "esp.h"
static void testCallback(HttpConn *conn, int event, int arg)
{
    if (event == HTTP_EVENT_READABLE) {
        /* Incoming web sockets message */
        HttpPacket *packet = httpGetPacket(conn->readq);
        printf("Message %s\n", mprGetBufStart(packet->content));
        /* Send a reply message */
        httpSend(conn, "Reply message at %s", mprGetDate(0));
        /* No need to free buffer, the garbage collector will free */
    } else if (event == HTTP_EVENT_APP_CLOSE) {
    } else if (event == HTTP_EVENT_ERROR) {
    }
}
/*
    Action run when the client connects
 */
static void test() {
    /* This keep the connection open */
    dontAutoFinalize();
    /* Define a callback for connection and I/O events */
    httpSetConnNotifier(getConn(), testCallback);
}
/* One-time ESP loadable module initialization */
ESP_EXPORT int esp_controller_websock(HttpRoute *route, MprModule *module) {
    espDefineAction(route, "test", test);
    return 0;
}

WebSocket References

Topic Description
RFC 6455 The WebSockets Protocol
WebSocket API The Javascript WebSockets API
WebSockets Wikipedia WebSockets Wikipedia
WebSockets 101 WebSockets 101
WebSocket.org WebSockets background and demo
WebSocket Online Demo The WebSockets online test site
Chrome WebSocket Tools Chrome browser developer tools guide
WebSocket Protocol The WebSocket Protocol - Past Travails are to be avoided
Real-time Data Exchange Real-time data exchange in HTML5 and WebSockets
WebSockets Streams WebSockets is a stream not a message based protocol....

© Embedthis Software LLC, 2003-2013. All rights reserved. Embedthis, Appweb, ESP, Ejscript and Embedthis GoAhead are trademarks of Embedthis Software LLC.