Release 2.4.0: Native Server-Sent Events support#63
Merged
dcrockwell merged 4 commits intomainfrom Mar 11, 2026
Merged
Conversation
## Why This Change Was Made - The existing `sse_response` function used chunked transfer encoding (`Stream(yielder)`) which Mist converted to `Chunked(yielder)`. The yielder blocked in the mist handler process's mailbox, competing with TCP messages (ACKs, window updates), causing SSE streams to stall after 2-3 events. - A proper SSE implementation requires a dedicated OTP actor per connection with its own mailbox, matching how Mist handles WebSockets. ## What Was Changed - Added `src/dream/servers/mist/sse.gleam` module with `upgrade_to_sse`, `send_event`, event builders (`event`, `event_name`, `event_id`, `event_retry`), and action helpers (`continue_connection`, `continue_connection_with_selector`, `stop_connection`) - Follows the same stash-and-upgrade pattern as `websocket.gleam` — retrieves the raw Mist request from the process dictionary, calls `mist.server_sent_events`, stashes the resulting response - Deprecated `response.sse_response` with doc comment directing users to the new module - Added unit tests, tested documentation snippets, and a full `examples/sse/` example app with Cucumber integration tests - Added `docs/guides/sse.md` guide, updated `docs/reference/streaming-api.md` and `docs/guides/streaming.md` - Bumped version to 2.4.0, updated CHANGELOG.md, created release notes ## Note to Future Engineer - Dream's `SSEConnection` wraps Mist's opaque `SSEConnection` which wraps an internal `Connection(body, socket, transport)`. If you ever need raw socket access (e.g., for `send_raw`), capture socket/transport from `mist_request.body` at upgrade time rather than cracking open the opaque type later. Your future self will thank you when Mist bumps a minor version. - The old `sse_response` is deprecated but not removed. If you're reading this in 2028 wondering why it's still here — congratulations, you found the tech debt. The `data:` prefix adds itself, like a clingy coworker who cc's themselves on every email.
## Why This Change Was Made - CI was hanging indefinitely during SSE integration tests because the readiness check curled the `/events` SSE endpoint, which is a long-lived stream that never closes - `curl -s http://localhost:8081/events` connects, receives the 200 + headers, then blocks forever waiting for the stream to end — so the readiness loop never advances ## What Was Changed - Added a `/health` endpoint to the SSE example router that returns a plain 200 "ok" response - Changed the Makefile readiness check from `/events` to `/health` ## Note to Future Engineer - Every other example curls a normal HTTP endpoint for readiness. SSE and WebSocket endpoints are long-lived — never use them for health checks unless you enjoy watching CI spin like a loading screen from 2005.
## Why This Change Was Made
- CI was hanging because the "SSE endpoint returns correct headers" scenario did a synchronous `HTTPoison.get` to the `/events` SSE endpoint
- SSE streams never end, and the server sends events every second, so `recv_timeout` never fires (data IS being received) — the call hangs forever
## What Was Changed
- Merged the header assertions into the "SSE endpoint streams events" scenario, which already uses async streaming
- The SSE connect step now captures response headers from `AsyncHeaders` instead of discarding them
- Added a new step definition `the SSE response header {string} should contain {string}` for header assertions on async SSE connections
- Removed the standalone headers scenario that used synchronous HTTP
## Note to Future Engineer
- Never use synchronous HTTP requests to test SSE or WebSocket endpoints. They stream forever. Use async connections and check headers from the handshake phase.
- If you're tempted to add `recv_timeout: 1_000` as a "fix" — it won't work. The server keeps sending data, so recv_timeout resets with every event. It's not a timeout problem, it's a "this stream literally never ends" problem.
Fix SSE streams stalling by using dedicated OTP actors
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release 2.4.0
Adds native Server-Sent Events (SSE) support backed by dedicated OTP actors, fixing a critical bug where SSE streams stalled after 2–3 events.
What's in this release
New:
dream/servers/mist/ssemoduleupgrade_to_sseupgrades HTTP requests to SSE connections, each with a dedicated OTP actor and mailboxevent,event_name,event_id,event_retry) with pipeline syntaxcontinue_connection,continue_connection_with_selector,stop_connection)Bug fix: SSE streams no longer stall
The old
sse_responseused chunked transfer encoding, which blocked in the mist handler process's mailbox competing with TCP messages. The new module gives each SSE connection its own OTP actor, completely eliminating the contention.Deprecated:
response.sse_responseMarked deprecated with migration guidance. Not removed — existing code continues to compile.
Documentation and testing
docs/guides/sse.mdguidereleases/release-2.4.0.mdCommits
cad9983feat: add native SSE support via dedicated OTP actors679853dfix: use health endpoint for SSE integration test readiness checka982444fix: remove synchronous GET to SSE endpoint in integration testsChangelog
See CHANGELOG.md for the full list of additions and deprecations.