Workers Server-Sent events (server push)

We have been waiting a while for web socket support to get added but it looks like it won’t be ready for a while.

I was looking into alternative use cases, and I realised that maybe SSE works with workers and that could be utilised for unidirectional message flow from the edge. I did a quick POC and it seems like it does work.

I was wondering are there any restrictions of using workers like this? I noticed that on the limits page it says “There is no limit on the real runtime for a Workers script. As long as the client that sent the request remains connected…”.

I was thinking that maybe something like this could be used in combination with global variables, to create some sort of pub sub model.

Thanks in advance

This is the POC code:

addEventListener('fetch', event => {
  event.respondWith(fetchAndApply(event.request))
})

async function fetchAndApply(request) {

  let { readable, writable } = new TransformStream()
  var headers = new Headers();
  headers.append('Content-Type', 'text/event-stream');
  headers.append('Cache-Control', 'no-cache');
  headers.append('Connection', 'keep-alive');
  headers.append('Access-Control-Allow-Origin', '*');
  headers.append('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  var init = { "status": 200, "statusText": "ok", "headers": headers };

  writeToStream(writable)
  return new Response(readable, init)
}

async function writeToStream(writable) {

  let writer = writable.getWriter()

  var id = "id-test"

  // send first message
  await constructSSE(writer, id, "first message");

  // send message every 5 second
  setInterval(function () {
    constructSSE(writer, id, "repeated message");
  }, 5000);

}

async function constructSSE(writer, id, data) {
  let encoder = new TextEncoder()
  await writer.write(encoder.encode('id: ' + id + '\n'));
  await writer.write(encoder.encode("data: " + " - SSE Server - " + '\n\n'));
}
<!DOCTYPE html>
<html>

    <head>
        <meta charset="utf-8" />
        <title>demo</title>
        <link rel="stylesheet" type="text/css" href="app.css">
        <script src="vendor/eventsource.min-yaffle.js"></script>
    </head>

    <body>
        <script>
            const fn = function (e) {
                console.log(e.data);
            };

            const src = new EventSource("https://devdomain.workers.dev",);
            src.onmessage = fn;
 
        </script>
    </body>

</html>

@KentonVarda I was wondering if you can help with the answer for this, as you mentioned previously in the websockets thread:

I want to confirm if it is acceptable to have requests that run for a really long time?

Thank you in advance

Hi @nesh,

There is no explicit limit on real-world runtime of your code, but there is a limit on CPU time. Namely, for any one request, your worker can only spend up to 50ms executing JavaScript code. Each event here wouldn’t take much CPU time – probably less than a millisecond. But after a while they’d add up to 50ms, and then the request would be canceled. Since you’re already sending a response body, the connection would simply be dropped with no other error.

As long as your client-side code knows how to reconnect when this happens, though, it should work.

Also keep in mind when using global variables that Cloudflare runs many different copies of your worker around the world. Any two requests might not go to the same copy, so might not see the same global variables.

2 Likes

Awesome :slight_smile:

Thank you for the detailed explanation I really appreciate it.