ExAgent.SSE (ExAgent v0.4.1)

Copy Markdown View Source

Server-Sent Events framing.

Decoding is separated from transport because a frame can arrive split across two TCP reads: decode/1 returns only the frames that are complete, plus the remainder to prepend to the next read.

{frames, rest} = ExAgent.SSE.decode(buffer <> incoming)
# keep `rest` as the new buffer

A data: [DONE] sentinel decodes to the :done atom, so termination is a decoding concern rather than something each provider re-implements.

Summary

Functions

Decodes a buffer into complete frames plus the unconsumed remainder.

Extracts and joins the payload of every data: line in one SSE event.

Splits an SSE buffer into complete events plus the trailing partial event.

Types

frame()

@type frame() :: map() | :done

Functions

decode(buffer)

@spec decode(binary()) :: {[frame()], binary()}

Decodes a buffer into complete frames plus the unconsumed remainder.

Comment and keep-alive events (those with no data: line) and frames whose payload is not valid JSON are skipped rather than raising - a malformed frame should not tear down a stream that is otherwise fine.

Examples

iex> ExAgent.SSE.decode(~s(data: {"a":1}\n\ndata: [DONE]\n\n))
{[%{"a" => 1}, :done], ""}

iex> ExAgent.SSE.decode(~s(data: {"a":1}\n\ndata: {"b":))
{[%{"a" => 1}], ~s(data: {"b":)}

event_data(event)

@spec event_data(binary()) :: binary() | :done | nil

Extracts and joins the payload of every data: line in one SSE event.

Returns :done for a [DONE] sentinel and nil when the event carries no data.

Examples

iex> ExAgent.SSE.event_data("event: message\ndata: hello")
"hello"

iex> ExAgent.SSE.event_data("data: [DONE]")
:done

iex> ExAgent.SSE.event_data(": keep-alive")
nil

take_events(buffer)

@spec take_events(binary()) :: {[binary()], binary()}

Splits an SSE buffer into complete events plus the trailing partial event.

Events are separated by a blank line; the second element is always the (possibly empty) incomplete remainder, held back for the next read.

Examples

iex> ExAgent.SSE.take_events("a\n\nb\n\nc")
{["a", "b"], "c"}