How to use HTML5 server-sent events

Objective

After reading this tutorial you should be able to understand and take advantage of HTML5 server-sent events.

Requirements

  • No particular requirements needed

Conventions

  • # – requires given linux command to be executed with root privileges either
    directly as a root user or by use of sudo command
  • $ – given linux command to be executed as a regular non-privileged user

Introduction

Server-sent events is an HTML5 technology which allows a client to automatically monitor event notifications from a server, and react as needed. This technology is very useful to notify live events, to implement, for example, a live messaging application or a news feed. In this tutorial we will see how to implement this technology using PHP and javascript.

A simple example

For the sake of this tutorial, we will work with a list of “animals” that will be displayed in a simple html page. While in a real-world application the data would have been stored and retrieved from a database, in this case, for simplicity, we will use a php array. What we want to obtain is a real-time notification of the changes in the animal list, so that we can update our html page accordingly, without having to refresh it.



The Server side code

To begin with, let’s populate our little array of animals in the animals.php file (we are working in the root directory of our web server VirtualHost):

<?php
$animals = ["cat", "dog", "cow", "zebra", "snake"];

Save and close the file as animals.php. Now, for the most important part: we have to write the script which will emit the message that will be lately used by our client-side javascript code. With a lot of fantasy we will name the script script.php. The code is very simple, here it is:

<?php
header("Cache-Control: no-cache");
header("Content-Type: text/event-stream");

// Require the file which contains the $animals array
require_once "animals.php";

// Encode the php array in json format to include it in the response
$animals = json_encode($animals);

echo "data: $animals" . "\n\n";
flush();

The first thing to notice here is that we called the header function in Lines 2-3: this is a function used to send raw http headers. In this case we call it two times: the first in Lines 2 to setup the Cache-control header field and specify caching directives (no page caching), the second in Lines 3, to set the Content-Type to text/event-stream. Those headers setup is necessary for our script to work correctly. It’s also important to notice that to work correctly, the header function must always be called before any other output is created.

After setting up the html headers, we just used the require_once statement in Lines 6 to require the content of the animals.php file, which contains the array we wrote before. In a real-case scenario, this would have been replaced by a SQL query to retrieve such information from a database.

Finally in Lines 9-11, we sent our response to the client: the json-encoded “animals” array. A very important thing to notice: Server Side Events format requires each response sent by the server to be prefixed by the data: string and followed by two newline characters. In this case we used the \n newline character because we are running on a unix-like platform; to ensure cross-platform compatibility we would have used the PHP_EOL constant.

It’s even possible to break the response message on multiple lines: in this case each line, as said before, must start with “data:” and must be followed by a single newline character. The additional newline is required only on the last line.

The server can also control how often the client should try to reconnect (default is 3 seconds), and the name of the event (default is “message”) sent to the client. To customize the former, we must use the retry directive followed by the desired interval of time, expressed in milliseconds. For example, to setup an interval of 1 second:

echo "retry: 1000\n";

Notice that even here, a trailing newline is required. To change the event name, instead, we must use the event directive:

echo "event: customevent\n";

The default event is “message”: this is important because the event must be specified in the client javascript code when adding the event listener, as we will see in a moment.

After sending our response we called the flush function: this is needed to ouput the data to the client.



Client side code

First thing we are going to do client side is to prepare our html file with the list of available animals:

<?php require "animals.php"; ?>
<html>
  <body>
    <ul id="availableAnimals">
      <?php foreach ($animals as $animal) : ?>
        <li><?php echo $animal; ?></li>
      <?php endforeach ?>
    </ul>

    <script src="script.js"></script>

  </body>
</html>

This is really some basic html with a little bit of php to display the list of animals at the moment of the page loading and to include our .js file (script.js), but will server our purpose. Now, let’s see how actually we can use Server side events. The first thing we have to do is to instantiate an Event source object. In our javascript file, write:

let eventSource = new EventSource('script.php');

As you can see, we passed the path to our server script as an argument in the EventSource object constructor. This object will open a connection to the server. Now, we must add an event listener, so that we can perform some actions when a message is received from the server:

let eventSource = new EventSource('script.php');

eventSource.addEventListener("message", function(event) {
  let data = JSON.parse(event.data);
  let listElements = document.getElementsByTagName("li");

  for (let i = 0; i < listElements.length; i++) {
    let animal = listElements[i].textContent;
    if (!data.includes(animal)) {
      listElements[i].style.color = "red";
    }
  }
});

When a message is received, we use the JSON.parse method in Line 4 to transform the data sent by the server (a string, contained in the data property of the event object), into a javascript array.

After that we loop in Lines 7-11 through all the elements with the <li> tag, which are the elements of our list of animals: if some element does not appear to be anymore in the array sent by the server, the color of the text contained in the list is changed to red, because the “animal” is no longer available (a better solution would have been to only include the changed or missing element name in the server response, but our purpose here is just to demonstrate how the technology works). The change in the page will happen in real time, so no need to refresh. You can observe how our page takes advantage of server sent events, in the video below:

As you can see, as soon as the “cat” is removed from the “animals” array (our source of data) the element displayed in the html page is modified, to reflect that change.

The stream of data between the server and the client can be interrupted by using the close method of the eventSource object:

eventSource.close()

To handle connection open, and error events, dedicated event listeners can be added to the object.



Comments and Discussions
Linux Forum