[HN Gopher] Server Sent Events
       ___________________________________________________________________
        
       Server Sent Events
        
       Author : corentin88
       Score  : 54 points
       Date   : 2023-05-15 19:12 UTC (3 hours ago)
        
 (HTM) web link (developer.mozilla.org)
 (TXT) w3m dump (developer.mozilla.org)
        
       | 0xCAP wrote:
       | Curious why is this trending? Btw sse are pretty great, only
       | thing I don't get is why they don't allow for binary payloads.
       | That'd have been the icing on the cake.
        
         | mikek wrote:
         | OpenAI uses this in their APIs to stream tokens
        
         | asplake wrote:
         | Probably as a result of
         | https://news.ycombinator.com/item?id=35937554
        
           | marpstar wrote:
           | My first thought was ChatGPT API's `stream` option:
           | https://platform.openai.com/docs/api-
           | reference/completions/c...
        
         | b33j0r wrote:
         | This got into the zeitgeist somehow. I just implemented my
         | first SSE app with FastAPI last week, then I saw a youtube
         | video and this post!
         | 
         | I was implementing something with websockets and asked chatgpt
         | to tell me about modern alternatives. In my years of full-
         | stack... I hadn't heard of SSE, or at least never got why I
         | should care.
         | 
         | Damn, I see the limitations of REST, but I was building state
         | machines and custom protocols with websockets. It's nice to
         | prototype in REST again.
         | 
         | REST+SSE ftw, I guess!
        
         | VWWHFSfQ wrote:
         | > only thing I don't get is why they don't allow for binary
         | payloads
         | 
         | I've always appreciated that it's just a simple line-oriented
         | UTF-8 text protocol. Binary transfers are handled more
         | efficiently with something else. Or just base64 the payload if
         | it's not that big of a deal.
        
       | mr-wendel wrote:
       | My favorite use of this that I've spotted in the wild is
       | https://nightride.fm (not affiliated, just a fan!). It's a
       | Synthwave music site + community.
       | 
       | In particular, I noticed that EventSource goodies are used to
       | broadcast currently playing songs on each station, as well as to
       | provide anon read access to their IRC server via a simple vanilla
       | javascript webapp.
       | 
       | WAY cleaner and easier than web sockets for this use case, and
       | CDN friendly to boot!
        
       | [deleted]
        
       | rektide wrote:
       | I wish there were a way to push not just anonymous data/events
       | but http _resources,_ to similar effect.
       | 
       | SSE & websockets & http streams can send anonymous data, but I'd
       | like to be able to send an actual thing. As per the most core
       | architecture of the web:
       | 
       | > _Axiom 0a: Universality 2. Any resource of significance should
       | be given a URI._ https://www.w3.org/DesignIssues/Axioms.html#uri
       | 
       | Or alternatively,
       | 
       | > _Cool URLs don 't change_ (implicitly, cool things have URLs,
       | see above). https://www.w3.org/Provider/Style/URI
       | 
       | Instead of pushing to a chat app an anonymous blob of json for a
       | chat message to a room, the server could assert a
       | /room/42/msg/c0f3 resource, could identify universally what it is
       | it's sending. The advantage would be so high. It'd become a
       | standard way to assert a resource, to make known a fact, that
       | would be viable across systems. Further http requests to the
       | resource would just work. Folks could link to the resource. How
       | cool it would be if we could _send not just data but resources?_
       | 
       | We have come glancingly close to getting such a thing so many
       | times. The HyBi mailing list that begat websockets had a number
       | of alternate more resourceful ideas floating around such as a
       | BEEP protocol that allowed patterns beyond request/response of
       | resources. The browser actually implements an internal protocol
       | that uses HTTP2/push to send resourceful messages, web push
       | protocol/RFC8030, https://datatracker.ietf.org/doc/html/rfc8030.
       | 
       | But the underlying http2/push was de-implemented for webserving
       | in general, and even when it was available, it lacked the oft
       | requested ability to get notice of new pushed resources.
       | (https://github.com/whatwg/fetch/issues/65 was an old request.
       | https://github.com/whatwg/fetch/issues/607 had some steam in
       | making it happen.) Tragic backwards moves in my view, and never
       | acknowledged by blink-dev when protests arose.
       | 
       | The best we have today is to stream json-ls events, which have an
       | _@id_ property identifying them. But developers would have to
       | snoop these events, and store them in a service worker, to make
       | them actually accessible as http resources.
       | 
       | I continue to hold hope eventually we'll get better at using urls
       | to send data, to assert new things happening... But it's been
       | nearly 30 years of me hoping, and with some fleeting exceptions
       | the browser teams have seemed disinterested in making urls cool,
       | in spite of a number of requests. We've been left at un-web side-
       | channels like SSE & Websockets for a long long time now. Wouldn't
       | it be nice to see some real growth for http capabilities that we
       | really can use, that are more than abstract underlying transport
       | tweaks like h2 and h3?
        
       | quechimba wrote:
       | SSE is pretty good but I have never been able to see the messages
       | in firefox devtools, and doesn't work with binary data.
       | 
       | HTTP/2 streams are just as easy to implement, and work both ways
       | and support binary data.
        
       | toomim wrote:
       | ChatGPT uses SSE to stream the AI's responses, token-by-token!
        
       | paxys wrote:
       | I was looking through the implementation of chat.openai.com
       | recently and was pleasantly surprised to see that it was 100%
       | Server Sent Events, no Websockets.
       | 
       | SSE is a great tool to have in your belt. I'd say it can replace
       | the majority of use cases for Websockets out there today
       | (basically pushing small amounts of text/JSON data to the client)
       | with a tiny fraction of the complexity. Opening and maintaining a
       | bidirectional TCP stream is otherwise a huge pain in the ass.
        
       | kreetx wrote:
       | Server Sent Events infamously have low global connection limit
       | per origin[1], so if you have multiple tabs open for a single
       | site you'll run into it pretty quickly. If it weren't for that,
       | they would be great.
       | 
       | [1] https://stackoverflow.com/questions/18584525/server-sent-
       | eve...
        
         | VWWHFSfQ wrote:
         | Sophisticated services that offer SSE will have many different
         | endpoint domains to get around browser limits on simultaneous
         | connections to the same service.
         | 
         | endpoint-00.example.com endpoint-01.example.com
         | endpoint-02.example.com
         | 
         | etc. It isn't specific to SSE. It's any HTTP request. SSE is
         | just the most obvious because the connections stay open for
         | long periods.
        
           | paulddraper wrote:
           | And that's an easy workaround, with wildcard DNS entries and
           | certs.
        
         | flaviut wrote:
         | I've never worked with Web Workers, but the doc page mentions
         | them at the very top. Isn't that the intended workaround? Have
         | a background thread that receives the events & coordinates,
         | rather than having each page deal with them?
        
           | silverwind wrote:
           | Yes, that's `ShardedWorker`.
        
             | nikeee wrote:
             | I wanted so use these in a project recently, only to find
             | out that they aren't supported on Chrome for Android. This
             | makes them non-usable for me, since there is no drop-in
             | polyfill available yet.
        
               | silverwind wrote:
               | If you are doing websocket in there, you could fall back
               | to main thread, but it comes at the cost of code
               | duplication.
        
         | toomim wrote:
         | This problem is fixed by using HTTP/2.
        
           | silverwind wrote:
           | No, the browser's HTTP connection limits still apply on
           | HTTP2, each `EventSource` per tab counts against this limit
           | of I think 6 (?) connections.
           | 
           | Websockets to my knowledge have no such limit because they
           | are not HTTP requests.
        
             | makkesk8 wrote:
             | This is simply not true, it's negotiated between the client
             | and server and defaults to 100 usually. 6 is the limit for
             | http 1.1 not http/2.
             | 
             | Source: https://developer.mozilla.org/en-
             | US/docs/Web/API/Server-sent...
        
             | nitwit005 wrote:
             | I believe you have it essentially backward. Websockets
             | consume a connection no matter what, as the protocol does
             | not work with HTTP2 (there was an attempt, but I believe it
             | wasn't adopted and abandoned).
             | 
             | The SSE requests do work with HTTP2, as do some other old
             | methods like long polling. All the tabs will generally
             | share one connection to that domain.
        
       | MetalMatze wrote:
       | SSE are amazing! I've used them to push updates to clients from
       | multiple goroutines (Go threads) with https://lastfm.live
       | 
       | It's quite simple to start with on the Go backend side of things
       | and the frontend things aren't too complicated either.
        
       | clessg wrote:
       | For my fellow high-IQ individuals initially confused by the
       | seeming lack of content in the article, it's under 'Guides':
       | https://developer.mozilla.org/en-US/docs/Web/API/Server-sent...
       | 
       | A bit easy to miss, but includes some actual code samples and a
       | really nice walkthrough. Highly encouraged even if you already
       | "know" SSE and want to solidify your knowledge, I like how it
       | doesn't hide any of the details.
        
       | davidw wrote:
       | How well do these work out in practice across a variety of
       | platforms, behind firewalls, on phones, and so on?
        
         | [deleted]
        
         | lecarore wrote:
         | I use this at work (Enterprise SAAS web software) and we had to
         | implement a backup "polling every 5 seconds" for clients that
         | would not support it. It's quite rare though. We assume that
         | some caching proxy might be waiting for the GET request to be
         | fully done to cache it and then give it to the user or
         | something like this. The SSE would then never get updates,
         | while every normal HTTP request would work
        
         | Mogzol wrote:
         | They're well supported on modern browsers, both mobile and
         | desktop. If you need to support IE then you can use a polyfill.
         | It's essentially just a long-running HTTP request. The main
         | issue I've run into before is if your server has a response
         | timeout configured then you'll need to disable or work around
         | it, otherwise it will disrupt the event stream whenever the
         | timeout is hit, since the event stream is just an HTTP
         | response.
        
       | pieter wrote:
       | Note that this really isn't anything except the agreement to send
       | messages separated by 2 new lines. The SSE object in browsers is
       | old and quirky; for example, it only supports GET requests, which
       | means you'll hit path length limits if you use it for something
       | like LLM completion with large prompts.
       | 
       | Luckily since there's nothing special about the browser support,
       | you can very easily replace it with custom implementations.
       | https://www.npmjs.com/package/@microsoft/fetch-event-source for
       | example offers mostly the same API, but does offer POST requests
       | and a bunch of other goodies.
        
         | hamandcheese wrote:
         | This seems like a great solution for streaming responses scoped
         | to a single request. Neat!
         | 
         | I've always used SSEs to open a more persistent comms channel
         | to a browser which creates way more state and complexity both
         | on the client and the server.
        
         | jraph wrote:
         | There is this agreement, and also the fact that browsers can
         | forget previous messages, unlike any xhr-based custom
         | implementation since the EventSource object does not provide
         | methods to access previous contents. I don't know how well this
         | package handles this.
         | 
         | I agree with the note about the auto retry mechanism of
         | EventSource. It does no good, is unavoidable and a big
         | annoyance. The work we had to do to work around it in Tracim
         | (previous job) [1] was pure madness. I see this part has not
         | been touched since I left by the way.
         | 
         | https://github.com/tracim/tracim/blob/develop/frontend_lib/s...
        
       | patwolf wrote:
       | I did a web app project with SSEs a while back. They worked well
       | in the browser, but when it came time to build a mobile app
       | (React Native), I had trouble finding a good library to use. I'm
       | sure I could have rolled my own, but being in a time crunch I
       | ended up using Firebase Cloud Messaging. Hopefully there's better
       | support these days.
        
       | no_wizard wrote:
       | I know alot of websites and engineers use Websocket for _push
       | only_ data. SSE is tailored made for real time one sided updates!
       | 
       | In fact, I think it made a better chat protocol, when we built a
       | chat system at a previous job, similar to Slack. We ended up
       | leveraging SSE to push updates to channels and traditional HTTP
       | requests to send data to the server, resulting in lower latency
       | and less overhead on the server side. We did this because we
       | found the following to be true:
       | 
       | - At any given time, there are far more people reading chat
       | messages than writing them
       | 
       | - What matters is that updates after being sent to everyone else
       | is that their pretty close to instantaneous but you had a 500ms
       | window to allow for the server to receive a new message and then
       | propagate it out. Therefore, for the sender, you update the UI
       | optimistically so they aren't waiting for a response, and
       | everyone else will get it in ~500ms and its imperceptible
       | 
       | - one downside: its harder (we never figured it out before I
       | left) to do indicators for when someone is typing. That really
       | does seem to require two way real time connections
        
         | stevenguh wrote:
         | Slack does use websocket for "someone is typing" event:
         | https://slack.engineering/real-time-messaging/
        
         | franky47 wrote:
         | One major issue I encountered with SSE is dealing with
         | reconnection. WebSocket makes it very easy to detect
         | differences between a loss of network connectivity, a server
         | dying, and a client exiting properly (eg: closing the tab), on
         | either side of the socket.
         | 
         | Maybe there are facilities to do so with SSE that I don't know
         | of, but my experience in reliable server-to-client-only comms
         | with it has been a bit rough.
        
           | VWWHFSfQ wrote:
           | You pretty much just have to listen for and handle premature
           | closing of the underlying TCP connection. It's the only form
           | of dead peer detection you'll get if someone just abrubtly
           | closes their browser tab.
        
             | teaearlgraycold wrote:
             | Do server frameworks really require you to think of the TCP
             | connection?
        
         | marcellus23 wrote:
         | What was the challenge in typing indicators? My naive idea is
         | that it's basically sending `typing_started` and `typing_ended`
         | messages.
        
           | yamtaddle wrote:
           | You can skip the "typing ended". You're gonna need a shortish
           | timeout anyway, may as well just let the "typing is
           | happening" messages _all_ terminate by timeout.
        
             | marcellus23 wrote:
             | That really depends on the UX you want.
        
               | yamtaddle wrote:
               | What do you have in mind? I can't think of a case where
               | you'd want "X is typing" to persist more than a second or
               | _maybe_ two past the last-received message that affirmed
               | X is, in fact, still typing. I can think of cases in
               | which a short timeout might break down in bad ways
               | (making the notice really jittery on high-latency
               | connections, say) but I think those are probably just
               | situations in which any  "... is typing" message is
               | simply gonna be bad in one way or another.
        
           | VWWHFSfQ wrote:
           | I'm guessing that since it's not a full-duplex connection you
           | have to send those messages out-of-band, ie, with a separate
           | POST request API
        
             | hamandcheese wrote:
             | But messages themselves are already sent out-of-band as a
             | POST request, if I understand correctly. Don't see why that
             | couldn't be extended to include typing indicators.
        
         | twic wrote:
         | The big problem i've run into with SSE is the connection limit
         | [1]:
         | 
         | > Warning: When not used over HTTP/2, SSE suffers from a
         | limitation to the maximum number of open connections, which can
         | be specially painful when opening various tabs as the limit is
         | per browser and set to a very low number (6). The issue has
         | been marked as "Won't fix" in Chrome and Firefox. This limit is
         | per browser + domain, so that means that you can open 6 SSE
         | connections across all of the tabs to www.example1.com and
         | another 6 SSE connections to www.example2.com. (from
         | Stackoverflow). When using HTTP/2, the maximum number of
         | simultaneous HTTP streams is negotiated between the server and
         | the client (defaults to 100).
         | 
         | The natural way to use SSE is to create an EventSource on each
         | page. If you do this, and your user opens six tabs, they can
         | now no longer make HTTP requests to your site. Not just SSE
         | requests, any HTTP requests at all!
         | 
         | It's also quite natural, i think, to want to use multiple
         | separate streams. It's very convenient to write separate
         | endpoints for various kinds of data that a page might want, for
         | example one for streaming numerical data, one for control
         | messages, and one for descriptive events; or one for each panel
         | on a dashboard. Here, you can hit the limit on a single page.
         | 
         | I wrote a thin layer over my HTTP server's APIs which lets me
         | write a single handler which can send events via either SSE or
         | a websocket. Then, when i build a page, i use websockets, but
         | if i want to debug, i can still use curl to make a normal HTTP
         | request. Also, other apps can use SSE to pull data, which only
         | requires an HTTP client, not a websocket client.
         | 
         | [1] https://developer.mozilla.org/en-
         | US/docs/Web/API/EventSource
        
         | jraph wrote:
         | I used both WebSockets and SSE. In one project, I even support
         | WebSockets and a fallback working like what you describe.
         | 
         | I'm sold on using SSE for pushed updates. But how is regular
         | requests + SSE easier on the server compared to WebSockets? It
         | seems like an open connection to maintain in both cases plus
         | additional http request for the SSE+requests case.
        
       | schmichael wrote:
       | What is the advantage of SSE over streaming ndjson?
        
         | paulddraper wrote:
         | Built-in client API for parsing and event dispatching. Data
         | doesn't have to be JSON encoded.
         | 
         | But fundamentally nothing that Comet, steaming XHR, etc,
         | WebSockets, can't do.
        
         | [deleted]
        
       | collaborative wrote:
       | Not really related, but this is GAE's missing feature IMO
        
       | [deleted]
        
       | spullara wrote:
       | SSE is phenomenal and underutilized because it wasn't supported
       | by IE.
        
       | asynchronous wrote:
       | Love to see stuff like this on the HN front page, when I know
       | tons of web devs that have never even heard of SSE.
       | 
       | Tangentially, I'm having a massive problem with SSE datastreams
       | erroring out about two minutes in for "ERR: Network chunk
       | encoding" reasons, anyone else seen this before?
        
         | no_wizard wrote:
         | Try sending the messages without compression. A lot of servers
         | don't know how to handle this for SSE (since its a uncommon
         | content type)
        
       ___________________________________________________________________
       (page generated 2023-05-15 23:02 UTC)