immutant

http://immutant.org Note: dev discussion happens in #immutant on FreeNode IRC.
jdt 2018-08-08T13:07:57.000335Z

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.

jdt 2018-08-08T13:10:28.000424Z

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.

jdt 2018-08-08T13:23:37.000385Z

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.

jdt 2018-08-08T14:21:53.000576Z

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.

jdt 2018-08-08T14:22:33.000167Z

But (event->str "abc\ndef") => "data:abc\ndef\n" is not good.

2018-08-08T14:27:04.000529Z

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"]}

2018-08-08T14:27:27.000376Z

and work with data structures instead of generating strings

2018-08-08T14:28:24.000404Z

But it probably makes sense to also support raw strings, and do the right thing with them in event->str

2018-08-08T14:29:11.000188Z

And why is sending a vector "close"? Is the event->str result there incorrect?

jdt 2018-08-08T15:04:46.000616Z

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).

jdt 2018-08-08T15:05:00.000480Z

I think something like this will do the trick if added, but I'm still trying to test it.

jdt 2018-08-08T15:05:06.000356Z

(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")))))

jdt 2018-08-08T15:06:03.000534Z

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

jdt 2018-08-08T15:08:29.000292Z

Anyway, looks like there are workarounds to my problem.

jdt 2018-08-08T15:09:04.000184Z

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.

jdt 2018-08-08T15:13:21.000138Z

Hmm, not sure that's sufficient above, still grokking use of event->str results.