View Source Sock behaviour (Sock v0.2.0)
The Sock
behaviour defines an interface for web servers to flexibly host WebSocket
applications.
The lifecycle of a WebSocket connection is codified in the structure of this behaviour, and proceeds as follows:
- First, a client will attempt to Upgrade an HTTP connection to a WebSocket
connection by passing a specific set of headers in an HTTP request. A
Sock
implementation is notified of this via a call toSock.negotiate/2
. The implementation can inspect the request, which is passed as aPlug.Conn
structure. It can then choose to accept or refuse the WebSocket upgrade request - Assuming the
Sock
implementation accepted the WebSocket connection, the HTTP connection is then upgraded to a WebSocket connection, andSock.handle_connection/2
is called to notify the implementation that the connection is now live - The
Sock
implementation will then be notified of client data by way ofSock.handle_text_frame/3
,Sock.handle_binary_frame/3
,Sock.handle_ping_frame/3
orSock.handle_pong_frame/3
as appropriate. - The
Sock
implementation is free to send data to the client via the passed inSocket
instance at any time - At any time,
Sock.handle_close/3
,Sock.handle_error/3
orSock.handle_timeout/2
may be called to indicate a close, error or timeout condition respectively - All of the above callbacks take and return a state value, in a manner similar to GenServers.
- The initial value of this state is that returned from the
init/1
callback
Link to this section Summary
Types
The result as returned from many handle_ calls
The result as returned from negotiate calls
A WebSocket status code
Callbacks
Called by the web server implementation when binary data is received from the client. The web server implementation will only call this function once a complete binary frame has been received (that is, once any continuation frames have been received).
Called by the web server implementation when a connection is being closed due to one of the following reasons
Called by the web server implementation after a WebSocket connection has been established (that
is, after negotiate/2
has accepted the connection & the WebSocket handshake has been
successfully completed). Implementations can use this callback to eagerly send data to the
client, subscribe the client to any relevant subscriptions within the application, or any other
task which should be undertaken at the time the connection is established
Called by the web server implementation when a connection is being closed due to one of the following reasons
Called by the web server implementation when a ping frame has been received from the client.
Note that Sock
implementation SHOULD NOT send a pong frame in response; this MUST be
automatically done by the web server before this callback has been called.
Called by the web server implementation when a pong frame has been received from the client.
Called by the web server implementation when text data is received from the client. The web server implementation will only call this function once a complete text frame has been received (that is, once any continuation frames have been received).
Called by the web server implementation when a connection has not received any client data for a period of time. The exact details of what this means and any particular timeout values are dependent on the underlying web server implementation. The socket connection will be open in this case.
Called by a web server implementation to provide initial state for all future connections. The
manner by which the init_arg
value passed into this callback is implementation dependent.
Called by the web server implementation when a client attempts to upgrade to a WebSocket
connection. At this point, the web server has validated that the upgrade request is technically
correct (that is, it meets all the requirements laid out in RFC6455§4.1), but this callback is
provided in case the Sock
implementation wishes to perform further checks before accepting the
connection (perhaps by considering the request path or various headers).
Link to this section Types
The result as returned from many handle_ calls
@type negotiate_result() :: {:accept, Plug.Conn.t(), state()} | {:refuse, Plug.Conn.t(), state()}
The result as returned from negotiate calls
@type state() :: term()
The type of state passed into / returned from Sock
callbacks
@type status_code() :: non_neg_integer()
A WebSocket status code
Link to this section Callbacks
@callback handle_binary_frame(binary(), Sock.Socket.t(), state()) :: handle_result()
Called by the web server implementation when binary data is received from the client. The web server implementation will only call this function once a complete binary frame has been received (that is, once any continuation frames have been received).
The return value from this callback is handled as described in handle_connection/2
@callback handle_close(status_code(), Sock.Socket.t(), state()) :: any()
Called by the web server implementation when a connection is being closed due to one of the following reasons:
- A
Sock
callback for this connection returned a{:close, state()}
tuple. The status code in this case will be 1000. The socket connection will still be open in this case - The client send a connection close frame. The status code in this case will be the one sent by the client. The socket connection will still be open in this case
- The underlying TCP connection connection was closed by the client. The status code in this case will be 1006. The socket connection will be closed in this case
- The hosting web server is being shut down. The status code in this case will be 1001. The socket connection will still be open in this case
The return value from this callback is ignored
@callback handle_connection(Sock.Socket.t(), state()) :: handle_result()
Called by the web server implementation after a WebSocket connection has been established (that
is, after negotiate/2
has accepted the connection & the WebSocket handshake has been
successfully completed). Implementations can use this callback to eagerly send data to the
client, subscribe the client to any relevant subscriptions within the application, or any other
task which should be undertaken at the time the connection is established
The return value from this callback is handled as follows:
{:continue, state()}
: The connection is kept open & its state is updated to the returned value{:close, state()}
: The connection is closed cleanly & its state is updated to the returned value.handle_close/3
will be subsequently called a reason code of 1000 & the updated state{:error, term(), state()}
: The connection is closed abnormally & its state is updated to the returned value.handle_error/3
will be subsequently called with the updated state
@callback handle_error(term(), Sock.Socket.t(), state()) :: any()
Called by the web server implementation when a connection is being closed due to one of the following reasons:
- A
Sock
callback for this connection returned an{:error, reason, state()}
tuple. The socket connection will still be open in this case - The underlying TCP connection connection encountered an error. The socket connection will be closed in this case
The return value from this callback is ignored
@callback handle_ping_frame(binary(), Sock.Socket.t(), state()) :: handle_result()
Called by the web server implementation when a ping frame has been received from the client.
Note that Sock
implementation SHOULD NOT send a pong frame in response; this MUST be
automatically done by the web server before this callback has been called.
The return value from this callback is handled as described in handle_connection/2
@callback handle_pong_frame(binary(), Sock.Socket.t(), state()) :: handle_result()
Called by the web server implementation when a pong frame has been received from the client.
The return value from this callback is handled as described in handle_connection/2
@callback handle_text_frame(binary(), Sock.Socket.t(), state()) :: handle_result()
Called by the web server implementation when text data is received from the client. The web server implementation will only call this function once a complete text frame has been received (that is, once any continuation frames have been received).
The return value from this callback is handled as described in handle_connection/2
@callback handle_timeout(Sock.Socket.t(), state()) :: any()
Called by the web server implementation when a connection has not received any client data for a period of time. The exact details of what this means and any particular timeout values are dependent on the underlying web server implementation. The socket connection will be open in this case.
The return value from this callback is ignored
Called by a web server implementation to provide initial state for all future connections. The
manner by which the init_arg
value passed into this callback is implementation dependent.
The result returned by init/1 is used as the initial state of all connections using the Sock
implementation. Note that init/1 may be called during compilation and as such it must not return
pids, ports or values that are specific to the runtime.
@callback negotiate(conn :: Plug.Conn.t(), state()) :: negotiate_result()
Called by the web server implementation when a client attempts to upgrade to a WebSocket
connection. At this point, the web server has validated that the upgrade request is technically
correct (that is, it meets all the requirements laid out in RFC6455§4.1), but this callback is
provided in case the Sock
implementation wishes to perform further checks before accepting the
connection (perhaps by considering the request path or various headers).
This call is also the last time that the originating request is accessible by the Sock
implementation. Should values from the Plug.Conn
request be needed later on in the WebSocket
lifecycle, this callback should store such values in the returned connection state.
If the Sock
implementation wishes to accept this connection, it should return a {:accept, Plug.Conn.t(), state()}
tuple. The web server will then perform its half of the connection
handshake as outlined in RFC6455§4.2, and subsequently call handle_connection/2
. The Sock
implementation MUST NOT send any data on the Plug.Conn
connection in this case, or else the
handshake will not be able to complete.
If the Sock
implementation wishes to refuse this connection, it should:
- Create an HTTP response on the
Plug.Conn
connection describing the reason for refusal - Return a
{:refuse, Plug.Conn.t(), state()}
tuple with the updated conn as a second argument