WebSocket is a lightweight message oriented protocol on top of TCP/IP streams. It is typically used as an 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 streams and provides ws_open/3 to create a websocket stream from any Prolog stream. Typically, both an input and output stream are wrapped and then combined into a single object using stream_pair/3.
The high-level interface provides http_upgrade_to_websocket/3 to realise a websocket inside the HTTP server infrastructure and http_open_websocket/3 as a layer over http_open/3 to realise a client connection. After establishing a connection, ws_send/2 and ws_receive/2 can be used to send and receive messages. The predicate ws_close/2 is provided to perform the closing handshake and dispose of the stream objects.
subprotocol(Protocol)
.The following example exchanges a message with the html5rocks.websocket.org echo service:
?- 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 = <stream>(0xe4a440,0xe4a610), Reply = websocket{data:"Hello World!", opcode:text}.
WebSocket | is a stream pair (see stream_pair/3) |
call(Goal, WebSocket)
,
where WebSocket is a socket-pair. Options:
true
(default), guard the execution of Goal
and close the websocket on both normal and abnormal termination of Goal.
If false
, 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.infinite
.Note that the Request argument is the last for cooperation with http_handler/3. A simple echo server that can be accessed at =/ws/= can be implemented as:
:- 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) ).
switching_protocols(Goal, Options)
. The recovery from this
exception causes the HTTP infrastructure to call
call(Goal, WebSocket)
.text(+Text)
, but all character codes produced by Content
must be in the range [0..255]. Typically, Content will be an
atom or string holding binary data.text(+Text)
, provided for consistency.opcode
key. Other keys
used are:
format
:
Formatstring
, prolog
or json
.
See ws_receive/3.data
:
TermNote that ws_start_message/3 does not unlock the stream. This is done by ws_send/1. This implies that multiple threads can use ws_send/2 and the messages are properly serialized.
opcode
:
OpCodeclose
and data to the atom
end_of_file
.data
:
Stringrsv
:
RSV
If ping
message is received and WebSocket is
a stream pair,
ws_receive/1 replies with a pong
and waits for the next message.
The predicate ws_receive/3 processes the following options:
close
message if this was not already sent and wait for the close reply.
Code | is the numerical code indicating the close status. This is 16-bit integer. The codes are defined in section 7.4.1. Defined Status Codes of RFC6455. Notably, 1000 indicates a normal closure. |
Data | is currently interpreted as text. |
websocket_error(unexpected_message, Reply)
if the other
side did not send a close message in reply.server
or client
. If client
,
messages are sent as masked.true
(default), closing WSStream also closes Stream.subprotocols
option of http_open_websocket/3
and
http_upgrade_to_websocket/3.A typical sequence to turn a pair of streams into a WebSocket is here:
..., Options = [mode(server), subprotocol(chat)], ws_open(Input, WsInput, Options), ws_open(Output, WsOutput, Options), stream_pair(WebSocket, WsInput, WsOutput).