Skip to content

Instantly share code, notes, and snippets.

@jareware
Last active April 15, 2024 02:23
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jareware/aae9748a1873ef8a91e5 to your computer and use it in GitHub Desktop.
Save jareware/aae9748a1873ef8a91e5 to your computer and use it in GitHub Desktop.
Server-Sent Events Demo

Server-Sent Events Demo

Server-Sent events is a specification for implementing server-side-push for web frontend applications, through plain-old HTTP.

It is best contrasted with WebSockets, which offer a full-duplex messaging channel over a custom protocol, operating within a single TCP connection (with an HTTP-compatible handshake).

Trying out the demo

  1. Clone this gist
  2. node server.js
  3. Navigate to http://localhost:8888/
  4. See the events start rolling in
  5. Navigate away from the page, and back again
    • the node console will report the client going away and coming back
    • the client will automatically reconnect to the event source
  6. While on the page, kill the node process, then restart it
    • the client will report loss of connection
    • the client will automatically reconnect when the server comes back
    • the client will inform the server of the last message it saw (see below)

Main disadvantages

  • Support is not universal
    • Neither is WebSocket's, though
    • The single major difference is in IE-land; not even 11 supports SSE
  • There is no return channel for the client to use

Main advantages

  • As WebSocket is a custom protocol, it needs explicit support
    • Which is far from being a given in more exotic network setups
    • Proxies/middleware are good examples of where WebSockets often break
  • Easy to test and write automation against, since it's just HTTP
  • Things like session management work exactly like the rest of your HTTP API
  • Some have reported better latency performance over WebSockets
    • Though I'm not entirely sure how that's possible; maybe browsers' HTTP stacks are more mature than the WebSocket counterparts?
    • Maybe some extra latency comes from the initial protocol switch?
  • Sometimes you simply don't need a full-duplex connection
    • Talk to server over standard REST JSON, for example
    • Deliver notifications over SSE
  • Browser handles reconnections automatically behind the scenes
    • If you tag your messages with ID's, the browser will provide the last ID it saw along with the reconnection, allowing you to send the correct amount of catch-up to the client
    • Your frontend-application won't even have to care about the ID; it's by the server, for the server
  • While both WebSockets and SSE will likely need a polyfill/fallback in the wild, note that:
    • To allow falling back from WebSockets, you need a separate, completely different API on the backend for that
    • Falling back from SSE can be handled entirely client-side
  • Forward-compatible with... whatever may come!
    • SSE is an abstraction over push-events, with management of the underlying connection offloaded to the browser
    • There's no reason HTTP needs to be the only available transport: mobile user-agents could use the push-notification service of their platform, for instance
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, height=device-height" />
<title>Server-Sent Events Demo</title>
<style type="text/css">
body {
font-family: 'Open Sans', sans-serif;
}
</style>
</head>
<body>
<h1>Server-Sent Events Demo</h1>
<ul></ul>
<script>
(function() { "use strict";
var ul = document.querySelector('ul');
var es = new EventSource('/events');
function li(text) {
var li = document.createElement('li');
li.innerText = text;
ul.appendChild(li);
}
es.addEventListener('open', function() {
li('Server connected :)');
});
es.addEventListener('my-custom-event', function(event) {
li(event.data);
});
es.addEventListener('error', function() {
li('Server unavailable :(');
});
})();
</script>
</body>
</html>
var fs = require('fs');
var http = require('http');
var connectionCounter = 1;
http.createServer(function(request, response) {
if (request.url === '/') {
response.writeHead(200, { 'Content-Type': 'text/html' });
response.write(fs.readFileSync('index.html'));
response.end();
} else if (request.url === '/events') {
var thisConnection = connectionCounter++;
var thisEvent = 1;
console.log('Client connected to event stream (connection #' + thisConnection + ', Last-Event-Id: ' + request.headers['last-event-id'] + ')');
response.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache' // let intermediaries know to NOT cache anything
});
var ticker = setInterval(function() {
response.write('event: my-custom-event\n');
response.write('id: ' + (thisConnection * 1000 + thisEvent) + '\n');
response.write('data: Server says hi! (event #' + thisEvent++ +' of connection #' + thisConnection + ')\n\n');
}, 2500);
request.on('close', function() {
console.log('Client disconnected from event stream (connection #' + thisConnection + ')');
response.end();
clearInterval(ticker);
});
} else {
response.writeHead(404);
response.end();
}
}).listen(8888);
@titan83
Copy link

titan83 commented Apr 23, 2018

@jareware, thanks for your example.
But I found very unpleasant problem and I still can't solve it(
Problem in browsers (Chrome at least): they send "heartbeat" messages to SSE server with some interval (Chrome - around 2 mins), and on server side I can't diffirenciate "heartbeat" and "new client" messages, so my server increase amount of input connections continously.
May be you know how to solve this issue?
Thanks.

@Binamra7
Copy link

Hey @titan83 , I am facing similar issue. Did you figure out a solution to this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment