live-streaming
Live streaming and Server-Sent Events
A live action writes a response body incrementally instead of returning it all at once. It is the push counterpart of the pull-based stream primitive: the action runs on its own thread and writes chunks as they become available, while the adapter pulls them through and sends them to the client.
Live responses
live runs a block on a separate thread, passing the controller and a stream.
The block writes chunks with write and the framework closes the stream when the
block returns:
method numbers {
self.live(-> $controller, $stream {
for 1..10 -> $n {
$stream.write("$n\n");
}
});
}The action returns as soon as the block is spawned, so the dispatch loop is not
blocked while the body streams. The response reports is-live and is-streaming,
and live-promise is the Promise that completes when the writer thread
finishes.
A string chunk is encoded as UTF-8; a Blob chunk is written through unchanged.
Writing to a closed stream raises X::MVC::Keayl::Live::StreamClosed.
Client disconnects
When the client goes away, the adapter calls disconnect on the stream. The next
write then raises X::MVC::Keayl::Live::ClientDisconnected, which the action
catches to release resources and stop producing:
method feed {
self.live(-> $controller, $stream {
CATCH {
when X::MVC::Keayl::Live::ClientDisconnected { cleanup() }
}
loop {
$stream.write(next-chunk());
}
});
}is-disconnected reports whether the client has gone, and disconnect both marks
the stream and closes it.
Server-Sent Events
sse wraps the live stream in an SSE writer. It sets the text/event-stream
content type and a no-cache directive, then passes the controller and an SSE
writer to the block:
method events {
self.sse(-> $controller, $sse {
$sse.write('hello', event => 'greeting');
$sse.write('world');
});
}write emits one event frame. event, id, and retry precede the data, and a
multiline payload is split across data: fields:
event: greeting
data: hello
data: worldPass defaults to sse to apply them to every frame; a per-write option overrides
the default:
self.sse(-> $controller, $sse {
$sse.write('tick'); # retry: 5000 applied
$sse.write('pong', retry => 1000); # overridden for this frame
}, retry => 5000);comment writes an SSE comment line (: text), which serves as a heartbeat to
keep an idle connection alive without delivering an event:
$sse.comment('keep-alive');