Hi all. I'm trying to use some Server Sent Events with immutant.web.sse/send!
, but I'm not sure that what it's sending is entirely conformant with the spec: https://www.w3.org/TR/eventsource/. For example if I send {:data "YHOO\n+2\n10"}
in attempt to emulate an example in the spec, I believe I should receive three data:
lines in the SSE receiver, but instead what is being delivered is the following sequence of lines:
"data:YHOO"
"+2"
"10"
""
However this is not conformant. According to the spec the "+2"
and "10"
lines would be interpreted as field names, and corresponding values would be empty strings. What is the proper immuntant way to send multi-line data in an event? And is the above a bug? I think so.I could be misinterpreting the spec, I'm still looking at it, and trying to get a decent clojure SSE client to process SSE streams. Some of the ones I've looked at spend more time dwelling on use of clojure.core.async than on correctness of SSE client stream processing.
Hmmm, I'm guessing immutant.web.async/send!
calls wil let me send a conformant message, but it's a pity immutant.web.sse/event->str
doesn't do the right thing. Or have I got it all wrong? Anyway, suggestions/corrections welcome. From my perspective there is a documented protocol for SSE data transmission and ideally immutant's SSE send should conform to it if it wants conformant clients (such as web browsers and EventSource implementations) to work. If I develop any patch-worthy code I'll submit it, assuming I even have a clue, I'm new to SSE. I have an API that uses immutant SSE for select service calls, and clojure (not clojurescript) client processes that are subscribing to those SSE channels.
So (event->str {:data ["abc" "def"]}) => "data:abc\ndata:def\n"
is close, what we probably want is a method on the Event protocol for strings that splits them based on newlines (or CR/CRLF) to generate multiple data: elements.
But (event->str "abc\ndef") => "data:abc\ndef\n"
is not good.
It's been a long time since I looked at the sse code, but I recall that the intention was that if you wanted to send multiple data lines, you send a sequence: {:data ["line1" "line2" "line3"]}
and work with data structures instead of generating strings
But it probably makes sense to also support raw strings, and do the right thing with them in event->str
And why is sending a vector "close"? Is the event->str
result there incorrect?
It's fine if your data is already split into vectors, and "close" is because I was unsure when I wrote it (i.e. I wasn't sure).
I think something like this will do the trick if added, but I'm still trying to test it.
(extend-protocol immutant-sse/Event
String
(immutant-sse/event->str [str] (clojure.string/join "\n" (mapv (fn [s] (str "data:" s)) (clojure.string/split str #"(?:\r\n)|\r|\n")))))
That split/join logic will take a string with cr/lf/crlf terminated lines and break them up, including empty lines so that you still get data:\n
where appropriate
Anyway, looks like there are workarounds to my problem.
I want to make sure my SSE server will be conformant, because there will be clients in multiple languages, presumably using multiple (SSE reading) toolkits.
Hmm, not sure that's sufficient above, still grokking use of event->str
results.