Server-Sent Events
Server-Sent Events let a handler keep the response open and send events to the browser as they happen. This handler counts down from 10, one event a second:
fn countdown(req: *http.Request, res: *http.Response) !void {
req.setTimeout(.none);
var buf: [4096]u8 = undefined;
var events = try res.startEventStream(&buf);
var n: u32 = 10;
while (n > 0) : (n -= 1) {
const id = try std.fmt.allocPrint(req.arena, "{d}", .{n});
try events.send(id, .{ .event = "tick", .id = id });
try req.io.sleep(.fromSeconds(1), .awake);
}
try events.send("liftoff", .{ .event = "done" });
}
res.startEventStream sends the headers for an
event stream and returns an http.EventStream. Each send sends
one event to the client right away. The response ends when the handler returns.
buf is where each event is put together before it's sent. Make it big enough for a normal event,
so each one goes out in a single write.
In the browser, an EventSource gets the events:
const source = new EventSource("/countdown");
source.addEventListener("tick", (e) => console.log(e.data));
source.addEventListener("done", () => source.close());
Without the close, the browser reconnects when the stream ends, and the countdown starts over.
Event Fields
The second argument to send sets the other fields of the event:
.eventis the event type. The browser'sEventSourceuses it to pick the listener..idis the event ID. A browser that reconnects sends the last one it got in theLast-Event-IDheader..retrytells the browser how many milliseconds to wait before it reconnects.
Writing an Event in Pieces
startSend opens an event and gives you a writer for its data. Use it when the data comes from a
formatter or a serializer:
fn status(req: *http.Request, res: *http.Response) !void {
var buf: [4096]u8 = undefined;
var events = try res.startEventStream(&buf);
var event = try events.startSend(.{ .event = "status" });
try event.interface.print("{{\"method\":\"{f}\",\"path\":\"{s}\"}}", .{ req.method, req.url });
try event.end();
}
Call end when the event is done, before you send the next one.
When the Client Goes Away
When the client closes the connection, a later send returns an error. Return it from the handler
to end the request.
Timeouts
The request timeout covers the whole stream. It's 30 seconds by default. Long streams turn it off
with req.setTimeout(.none), as the countdown handler does. If a stream should stop after a fixed
time, set a timeout that long instead.
On shutdown, the server waits for streams that are still open, like any other request. It waits up
to timeout.shutdown, 30 seconds by default, and then cancels them. See
Deployment for more about shutting down.
Next Steps
- WebSocket covers connections where the client sends messages too.
- Configuration covers the timeouts.