Utilisation des envois d'événements côté serveur (server-sent events)

Cette traduction est incomplète. Aidez à traduire cet article depuis l'anglais.

Développer une application web qui utilise 'server-sent events' est assez facile. Vous aurez besoin d'un bout de code coté serveur qui va transmettre des informations en continu a l'application web, du côté de l'application web cela fonctionne à peu de chose près comme pour la plupart des événements.

Recevoir des événements du serveur

 

L'API SSE est contenue dans l'interface EventSource qui permet d'ouvrir une connection vers le serveur et commencer a recevoir des événements du serveur, vous devez créer un nouvelle objet EventSource, en spécifiant une URL d'un script coté serveur qui va générer les événements.

Par exemple :

var evtSource = new EventSource("ssedemo.php");

Une fois que vous avez instancié votre source d'événement, vous pouvez commencer à écouter les messages du serveur.

evtSource.onmessage = function(e) {
  var newElement = document.createElement("li");
  
  newElement.innerHTML = "message: " + e.data;
  eventList.appendChild(newElement);
}

Ce code écoute les messages entrants (qui viennent du serveur) et ajoute les messages textes a la suite de la liste HTML.

Vous pouvez aussi utiliser un écouteur d'événement avec addEventListener() :

evtSource.addEventListener("ping", function(e) {
  var newElement = document.createElement("li");
  
  var obj = JSON.parse(e.data);
  newElement.innerHTML = "ping at " + obj.time;
  eventList.appendChild(newElement);
}, false);

Ce code est similaire, à part qu'il sera appelé automatiquent avec le champ d'événement réglé sur "ping"; il transforme ensuite le message en format JSON du champ data et l'affiche.

Envoyer un événement depuis le serveur

Le script coté serveur qui envoie l'événement doit répondre en utilisant le type MIME text/event-stream. Chaque notification est envoyée en tant que bloc de texte qui finit par une paire de nouvelles lignes. Pour plus de détails sur le format d'envoi en continu, voir "Event Stream Format".

Voici le code PHP que nous utilisons pour notre exemple  : 

date_default_timezone_set("America/New_York");
header("Content-Type: text/event-stream\n\n");

$counter = rand(1, 10);
while (1) {
  // Chaque seconde, envoi d'un evenement de type "ping".
  
  echo "event: ping\n";
  $curDate = date(DATE_ISO8601);
  echo 'data: {"time": "' . $curDate . '"}';
  // Paire de nouvelle ligne
  echo "\n\n";
  
  // Envoie un message simple à des intervalles aléatoires.
  
  $counter--;
  
  if (!$counter) {
    echo 'data: This is a message at time ' . $curDate . "\n\n";
    $counter = rand(1, 10);
  }
  
  ob_flush();
  flush();
  sleep(1);
}

