Server-sent events
La communication entre un navigateur web (le client) et un site web (le serveur) se fait essentiellement dans un seul sens : du client vers le serveur, c'est-à-dire que c'est le navigateur web qui demande au serveur la ressource (page web, image, vidéo, etc.) à afficher. Mais il est parfois utile d'avoir l'inverse, que ce soit le serveur qui transmette de « son propre chef » une ressource au client. C'est à cela que servent les événements serveurs.
Présentation
Les événements serveur sont à utiliser quand on a besoin de ce canal de communication serveur ⇨ client et que la communication inverse, client ⇨ serveur est gérée classiquement par des requêtes http avec fetch
ou XmlHttpRequest
. Pour une communication bi-directionnelle, on préférera les WebSocket (qui nécessite cependant une autre connexion TCP et un protocole dédié). Une nouvelle spec, WebTransport, apportera aussi bientôt de nouvelles possibilités.
Utilisation dans le navigateur web
// Création d'une source d'événement pointant vers '/events'
const eventSource = new EventSource('/events');
// Écoute des messages provenant de cette source
eventSource.addEventListener('message', e => {
// S'arrurer que le message vient du même domaine
if (e.origin === globalThis.location.origin) {
const data = e.data;
// => do something with data
}
});
Utilisation avec node.js
Une implémentation simple en node.js, sans avoir besoin de librairie tiers. D'abord création d'un serveur avec un RequestListener
:
import http from 'node:http';
const server = http.createServer(requestListener);
server.listen(8000);
L'écouteur de requête doit gérer les requêtes pointant vers l'endpoint définit (/events
) avec l'en-tête accept
ayant pour valeur text/event-stream'
:
const requestListener = (req, res) => {
if (req.url === '/events' && req.headers.accept
&& req.headers.accept === 'text/event-stream') {
addSseClient(req.url, req, res);
}
}
La gestion des « clients SSE » se fait à travers un tableau qui stocke les clients
const sseClients = [];
const addSseClient = (req, res) => {
sseClients.push(new SSEClient(req, res, removeSseClient));
};
const removeSseClient = client => {
sseClients = sseClients.filter(c => c.id !== client.id);
};
Le client SSE est un objet chargé de définir les en-êtes de la réponse http et s'assurer du nettoyage lors de la fermeture de la requête cliente :
class SSEClient {
constructor(req, res, onclose) {
this.id = Date.now();
this.response = res;
this.response.writeHead(200, {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
});
req.on('close', () => {
onclose(this);
this.response = null;
this.id = null;
});
}
}
Plus tard, sur une action quelconque, s'il y a besoin d'envoyer un événement au client, il suffit d'appeler une méthode pushEvent
par exemple :
const pushEvent = data => {
const message = `data: ${JSON.stringify(data)}\n\n`;
sseClients.forEach(client => {
client.response.write(message);
});
};
Ressources et références
- Titre
- Stream Updates with Server-Sent Events
- Auteurs
- Eric Bidelman
- Éditeur
- web.dev
- Date
- Titre
- Server-Sent Events- the alternative to WebSockets you should be using
- Auteurs
- @germanodev
- Éditeur
- germano.dev
- Date
- Titre
- Server-Sent Events, WebSockets, and HTTP
- Auteurs
- Mark Nottingham
- Éditeur
- www.mnot.net
- Date