How does server sent events or SSE work in Duct/Ring applications?
Ring has a protocol ring.core.protocols.StreamableResponseBody. that can be placed in the body of the response. This gives you access to an OutputStream you can use to write to the responose stream.
For example:
(defn handler [_request]
{:status 200
:headers {"Content-Type" "text/event-stream"}
:body
(reify ring.core.protocols.StreamableResponseBody
(write-body-to-stream [_ _response out-stream]
(with-open [w ( out-stream)]
(.write w "event: message\n")
(.write w "data: Hello World\n\n"))))})
If you want to keep the stream open after the handler function ends, you'll need to use Ring in async mode.
There might also be SSE libraries for Ring, but I haven't investigated that.Thanks, I want to send events after handler function ends.
In which case, you'll want to tell Ring you want async handlers. In Duct, you can do this by adding this to your duct.edn :system:
:duct.server.http/jetty {:async? true}
Then your handlers will take three arguments instead of one:
(defn handler [request respond raise]
(respond {:status 200, :headers ..., :body ...}))
The respond function is for sending the response, the raise function is for reporting errors.
Alternatively, you could use the normal synchronous mode of Ring and just pause the handler thread via a .join until you're finished. With virtual thread support in Jetty 12, this can be quite efficient.