Network.Wai.EventSource from wai-extra provides a simple api to allow sending Server-Sent Events (SSE) to clients.
You just provide a
Chan ServerEvent to
eventSourceAppChan and anything you write to this Chan is sent to the client that connects to the underlying endpoint (let’s call it SSE endpoint).
Now suppose I want ho have one
Chan ServerEvent for each user (identified by UserId) and I want to be able to send SSE events through it from other, normal HTTP handlers.
I can stick a
Map UserId (Chan ServerEvent) in some mutable variable inside my App’s ReaderT environment.
If user connects to SSE endpoint, I insert a new
Channel ServerEvent under their UserId into the Map.
Any time I want to send something to that client from other HTTP handlers, I just lookup the Chan in the map and send message to it via
writeChan usersChan someMsg.
Here’s a standalone gist with the source code extracted from larger App, stripped from all unnecessary complexity:
… and here’s a screenshot of example interactions
Now the problem I have is that with growing number of listening users, the number of Chans in the Map grows.
Question: Ideally I’d like to know when given UserId has no listening connections and remove that user’s Chan from the Map. But I didn’t figure out a way to detect whether given SSE client disappeared (e.g. because they closed their browser). Is there a way to do this?
I searched all the usage of eventSourceAppChan on github,
but most of them either just create a single Chan for the whole app, or they just accumulate Chans somewhere without ever cleaning them up.
Map UserId (Chan ServerEvent) reasonable idea to share state between handlers? Could it be made to work?
I saw the yesod seems to have some support for this, but I’m using Servant and would like to stick with it.
Please note I’m aware that WebSockets exist, but I’m interested if there’s a way to solve this via EventSource.