Skip to main content

Using WebSockets (RFC 6455)

Using WebSockets (RFC 6455)

The web has been built around the request/response paradigm: the client sends a request to the server and the server reacts by sending a response to the client. This paradigm, and HTTP itself, does not allow for an inverted form of this communication protocol whereby a server initiates a request/response cycle with the client. A number of technologies have been developed to create an illusion that a server can initiate a dialogue with a client. These technologies are generally known as push-based or comet-based technologies and all suffer from problems that make them unsuitable for general deployment over web infrastructure. The three main techniques in current use are described below.

Short Polling

With this technique a client regularly sends HTTP requests to detect changes in server state and the server is programmed to responds immediately. An empty response signifies no change.

Problems:

  • Polling frequency (and responsiveness) is limited by the refresh latency that can be tolerated by the client.

  • Each request is a full HTTP request/response round trip which leads to high volumes of HTTP traffic which in turn leads to an unacceptable burden on the server and network infrastructure

  • Each message exchange carries the overhead of the HTTP protocol and can be particularly burdensome if the message size exceeds the Maximum Transmission Unit (MTU) which is usually 1500 Bytes for Ethernet.

Long Polling

With this technique a client sends a HTTP request but the server only responds when the client needs to be notified of a change. The client typically sends another “Long Poll” request as soon as the server sends a response message.

Problems:

  • Each request is a full HTTP request/response round trip, though this technique involves lower volumes of HTTP traffic than short-polling.

  • There is the burden of maintaining persistent connections.

  • Each message exchange carries the overhead of the HTTP protocol.

  • The success of the technique can be adversely affected by timeouts.

HTTP Streaming

This technique takes advantage of the HTTP protocol’s ability to maintain persistent (or ‘KeepAlive’) connections between the client and server. The client sends an HTTP request which is permanently kept open with the server only responding when the client needs to be notified of a change. The server does not terminate the connection after dispatching a response message and the client waits for the next message from the server (or sends a message of its own to the server).

Problems:

  • The whole client/server exchange is framed in a single HTTP request/response round trip and not all servers will support this.

  • The success of this technique can be adversely affected by the behavior of intermediaries such as Proxies and Gateways etc. …

  • There is no obligation on either side to immediately forward partial responses to the other party.

  • The technique can be adversely affected by client buffering schemes.

  • The technique can be adversely affected by timeouts.

WebSockets Protocol

The WebSockets protocol (RFC 6455) addresses the fundamental requirement of allowing servers to proactively push messages to clients by providing a full-duplex message-oriented communications channel between a client and its server. The protocol is designed to operate, and hence be secured, over the standard TCP channel already established between the client and server. In other words, the channel already used to support the HTTP protocol between a web browser and web server.

The WebSockets protocol and its API are standardized by the W3C and the client part is included with HTML 5.

Intermediaries (such as proxies and firewalls) are expected to be aware of (and expected to support) the WebSockets protocol.

Browser Support

There have been several iterations in creating the final standard for the WebSockets protocol, each with varying degrees of browser support. The history is summarized below.

  • Hixie-75:

    • Chrome 4.0+5.0, Safari 5.0.0

  • HyBi-00/Hixie-76:

    • Chrome 6.0-13.0, Safari 5.0.2+5.1, Firefox 4.0 (disabled), Opera 11 (disabled)

  • HyBi-07+:

    • Chrome 14.0, Firefox 6.0, IE 9 (via Silverlight extension)

  • HyBi-10:

    • Chrome 14.0+15.0, Firefox 7.0+8.0+9.0+10.0, IE 10 (via Windows 8 developer preview)

  • HyBi-17/RFC 6455

    • Chrome 16

    • Safari 6

    • Firefox 11

    • Opera 12.10/Opera Mobile 12.1

    • IE 10

The final highlighted section is the most significant for the purpose of developing portable web applications.

Server Support

The server-oriented JavaScript-based Node.js technology arguably offers the most sophisticated, and currently most mature, implementation of the WebSockets protocol. And for this reason, WebSockets have been closely associated with Node.js up until the time of writing (March 2013). However, other web server technologies are quickly catching up and the latest versions of all major web servers now offer WebSockets support as shown below.

  • Node.js

    • All versions

  • Apache v2.2

  • IIS v8.0

    • Windows 8 and Windows Server 2012

  • Nginx v1.3

  • Lighttpd

The highlighted section is the most significant for the purpose of developing portable web applications with CSP.

Protocol in Detail

