Cookbook / Realtime

Server-sent events

Push live updates to the browser over a plain HTTP response.

Server-sent events (SSE) are the simplest way to push data to a browser: a long-lived text/event-stream response with automatic reconnection built into EventSource. sse::channel gives you a sender and the response to return.

src/main.rs
use axeon::sse::{self, Event};
use axeon::{Html, Server};
use std::time::{Duration, Instant};

const PAGE: &str = r#"<!doctype html>
<h1>Uptime: <span id="t">...</span></h1>
<script>
  new EventSource("/events").addEventListener("tick", e => t.textContent = e.data);
</script>"#;

fn main() {
    let mut app = Server::new();

    app.get("/", |_req| async { Html(PAGE) });

    // A clock ticking once per second until the client disconnects.
    app.get("/events", |_req| async {
        let (tx, response) = sse::channel(8);
        tokio::spawn(async move {
            let started = Instant::now();
            loop {
                let event = Event::new().event("tick").data(format!("{}s", started.elapsed().as_secs()));
                if tx.send(event).await.is_err() {
                    break; // the client went away
                }
                tokio::time::sleep(Duration::from_secs(1)).await;
            }
        });
        response
    });

    app.listen("127.0.0.1:3000").expect("server failed to start");
}
Terminal
curl -N http://127.0.0.1:3000/events

Notes#

  • tx.send(..) fails once the client disconnects. That is your cue to stop the producer task.
  • Event also supports .id(..), .retry(..) and .json_data(&value).
  • On shutdown, event streams are ended cleanly so a deploy never hangs on open connections.