PublicShow sourcehub.pl -- Manage a hub for websockets

This library manages a hub that consists of clients that are connected using a websocket. Messages arriving at any of the websockets are sent to the event queue of the hub. In addition, the hub provides a broadcast interface. A typical usage scenario for a hub is a chat server A scenario for realizing an chat server is:

  1. Create a new hub using hub_create/3.
  2. Create one or more threads that listen to Hub.queues.event from the created hub. These threads can update the shared view of the world. A message is a dict as returned by ws_receive/2 or a hub control message. Currently, the following control messages are defined:
    hub{error:Error, left:ClientId, reason:Reason}
    A client left us because of an I/O error. Reason is read or write and Error is the Prolog I/O exception.
    hub{joined:ClientId}
    A new client has joined the chatroom.

    The thread(s) can talk to clients using two predicates:

A hub consists of (currenty) four message queues and a simple dynamic fact. Threads that are needed for the communication tasks are created on demand and die if no more work needs to be done.

To be done
- The current design does not use threads to perform tasks for multiple hubs. This implies that the design scales rather poorly for hosting many hubs with few users.
Source hub_create(+Name, -Hub, +Options) is det
Create a new hub. Hub is a dict containing the following public information:
Hub.name
The name of the hub (the Name argument)
queues.event
Message queue to which the hub thread(s) can listen.

After creating a hub, the application normally creates a thread that listens to Hub.queues.event and exposes some mechanisms to establish websockets and add them to the hub using hub_add/3.

See also
- http_upgrade_to_websocket/3 establishes a websocket from the SWI-Prolog webserver.
Source current_hub(?Name, ?Hub) is nondet
True when there exists a hub Hub with Name.
Source hub_add(+Hub, +WebSocket, ?Id) is det
Add a WebSocket to the hub. Id is used to identify this user. It may be provided (as a ground term) or is generated as a UUID.
Source hub_send(+ClientId, +Message) is semidet
Send message to the indicated ClientId. Fails silently if ClientId does not exist.
Arguments:
Message- is either a single message (as accepted by ws_send/2) or a list of such messages.
Source hub_broadcast(+Hub, +Message) is det
Source hub_broadcast(+Hub, +Message, :Condition) is det
Send Message to all websockets associated with Hub for which call(Condition, Id) succeeds. Note that this process is asynchronous: this predicate returns immediately after putting all requests in a broadcast queue. If a message cannot be delivered due to a network error, the hub is informed through io_error/3.
Source hub_broadcast(+Hub, +Message) is det
Source hub_broadcast(+Hub, +Message, :Condition) is det
Send Message to all websockets associated with Hub for which call(Condition, Id) succeeds. Note that this process is asynchronous: this predicate returns immediately after putting all requests in a broadcast queue. If a message cannot be delivered due to a network error, the hub is informed through io_error/3.