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:

// JavaScript
const eventSource = new EventSource('http://stream/sub');

eventSource.onmessage = function(e) {
	console.log(e.data);
};

eventSource.addEventListener('my_event', function(e) {
	console.log(e.data);
});

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:

Client:

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

Server:

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

render1535538148153

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.
Source code
Documentation

Use Cases

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 it

That's all for now. Thanks for reading.

If you have something to add, questions or suggestions don't exitate to contact me.