Creating a WebSocket involves an ordered exchange of messages between the client and the server. First, the WebSocket handshake must take place. The handshake is based on, and resembles, an HTTP message exchange so that it can pass without problem through existing HTTP infrastructure.

  • Client sends handshake request for a WebSocket connection.

  • Server sends handshake response (if it is able to).

The web server recognizes the conventional HTTP header structure in the handshake request message and sends a similarly constructed response message to the client indicating that it supports the WebSocket protocol - assuming it is able to. If both parties agree then the channel is switched from HTTP (http://) to the WebSockets protocol (ws://).

  • When the protocol is successfully switched, the channel allows full duplex communication between the client and server.

  • The data framing for individual messages is minimal.

Typical WebSocket Handshake Message from Client

GET /csp/user/MyApp.MyWebSocketServer.cls HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://localhost

Typical WebSocket Handshake Message from Server

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

Note how the client handshake message requests that the protocol be upgraded from HTTP to WebSocket. Note also the exchange of unique keys between the client (Sec-WebSocket-Key) and server (Sec-WebSocket-Accept).

WebSockets Client Code (JavaScript)

In the browser environment the client side of the WebSocket protocol is implemented in JavaScript code. Standard text books describe the usage model in detail. This document will briefly describe the basics.

Create a WebSocket

The first parameter represents the URL identifying the server end of the WebSocket application. The second parameter is optional, and if present, specifies the sub-protocol that the server must support for the WebSocket connection to be successful.

var ws = new WebSocket(url, [protocol]);

Example:

ws = new WebSocket(((window.location.protocol == "https:")
     ? "wss:" : "ws:") \
     + "//" + window.location.host
     + /csp/user/MyApp.MyWebSocketServer.cls);

Note how the protocol is defined as either ws or wss depending on whether or not the underlying transport is secured using SSL/TLS.

The read-only attribute ws.readyState defines the state of the connection. It can take one of the following values:

  • 0 The connection is not yet established.

  • 1 The connection is established and communication is possible.

  • 2 The connection is subject to the closing handshake.

  • 3 The connection is closed or could not be opened.

The read-only attribute ws.bufferedAmount defines the number of bytes of UTF-8 text that have been queued using the send() method.

WebSocket Events

The following events are available.

  • ws.onopen Fires when the socket connection is established.

  • ws.onmessage Fires when the client receives data from the server.

Data received in event.data.

  • ws.onerror Fires when an error occurs in the communication.

  • ws.onclose Fires when the connection is closed.

WebSocket Methods

The following methods are available.

  • ws.send(data) Transmit data to the client.

  • ws.close() Close the connection.

WebSockets Server Code (CSP)

The base Caché class for implementing WebSocket Servers is%CSP.WebSocketOpens in a new tab

When the client requests a WebSocket connection, the initial HTTP request (the initial handshake message) instructs the CSP engine to initialize the application's WebSocket server. The WebSocket server is the class named in the requesting URL. For example, if your WebSocket server is called MyApp.MyWebSocketServer and is designed to operate in the USER NameSpace then the URL used to request the WebSocket connection is:

/csp/user/MyApp.MyWebSocketServer.cls
WebSocket Events

To implement a WebSocket server, create a subclass of %CSP.WebSocket and define callbacks in that class as needed. Note that the web session is unlocked before calling any of these methods. Also in all cases, the socket times out after the CSP session times out.

OnPreServer (optional)

Use this method to invoke code that should be executed before the WebSocket server is established. Changes to the SharedConnection property must be made here.

Server (Mandatory)

The WebSocket server. This is the server-side implementation of the WebSocket application. Messages can be exchanged with the client using the Read() and Write() methods. Use the EndServer() method to gracefully close the WebSocket from the server end.

OnPostServer (optional)

Use this method to invoke code that should be executed after the WebSocket server has closed.

WebSocket Methods

The following methods are provided

Method Read(ByRef len As %Integer = 32656,
     ByRef sc As %Status,
     timeout As %Integer = 86400) As %String

This method reads up to len characters from the client. If the call is successful the status (sc) is returned as $$$OK, otherwise one of the following error codes is returned:

  • $$$CSPWebSocketTimeout The Read method has timed-out.

  • $$$CSPWebSocketClosed The client has terminated the WebSocket.

Method Write(data As %String) As %Status

This method writes data to the client.

Method EndServer() As %Status

This method gracefully ends the WebSocket server by closing the connection with the client.

Method OpenServer(WebSocketID As %String = "") As %Status

This method opens an existing WebSocket Server. Only a WebSocket operating asynchronously (SharedConnection=1) can be accessed using this method.

WebSocket Properties

The following properties are provided:

SharedConnection (default: 0)

This property determines whether the communication between the client and WebSocket server should be over a dedicated Gateway connection or asynchronously over a pool of shared connections. This property must be set in the OnPreServer() method and may be set as follows:

  • SharedConnection=0 The WebSocket server communicates synchronously with the client via a dedicated Gateway connection. In this mode of operation the hosting connection is effectively 'private' to the application’s WebSocket Server.

  • SharedConnection=1 The WebSocket server communicates asynchronously with the client via a pool of shared Gateway connections.

WebSocketID

This property represents the unique identity of the WebSocket.

SessionId

This property represents the hosting CSP Session ID against which the WebSocket was created.

BinaryData

This property instrucst the Gateway to bypass functionality that would otherwise interpret the transmitted data stream as UTF-8 encoded text and set the appropriate binary data fields in the WebSocket frame header.

This should be set to 1 before writing a stream of binary data to the client. For example:

Set ..BinaryData = 1

WebSockets Server Example

The following simple WebSocket server class accepts an incoming connection from a client and simply echo back data received.

The timeout is set to 10 seconds and each time the Read() method times-out a message is written the client. This illustrates one of the key concepts underpinning WebSockets: initiating a message exchange with the client from the server.

Finally, the WebSocket closes gracefully if the client (i.e. user) sends the string exit.

Method OnPreServer() As %Status
{
   Quit $$$OK
}

Method Server() As %Status
{
   Set timeout=10
   For  {
      Set len=32656
      Set data=..Read(.len, .status, timeout)
      If $$$ISERR(status) {
If $$$GETERRORCODE(status) = $$$CSPWebSocketClosed {
Quit
}
         If $$$GETERRORCODE(status) = $$$CSPWebSocketTimeout {
               Set status=..Write(“Server timed-out at “_$Horolog)
         }
      }
      else {
         If data="exit" Quit
         Set status=..Write(data)
      }
   }
   Set status=..EndServer()
   Quit $$$OK
}

Method OnPostServer() As %Status
{
   Quit $$$OK
}

}