Ce code génère un événement "ping" chaque seconde. Chaque événement est un objet JSON, dans ce cas, juste ISO 8601 timestamp qui correspond à l'heure a laquelle l'événement a été généré. A intervalle aléatoire, un message simple (sans type d'événement) est envoyé.

Gestion d'erreurs (Error handling )

Losqu'un événement survient (tel un délai réseau trop long ou un problème d'autorisation d'accès), un événement erreur (error event) est généré. À partir de l'événement onerror de l'objet EventSource  vous pouvez récupérer l'erreur et effectuer le traitement à l'aide d'une fonction javascript.

evtSource.onerror = function(e) {
  alert("EventSource failed.");
};

Jusqu'à Firefox 22, il semble qu'il n'y ait aucune façon de distiguer les différentes événements erreur possible. 

Fermeture de connexion (Closing event streams)

Par défaut, si la communication entre le client et le serveur est interrompue, la connexion sere réinitialisée. Également, il est possible de terminer la connexion à l'aide de la méthode .close() de l'objet EventSource.

evtSource.close();

Format du flux d'événements (Event stream format)

Le format du flux d'événements est une chaine de données texte simple codées en UTF-8. Chaque chaine de message doit être séparé par deux lignes d'espacement.  

Note : Le caractère deux point (:) utilisé en début de chaine indique un message de commentaire. Ce type de message peut être utilisé pour maintenir l'état de la connexion active.     

: Ceci est un commentaire
 
data: Premier message 
 
data: Second message mais 
data: avec deux lignes 

Chaque message est formé d'une ou plusieurs lignes de texte dans laquelle on retrouve certains champs. Le nom du champ doit apparaitre en début de message suivit du caractère deux points (:). Ensuite, la  valeur du champ doit suivre sous forme d'une chaine de texte.

Champs

Les champs suivants sont définis par la spécification :

event
Le champ event corespond au type d'événement. Si le type d'événement est spécifié il sera envoyé vers le navigateur au gestionnaire définie pour l'événement spécifié. Le script doit contenir un gestionnaire avec le nom de l'événement a récupérer.
data
Le champ data correspond au message qui sera échangé. Lorsque l'objet EventSource reçois plusieurs lignes de messages consécutives commençant par le champ data: il en fera automatiquement la concaténation en ajoutant un code de retour ( \n )  entre chaque chacune d'elle. Sur la dernière ligne data le dernier code de retour (\n)  sera supprimé.
id
Le champ id correspond à un numéro ID que l'on peut attribuer à un événement EventSource. Ce numéro peut être récupéré à l'aide de la propriété event.id  
evtSource.onmessage = function(e) {
if(e.id === 911)
    alert('Message 911');
}
retry
Le délai de reconnexion est d'environ 3 secondes par défaut (3000). Le champ retry correspond au délai avant reconnexion. Le nombre suivant le champ retry doit être un entier qui représente le nombre de milième de seconde avant la reconnexion. Si une valeur invalide est spécifiée le champ retry sera ignoré et la valeur par défaut sera utilisée. 

Tous les autres champs autres que ceux décrit précédemment seront ignoré. 

Note:

Si une ligne de message ne contient aucun symbole deux points ( : ) , la ligne entière sera traité comme un nom de champ et le contenu sera considéré comme étant vide.   

Exemples

Messages de données seulement

Dans l'exemple suivant, on retrouve trois message envoyés. Le premier correspond seulement a un commentaire puisqu'il débute par le caractère deux points (:) Tel que mentionné précédemment, ce type de message peut être utilisé pour maintenir la connexion si des messages doivent être transmis de façon irrégulière. 

Le second message contient un champ data avec la valeur "some text". Le troisième message contient un champ data répartie sur deux lignes. Ce message deviendra "another message\nwith two lines" du côté client.  Notez que le caractère de retour (\n) sera inséré entre les deux lignes de message.

: this is a test stream

data: some text

data: another message
data: with two lines 

Événements nommés

Cet exemple transmets une série d'événements nommés. Chacun des messages a un nom spécifique qui est précédé par le champ event: tandis que le champ data: contient une chaine de données formatée JSON qui pourra être récupéré du côté client sur un événement message avec une instruction du genre var message = JSON.parse(e.data). Le champ data peut évidemment contenir une chaine de texte simple sans nécessairement être formaté JSON.

 

event: userconnect
data: {"username": "bobby", "time": "02:33:48"}

event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}

event: userdisconnect
data: {"username": "bobby", "time": "02:34:23"}

event: usermessage
data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}

Messages mélangés

Vous n'êtes pas obligé d'utiliser seulement des messages avec des événements nommés ou des chaines de données formatées JSON. Vous pouvez mélanger les deux types de messages dans un même événement. Dans cet exemple, l'événement 'userconnect' contient à la fois une chaine JSON suivi d'une chaine de texte simple qui pourrait être utilisé pour préciser l'événement par exemple. Ensuite vient un deuxième événement 'usermessage' qui contient une chaine JSON avec  entre autre un message intégré dans l'un des champs du tableau associatif  "text" : "Hi everyone."

event: userconnect
data: {"username": "bobby", "time": "02:33:48"}

data: Here's a system message of some kind that will get used
data: to accomplish some task.

event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}

Compatibilité des navigateurs

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
EventSource support 9 6.0 (6.0) Pas de support 11 5
Feature Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
EventSource support Pas de support ? ? 11.1 4

 

Références

WHATWG community

https://html.spec.whatwg.org/multipage/comms.html#server-sent-events

Eric Bidelman on HTML5 ROCKS blog

http://www.html5rocks.com/en/tutorials/eventsource/basics/?redirect_from_locale=fr

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : duchesne.andre, ygarbage, QuentinChx, mikadev
 Dernière mise à jour par : duchesne.andre,