% This LaTeX document was generated using the LaTeX backend of PlDoc, % The SWI-Prolog documentation system \section{library(http/websocket): WebSocket support} \label{sec:websocket} \begin{tags} \tag{See also} RFC 6455, \url{http://tools.ietf.org/html/rfc6455} \tag{To be done} Deal with protocol extensions. \end{tags} WebSocket is a lightweight message oriented protocol on top of TCP/IP streams. It is typically used as an \textit{upgrade} of an HTTP connection to provide bi-directional communication, but can also be used in isolation over arbitrary (Prolog) streams. The SWI-Prolog interface is based on \textit{streams} and provides \predref{ws_open}{3} to create a \textit{websocket stream} from any Prolog stream. Typically, both an input and output stream are wrapped and then combined into a single object using \predref{stream_pair}{3}. The high-level interface provides \predref{http_upgrade_to_websocket}{3} to realise a websocket inside the HTTP server infrastructure and \predref{http_open_websocket}{3} as a layer over \predref{http_open}{3} to realise a client connection. After establishing a connection, \predref{ws_send}{2} and \predref{ws_receive}{2} can be used to send and receive messages. The predicate \predref{ws_close}{3} is provided to perform the closing handshake and dispose of the stream objects.\vspace{0.7cm} \begin{description} \predicate[det]{http_open_websocket}{3}{+URL, -WebSocket, +Options} Establish a client websocket connection. This predicate calls \predref{http_open}{3} with additional headers to negotiate a websocket connection. In addition to the options processed by \predref{http_open}{3}, the following options are recognised: \begin{description} \termitem{subprotocols}{+List} \arg{List} of subprotocols that are acceptable. The selected protocol is available as ws_property(\arg{WebSocket}, \verb$subprotocol(Protocol)$. \end{description} Note that clients often provide an \arg{Origin} header and some servers require this field. See RFC 6455 for details. By default this predicate does not set \arg{Origin}. It may be set using the \verb$request_header$ option of \predref{http_open}{3}, e.g. by passing this in the \arg{Options} list: \begin{code} request_header('Origin' = 'https://www.swi-prolog.org') \end{code} The following example exchanges a message with the html5rocks.websocket.org echo service: \begin{code} ?- URL = 'ws://html5rocks.websocket.org/echo', http_open_websocket(URL, WS, []), ws_send(WS, text('Hello World!')), ws_receive(WS, Reply), ws_close(WS, 1000, "Goodbye"). URL = 'ws://html5rocks.websocket.org/echo', WS = (0xe4a440,0xe4a610), Reply = websocket{data:"Hello World!", opcode:text}. \end{code} \begin{arguments} \arg{WebSocket} & is a stream pair (see \predref{stream_pair}{3}) \\ \end{arguments} \predicate{http_upgrade_to_websocket}{3}{:Goal, +Options, +Request} Create a websocket connection running \verb$call(Goal, WebSocket)$, where WebSocket is a socket-pair. \arg{Options}: \begin{description} \termitem{guarded}{+Boolean} If \const{true} (default), guard the execution of \arg{Goal} and close the websocket on both normal and abnormal termination of \arg{Goal}. If \const{false}, \arg{Goal} itself is responsible for the created websocket. This can be used to create a single thread that manages multiple websockets using I/O multiplexing. \termitem{subprotocols}{+List} \arg{List} of acceptable subprotocols. \termitem{timeout}{+TimeOut} Timeout to apply to the input stream. Default is \const{infinite}. \end{description} Note that the \arg{Request} argument is the last for cooperation with \predref{http_handler}{3}. A simple \textit{echo} server that can be accessed at =/ws/= can be implemented as: \begin{code} :- use_module(library(http/websocket)). :- use_module(library(http/thread_httpd)). :- use_module(library(http/http_dispatch)). :- http_handler(root(ws), http_upgrade_to_websocket(echo, []), [spawn([])]). echo(WebSocket) :- ws_receive(WebSocket, Message), ( Message.opcode == close -> true ; ws_send(WebSocket, Message), echo(WebSocket) ). \end{code} \begin{tags} \tag{throws} \verb$switching_protocols(Goal, Options)$. The recovery from this exception causes the HTTP infrastructure to call \verb$call(Goal, WebSocket)$. \tag{See also} \predref{http_switch_protocol}{2}. \end{tags} \predicate[det]{ws_send}{2}{+WebSocket, +Message} Send a message over a websocket. The following terms are allowed for \arg{Message}: \begin{description} \termitem{text}{+Text} Send a text message. \arg{Text} is serialized using \predref{write}{1}. \termitem{binary}{+Content} As \verb$text(+Text)$, but all character codes produced by \arg{Content} must be in the range [0..255]. Typically, \arg{Content} will be an atom or string holding binary data. \termitem{prolog}{+Term} Send a Prolog term as a text message. Text is serialized using \predref{write_canonical}{1}. \termitem{json}{+JSON} Send the Prolog representation of a \arg{JSON} term using \predref{json_write_dict}{2}. \termitem{string}{+Text} Same as \verb$text(+Text)$, provided for consistency. \termitem{close}{+Code, +Text} Send a close message. \arg{Code} is 1000 for normal close. See websocket documentation for other values. \termitem{\arg{Dict}}{} A dict that minimally contains an \const{opcode} key. Other keys used are: \begin{description} \infixtermitem{\Smodule}{\term{format}{}}{\arg{Format}} Serialization format used for \arg{Message}.data. \arg{Format} is one of \const{string}, \const{prolog} or \const{json}. See \predref{ws_receive}{3}. \infixtermitem{\Smodule}{\term{data}{}}{\arg{Term}} If this key is present, it is serialized according to \arg{Message}.format. Otherwise it is serialized using \predref{write}{1}, which implies that string and atoms are just sent verbatim. \end{description} \end{description} Note that \predref{ws_start_message}{3} does not unlock the stream. This is done by \predref{ws_send}{1}. This implies that multiple threads can use \predref{ws_send}{2} and the messages are properly serialized. \begin{tags} \tag{To be done} Provide serialization details using options. \end{tags} \predicate[det]{ws_receive}{2}{+WebSocket, -Message:dict} \nodescription \predicate[det]{ws_receive}{3}{+WebSocket, -Message:dict, +Options} Receive the next message from \arg{WebSocket}. \arg{Message} is a dict containing the following keys: \begin{description} \infixtermitem{\Smodule}{\term{opcode}{}}{\arg{OpCode}} \arg{OpCode} of the message. This is an atom for known opcodes and an integer for unknown ones. If the peer closed the stream, \arg{OpCode} is bound to \const{close} and data to the atom \verb$end_of_file$. \infixtermitem{\Smodule}{\term{data}{}}{\arg{String}} The data, represented as a string. This field is always present. \arg{String} is the empty string if there is no data in the message. \infixtermitem{\Smodule}{\term{rsv}{}}{\arg{RSV}} Present if the \arg{WebSocket} \arg{RSV} header is not 0. \arg{RSV} is an integer in the range [1..7]. \end{description} If \const{ping} message is received and \arg{WebSocket} is a stream pair, \predref{ws_receive}{1} replies with a \const{pong} and waits for the next message. The predicate \predref{ws_receive}{3} processes the following options: \begin{description} \termitem{format}{+Format} Defines how \textit{text} messages are parsed. \arg{Format} is one of \begin{description} \termitem{string}{} Data is returned as a Prolog string (default) \termitem{json}{} Data is parsed using \predref{json_read_dict}{3}, which also receives \arg{Options}. \termitem{prolog}{} Data is parsed using \predref{read_term}{3}, which also receives \arg{Options}. \end{description} \end{description} \begin{tags} \tag{To be done} Add a hook to allow for more data formats? \end{tags} \predicate[det]{ws_close}{3}{+WebSocket:stream_pair, +Code, +Data} Close a \arg{WebSocket} connection by sending a \const{close} message if this was not already sent and wait for the close reply. \begin{arguments} \arg{Code} & is the numerical code indicating the close status. This is 16-bit integer. The codes are defined in section \textit{7.4.1. Defined Status Codes} of RFC6455. Notably, 1000 indicates a normal closure. \\ \arg{Data} & is currently interpreted as text. \\ \end{arguments} \begin{tags} \tag{Errors} \verb$websocket_error(unexpected_message, Reply)$ if the other side did not send a close message in reply. \end{tags} \predicate[det]{ws_open}{3}{+Stream, -WSStream, +Options} Turn a raw TCP/IP (or any other binary stream) into a websocket stream. \arg{Stream} can be an input stream, output stream or a stream pair. \arg{Options} includes \begin{description} \termitem{mode}{+Mode} One of \const{server} or \const{client}. If \const{client}, messages are sent as \textit{masked}. \termitem{buffer_size}{+Count} Send partial messages for each \arg{Count} bytes or when flushing the output. The default is to buffer the entire message before it is sent. \termitem{close_parent}{+Boolean} If \const{true} (default), closing \arg{WSStream} also closes \arg{Stream}. \termitem{subprotocol}{+Protocol} Set the subprotocol property of WsStream. This value can be retrieved using \predref{ws_property}{2}. \arg{Protocol} is an atom. See also the \const{subprotocols} option of \predref{http_open_websocket}{3} and \predref{http_upgrade_to_websocket}{3}. \end{description} A typical sequence to turn a pair of streams into a WebSocket is here: \begin{code} ..., Options = [mode(server), subprotocol(chat)], ws_open(Input, WsInput, Options), ws_open(Output, WsOutput, Options), stream_pair(WebSocket, WsInput, WsOutput). \end{code} \predicate[nondet]{ws_property}{2}{+WebSocket, ?Property} True if \arg{Property} is a property \arg{WebSocket}. Defined properties are: \begin{description} \termitem{subprotocol}{Protocol} \arg{Protocol} is the negotiated subprotocol. This is typically set as a property of the websocket by \predref{ws_open}{3}. \end{description} \predicate{ws_mask}{1}{-Mask} Produce a good random number of the mask of a client message. \end{description}