WebSockets Server Asynchronous Operation

The example given in the previous section illustrates a WebSocket server operating synchronously with the client over a dedicated Caché connection. When such a connection is established it is labeled as WebSocket in the status column of the Gateways Systems Status form. With this mode the WebSocket is operating within the security context of the hosting CSP session and all properties associated with that session can be easily accessed.

With the asynchronous mode of operation (SharedConnection=1), the hosting connection is released as soon as the WebSocket Object is created and subsequent dialogue with the client is over the pool of shared connections: messages from the client arrive via the conventional pool of Gateway connections to Caché and messages to the client are dispatched over the pool of Server connections that have been established between the Gateway and Caché.

In asynchronous mode, the WebSocket Server becomes detached from the main CSP session: the SessionId property holds the value of the hosting Session ID but an instance of the session object is not automatically created.

The example given previously can be run asynchronously simply by setting the SharedConnection property in the OnPreServer() method. However, it is not necessary to have a Caché process permanently associated with the WebSocket. The Server() method can exit (and the hosting process halt) without closing the WebSocket. Provided the WebSocketID has been retained, the WebSocket can be subsequently opened in a different Caché process and communication with the client resumed.

Example:

Class MyApp.MyWebSocketServer Extends %CSP.WebSocket
{

Method OnPreServer() As %Status
{
MYAPP.SAVE(..WebSocketID)
   Set ..SharedConnection = 1
   Quit $$$OK
}

Method Server() As %Status
{
   Quit $$$OK
}

Method OnPostServer() As %Status
{
   Quit $$$OK
}

}

Note that the WebSocketID is retained for subsequent use in the OnPreServer() method. Note also, the setting of the SharedConnection property in the OnPreServer() method and that the Server() method simply exits.

Subsequently retrieving the WebSocketID:

Set WebSocketID = MYAPP.RETRIEVE()

Re-establishing a link with the client:

Set ws=##class(%CSP.WebSocketTest).%New()
Set %status = ws.OpenServer(WebSocketID)

Reading from and writing to the client:

Set %status=ws.Write(message)
Set data=ws.Read(.len, .%status, timeout)

Finally, closing the WebSocket from the server side:

Set %status=ws.EndServer()
FeedbackOpens in a new tab