Did you know ... Search Documentation:
sse.pl -- Server-Sent Events (SSE)
PublicShow source

This library provides a small server-side helper for the Server-Sent Events wire format. SSE is a one-way (server to client) push channel that lives inside a normal HTTP response with Content-Type: text/event-stream. Each event is a sequence of Field: Value lines terminated by a blank line.

Typical use, with the handler running on a separate thread so that the worker pool is not blocked:

:- use_module(library(http/sse)).
:- use_module(library(http/http_dispatch)).

:- http_handler(root(events), events,
                [ spawn([]), time_limit(infinite) ]).

events(_Request) :-
    sse_open,
    between(1, infinite, Min),
        sse_send(_{event: minute, data: Min}),
        sleep(60),
        fail.

The underlying machinery is provided by the CGI output stream, which recognises Content-Type: text/event-stream and switches into a mode where every flushed write is sent unbuffered to the client. The present library hides the wire format, multi-line data handling and the response headers needed to defeat HTTP intermediaries that buffer small responses.

If the client disconnects, the next write raises an I/O exception that typically terminates the event-producing loop.

A single SSE response is single-writer; multiple threads writing to the same response will interleave bytes and corrupt the stream. Fan out to multiple clients by giving each connection its own writer.

Browsers loading the page from a different origin than the SSE endpoint need Cross-Origin Resource Sharing (CORS). sse_open/0,1 calls cors_enable/0, so setting the http:cors setting (see library(http/http_cors)) enables it.

See also
- library(http/websocket) for bi-directional messaging.
- library(http/http_cors) for enabling CORS.
Source sse_open is det
Source sse_open(+Options) is det
Emit the HTTP response headers that turn the current response into a Server-Sent Events stream and write the blank line that ends the headers. After this call the CGI stream is in event_stream transfer mode and each sse_send/1 or sse_send/2 is flushed to the client. The mandatory headers emitted are:
  • Content-Type: text/event-stream
  • Cache-Control: no-cache
  • X-Accel-Buffering: no (disables nginx response buffering)

cors_enable/0 is called after the mandatory headers, so the http:cors setting controls whether an Access-Control-Allow-Origin header is emitted. Browser-based EventSource clients on a different origin need this; non-browser clients do not.

Options:

headers(+List)
Additional response headers to emit, given as a list of Name-Value pairs. Both Name and Value are written with format/3 using `~w`.
retry(+Seconds)
After the headers, emit an initial `retry: Millis\n\n` field, which sets the client's reconnect delay in milliseconds, computed from Seconds.
Source sse_send(+Event) is det
Source sse_send(+Stream, +Event) is det
Emit one or more SSE events and flush the stream. Event is one of:
  • An atom or string. Shorthand for emitting a single event with just a `data:` field, splitting the text on line breaks.
  • A dict with any of the optional keys event, data, id, retry and comment. Unknown keys raise a domain_error/2.
    • event (atom or string) becomes one `event:` line.
    • id (atom, string or integer) becomes one `id:` line.
    • retry (non-negative integer) becomes one `retry:` line.
    • comment (atom, string or list) becomes one or more : lines (an SSE comment) before the rest of the event.
    • data may be an atom, string, integer or list. Atoms and strings are split on \n (with an optional \r) so that multi-line text becomes multiple `data:` lines. A list is taken to be a list of lines (which should not themselves contain newlines).
  • A list of any combination of the above. The whole list is flushed once at the end.

The empty event (a dict containing none of the recognised keys) is not allowed and raises a domain_error/2.

Source sse_comment(+Comment) is det
Source sse_comment(+Stream, +Comment) is det
Emit Comment as one or more SSE comment lines (: lines) followed by a blank line and flush the stream. Comments are ignored by clients and are mainly useful as a keep-alive heartbeat through intermediaries that close idle connections. Comment is an atom, string or list of lines; multi-line text is split as for data.