View Source WebSocket Handler

Included in this folder is a complete ThousandIsland.Handler based implementation of WebSockets as defined in RFC 6455.

Upgrade mechanism

A good overview of this process is contained in this ElixirConf EU talk.

Upgrading an HTTP connection to a WebSocket connection is coordinated by code contained within several libraries, including Bandit, WebSockAdapter, and Plug.

The HTTP request containing the upgrade request is first passed to the user's application as a standard Plug call. After inspecting the request and deeming it a suitable upgrade candidate (via whatever policy the application dictates), the user indicates a desire to upgrade the connection to a WebSocket by calling WebSockAdapter.upgrade/4, which checks that the request is a valid WebSocket upgrade request, and then calls Plug.Conn.upgrade_adapter/3 to signal to Bandit that the connection should be upgraded at the conclusion of the request. At the conclusion of the Plug.call/2 callback, Bandit.Pipeline will then attempt to upgrade the underlying connection. As part of this upgrade process, Bandit.DelegatingHandler will switch the Handler for the connection to be Bandit.WebSocket.Handler. This will cause any future communication after the upgrade process to be handled directly by Bandit's WebSocket stack.

Process model

Within a Bandit server, a WebSocket connection is modeled as a single process. This process is directly tied to the lifecycle of the underlying WebSocket connection; when upgrading from HTTP/1, the existing HTTP/1 handler process 'magically' becomes a WebSocket process by changing which Handler the Bandit.DelegatingHandler delegates to.

The execution model to handle a given request is quite straightforward: at upgrade time, the Bandit.DelegatingHandler will call handle_connection/2 to allow the WebSocket handler to initialize any startup state. Connection state is modeled by the Bandit.WebSocket.Connection struct and module.

All data subsequently received by the underlying Thousand Island library will result in a call to Bandit.WebSocket.Handler.handle_data/3, which will then attempt to parse the data into one or more WebSocket frames. Once a frame has been constructed, it is them passed through to the configured WebSock handler by way of the underlying Bandit.WebSocket.Connection.

Testing

All of this is exhaustively tested. Tests are broken up primarily into protocol_test.exs, which is concerned with aspects of the implementation relating to protocol conformance and client-facing concerns, while sock_test.exs is concerned with aspects of the implementation having to do with the WebSock API and application-facing concerns. There are also more unit-style tests covering frame serialization and deserialization.

In addition, the autobahn conformance suite is run via a System wrapper & executes the entirety of the suite against a running Bandit server.