docs: README streaming/SSE sections, ROADMAP v0.3 done, crud SSE ticker (v0.3 chunk 4)

README: write_timeout in the Config sample, read/write timeout-split
semantics spelled out, new Streaming Responses and Server-Sent Events
sections (producer contract, 1.0 fallback, heartbeat liveness), roadmap
summary bumped. ROADMAP: v0.3 marked done with the as-landed decisions
(pull shape per the user's fork answer; Q3 timeout split as proposed)
and verified exit criteria. crud example: GET /ticker SSE route — one
event/second, producer exits on SseClosed or the example's shutdown
flag (so an open ticker doesn't block AllDone past the drain).
This commit is contained in:
Claude
2026-06-12 04:59:39 +00:00
parent d14fb7c331
commit 43bd5a91a4
3 changed files with 126 additions and 18 deletions
+24 -1
View File
@@ -204,6 +204,28 @@ fn parse_id(s: &str) -> Option<u64> {
// Handlers
// ---------------------------------------------------------------------------
/// SSE demo: `curl -N localhost:8080/ticker` streams a tick every second
/// (with `: keep-alive` comments if it ever goes quiet). The producer
/// exits on SseClosed (client gone / write timeout / shutdown drain) or
/// when the example is shutting down.
fn ticker(conn: Conn, _next: Next) -> Conn {
let (conn, events) = conn.sse();
smarm::spawn(move || {
let mut n: u64 = 0;
loop {
if SHUTTING_DOWN.load(std::sync::atomic::Ordering::Relaxed) {
return; // dropping `events` ends the stream cleanly
}
if events.send("tick", &n.to_string()).is_err() {
return; // SseClosed
}
n += 1;
smarm::sleep(std::time::Duration::from_secs(1));
}
});
conn
}
fn list(conn: Conn, _next: Next) -> Conn {
let (tx, rx) = channel::<(u16, Vec<u8>)>();
store().send(Request::List { reply: tx }).ok();
@@ -278,7 +300,8 @@ fn main() {
.post( "/users", create)
.get( "/users/:id", get_one)
.put( "/users/:id", update)
.delete("/users/:id", delete),
.delete("/users/:id", delete)
.get( "/ticker", ticker),
);
let cfg = Config::new("127.0.0.1:8080".parse().unwrap());