Server-Sent events is a standard that describes a way for servers to send updates to clients through a persistent HTTP stream.
SSE doesn't rely on any new technology. Instead, it uses features already existent on HTTP.
This is an example of how an
event-stream response looks like:
HTTP/1.1 200 OK Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive Transfer-Encoding: chunked : Welcome! this is a comment. id: 1535462271572 data: my first message! id: 1535462271572 data: another message event: my_event [...]
In a event stream all information is sent as plain text in UTF-8. Each event is separated by an empty line and each line defines a field. The standard describes how clients should react to those messages and how to parse them. This is made through an interface named
EventSource, which looks like this:
After the first handshake no messages are sent from client to server, only the other way around. This means event streams provide one-way communication where the server talks and all clients listen.
Every client receive the same data in the same order. When a new client connects, it receives all the messages sent in the stream before it's connection.
In case of failure, the standard describes a way for clients to retrieve the stream starting from the last event received. The standard doesn't cover questions like message expiration or limits. It's up to the server to handle those questions.
Those are just some highlights. The standard contains a lot more about how to handle reconnections, redirects, failures and messages.
As I said before, SSE relies on features already existent in the HTTP implementation. Let's check how they are used.
Under the hood
In order to easily explain how SSE works let's remember, in a simplified way, how a normal HTTP request flow works.
Let's say you want to access the resource
https://thisisvini.com/about/. This is what happens between your client/browser and the server:
As the server is listening to incoming connections when the client contacts it a TCP socket is opened between them. After the connection is established the following conversation happens:
GET /about/ HTTP/1.1 Host: thisisvini.com Accept: */*
"Yo! Could you give me the resource
/about. By the way, I know how to communicate using HTTP/1.1, and to be honest, I don't really care about the type of the content you'll send me."
HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 7915 <html>...
"Sure thing! I can do that. I'll send you HTML. Here it is [...] See you later"
The content is streamed to the client in chunks of data. After all the content is transmitted the socket is closed by the server and this polite conversation is over.
As we can see, TCP and HTTP already allow us to send chuncked data and keep a stream open between server and client. The missing part was defining a standardised way for servers and clients to deal with communication through a long-lived stream.
The only real difference between the previous flow and an event stream is that instead of closing the connection, the server would keep it open sending new chunks of data when necessary.
Obviously there are more details and bureaucracy in this conversation. You can check the living standard for more details.
Show me the code
Bohemian Rhapsody stream
If you want to see a real stream working you can
curl my Bohemian Rhapsody stream with the following command:
curl -i -k -H "Accept: text/event-stream" http://playground.thisisvini.com/bohemian-rhapsody-event-stream
This stream sends messages in sync with Bohemian Rhapsody song. Just press play when the
[START] message is received and sing together. :P
SSE-Client/EventSource implementation in Rust
I recently implemented a SSE-Client in Rust for a side project.
This library is fully functional, following the living standard and even implementing an exponential back off for reconnections.
SSE is really good for sending live data and broadcasting updates for many clients.
Things to keep in mind:
- SSE is one-direction.
- There is no message acknowledge mechanism.
- On reconnect you can start from the last event received, however you'd better off ensuring your clients are idempotent.
- The stream of data can get really big. Luckily servers like nchan have features to mitigate this problem, such as message expiration, max number of messages in a stream and channels.
- Even though the standard was devised to be used in browsers, many languages have libraries implementing the EventSource interface allowing event streams to be used everywhere.
- People usually compare SSE to Web Sockets, but I feel both solve different problems.
That's all for now. Thanks for reading.
If you have something to add, questions or suggestions don't exitate to contact me.