Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript EventSource SSE not firing in browser

I have been developing a nodejs server to provide server-side-events for a new website I am developing in HTML5.

When I telnet to the server it works correctly, sending me the required HTTP response headers followed by a stream of events that i am presently generating every 2 or 3 seconds just to prove it works.

I have tried the latest version of FireFox, Chrome and Opera and they create the EventSource object and connect to the nodejs server OK but none of the browsers generate any of the events, including the onopen, onmessage and onerror.

However, if I stop my nodejs server, terminating the connection from the browsers, they all suddenly dispatch all the messages and all my events are shown. The browsers then all try to reconnect to the server as per spec.

I am hosting everything on a webserver. nothing is running in local files.

I have read everything I can find online, including books I've purchased and nothing indicates any such problem. Is there something Im missing?

A sample server implementation

  var http = require('http');
  var requests = [];

  var server = http.Server(function(req, res) {
    var clientIP = req.socket.remoteAddress;
    var clientPort = req.socket.remotePort;

    res.on('close', function() {
      console.log("client " + clientIP + ":" + clientPort + " died");

      for(var i=requests.length -1; i>=0; i--) {
        if ((requests[i].ip == clientIP) && (requests[i].port == clientPort)) {
          requests.splice(i, 1);
        }
      }
    });

    res.writeHead(200, {
      'Content-Type': 'text/event-stream', 
      'Access-Control-Allow-Origin': '*', 
      'Cache-Control': 'no-cache', 
      'Connection': 'keep-alive'});

    requests.push({ip:clientIP, port:clientPort, res:res});

    res.write(": connected.\n\n");
  });

  server.listen(8080);

  setInterval(function test() {
    broadcast('poll', "test message");
  }, 2000);

function broadcast(rtype, msg) {
  var lines = msg.split("\n");

  for(var i=requests.length -1; i>=0; i--) {
    requests[i].res.write("event: " + rtype + "\n");
    for(var j=0; j<lines.length; j++) {
      if (lines[j]) {
        requests[i].res.write("data: " + lines[j] + "\n");
      }
    }
    requests[i].res.write("\n");
  }
}

A sample html page

<!DOCTYPE html>
<html>
  <head>
    <title>SSE Test</title>
    <meta charset="utf-8" />
    <script language="JavaScript">
      function init() {
        if(typeof(EventSource)!=="undefined") {
          var log = document.getElementById('log');
          if (log) {
            log.innerHTML = "EventSource() testing begins..<br>";
          }

          var svrEvents = new EventSource('/sse');

          svrEvents.onopen = function() {
            connectionOpen(true);
          }

          svrEvents.onerror = function() {
            connectionOpen(false);
          }

          svrEvents.addEventListener('poll', displayPoll, false);             // display multi choice and send back answer

          svrEvents.onmessage = function(event) {
              var log = document.getElementById('log');
              if (log) {
                log.innerHTML += 'message: ' + event.data + "<br>";
              }
            // absorb any other messages
          }
        } else {
          var log = document.getElementById('log');
          if (log) {
            log.innerHTML = "EventSource() not supported<br>";
          }
        }
      }

      function connectionOpen(status) {
          var log = document.getElementById('log');
          if (log) {
            log.innerHTML += 'connected: ' + status + "<br>";
          }
      }

      function displayPoll(event) {
        var html = event.data;
          var log = document.getElementById('log');
          if (log) {
            log.innerHTML += 'poll: ' + html + "<br>";
          }
      }
    </script>
  </head>
  <body onLoad="init()">
    <div id="log">testing...</div>
  </body>
</html>

These examples are basic but of the same variety as every other demo i've seen in books and online. The eventSource only seems to be working if I end a client connection or terminate the server but this would be polling instead of SSE and I particularly want to use SSE.

Interestingly, demos, such as thouse from html5rock also seem to not quite work as expected when I use them online..

like image 261
Dan Avatar asked Oct 19 '12 16:10

Dan


People also ask

What is EventSource in JavaScript?

The EventSource interface is web content's interface to server-sent events. An EventSource instance opens a persistent connection to an HTTP server, which sends events in text/event-stream format. The connection remains open until closed by calling EventSource.

What is SSE in JavaScript?

Server-Sent Events (SSE) allow a web page to get updates from a server.

How do you test SSE?

To test it, you will normally open browser, making a connection to SSE channel and then wait for the push notification to receive on the browser when an event is triggered on the server. To test with multiple clients, you might open multiple browsers and do the same tests.


2 Answers

cracked it! :)

Thanks to some help from Tom Kersten who helped me with testing. Turns out the code isnt the problem.

Be warned.. if your client uses any kind of anti-virus software which intercepts web requests, it may cause problems here. In this case, Sophos Endpoint Security, which provides enterprise grade anti-virus and firewall protection has a feature called web protection. Within this features is an option to scan downloads; it seems that the SSE connection is treated as a download and thus not released to the browser until the connection is closed and the stream received to scan. Disabling this option cures the problem. I have submitted a bug report but other anti-virus systems may do the same.

thanks for your suggestions and help everyone :)

like image 178
Dan Avatar answered Oct 20 '22 13:10

Dan


http://www.w3.org/TR/eventsource/#parsing-an-event-stream

Since connections established to remote servers for such resources are expected to be long-lived, UAs should ensure that appropriate buffering is used. In particular, while line buffering with lines are defined to end with a single U+000A LINE FEED (LF) character is safe, block buffering or line buffering with different expected line endings can cause delays in event dispatch.

Try to play with line endings ("\r\n" instead of "\n").

http://www.w3.org/TR/eventsource/#notes

Authors are also cautioned that HTTP chunking can have unexpected negative effects on the reliability of this protocol. Where possible, chunking should be disabled for serving event streams unless the rate of messages is high enough for this not to matter.

like image 2
Victor Avatar answered Oct 20 '22 12:10

Victor