Saltar a contenido

Server-Sent Events spec

Se trata de una comunicación uni-direccional, es ideal para escuchar eventos y actualizaciones de servidor pero a los cuales vamos a reaccionar de manera autónoma, es decir no vamos a ejecutar cambios sobre el servidor.

Análogo seria un televisor, estamos viendo la información que nos da y en función de ella reaccionamos haciendo algo en nuestro entorno, es decir no necesitamos hablar con el televisor, él nos informa y eso ya nos vale para que nosotros reaccionemos de manera autónoma.

Un ejemplo claro es una web que escucha cambios de stock. Llegan eventos de cambios de stock, precio etc… y la aplicación muestra alertas o lo que tenga que mostrar, pero no necesita comunicación directa con ese recurso del servidor (el servidor que le esta dando la información).

Características

  • Su uso es ideal sobre el protocolo HTTP(2).

  • El API Nativo de Server-Sent Events en navegadores soporta el envío de cabeceras y por lo tanto protocolos de seguridad OAuth.

  • Para la implementación de TLS no es necesario hacer nada.

  • El API Nativo de Sever-Sent Events contempla de manera natural la reconexión y demás gestiones comunes de comunicación HTTP.

  • La especificación de Server Side Events cae bajo el paraguas OpenAPI https://swagger.io/specification

  • Navegadores más antiguos de 2011 pueden soportar fácilmente la integración con SSE.

  • No se permite el envío de datos binarios, solo texto UTF-8.

WebSockets spec

Se trata de una comunicación bidireccional, tanto nos cae información del servidor, como el servidor espera hacer algo que le digamos en ese mismo recurso.

Características:

  • El API Nativo de WebSockets en navegadores no permite el intercambio de cabeceras, por lo que el uso de OAuth no esta soportado de manera “estándar”.

  • Solo esta soportada la autenticación básica en url:

var ws = new WebSocket("ws://username:password@example.com");

(Ref: https://stackoverflow.com/questions/4361173/http-headers-in-websockets-client-api) o implementar un mecanismo de ticket que si use OAuth para este fin (https://devcenter.heroku.com/articles/websocket-security).
  • El protocolo usado es sobre TCP, pero no es HTTP. (Hay que revisar la compatibilidad de tu sidecar (envoy)si es capaz de soportarlo)

  • La implementación de protocolo TLS sobre websockets (wss:) necesita ser revisado.

  • El API Nativo de Websockets no contempla la reconexión automática y es necesario hacer esta programación manual por el cliente.

  • Es capaz de enviar y recibir datos binarios.

  • Navegadores anteriores a 2011 no soportan websockets

  • La especificación de Protocolo Websocket cae sobre el paraguas asyncapi https://www.asyncapi.com/blog/websocket-part3

Cuando usar Websockets o Server-Sent Events

Cuando recibimos eventos ya sea por websockets o sse debemos pensar en cómo vamos a reaccionar respecto a ellos. Al hablar de reaccionar respecto a un evento debemos distinguir bien el ámbito de la reacción.

Imaginemos un api que devuelve stock de frutas.

curl https://{mitienda}/frutas/sandias/events

A nivel semantico de API:

En modo Server-Sent Events, hemos abierto una conexión solo de escucha hacia el recurso http://mitienda.com:8080/frutas/sandias/events

En modo WebSockets hemos abierto una conexión bidireccional a /frutas/sandias/events. Es decir que podríamos enviar información a /frutas/sandias/events , que semánticamente seria enviar eventos a ese “canal” de eventos de sandias.

Caso de uso Server Send Events:

Sigamos imaginando, que queremos implementar una regla por la cual si el stock de sandias es menor que 10, queremos hacer una petición al API para crear mas sandias (ya que se están acabando).

En una filosofía API Rest, nuestro cliente que ha escuchado el evento hará una llamada de este tipo para crear mas sandias.

curl POST https://{mitienda}/frutas/sandias  
{"cantidad": 50}

Si nos fijamos estamos invocando a otro recurso (ámbito) a /frutas/sandias, que es el indicado para crear sandias, por lo que no necesitábamos una comunicación bi-direccional con el recurso frutas/sandias/events, y en este caso el uso de Server-Sent Events es el mas adecuado ya que Websockets aquí esta desaprovechado y desaconsejado. Ademas seguimos usando la misma especificación de OpenAPI que tuviera nuestro APIRest.

Caso de uso Websockets:

Por el contrario, como hemos dicho anteriormente, el uso de Websockets tendría sentido en un ámbito en el que el recurso al que estamos conectados fuera con el que tuviéramos que comunicarnos directamente. El ejemplo mas manido, pero mas representativo es el de un chat.

GET /chats/chat23

En este caso, nos subscribimos a los eventos que se produzcan en el chat23, pero si queremos reaccionar y enviar información, queremos que sea al chat23 y podríamos hacerlo directamente, porque es ademas su ambito propio de conversacion.

AsyncAPI y OpenAPI

Si optáramos por websockets deberíamos tener una especificación AsyncAPI que indique qué tipo de acciones y payloads gobiernan esa comunicación.

Si nuestro servidor ya genera un documento OPEN API para otros endpoints , deberíamos considerar separar estos endpoints de comunicación asíncrona a otro servicio (a día de hoy creo que envoy no soporta 2 especificaciones a la vez, de ahi que comente lo de un segundo servicio de naturaleza asíncrona).

Conclusiones:

Como hemos visto WebSocket puede cubrir también el caso de uso de Server-Sent Events, pero desde el punto de vista de API mientras que en Server-Sent Events mantiene sus recursos REST (swagger) intactos para cualquier tipo de cliente que soporte el protocolo HTTP, vemos que para websockets tendríamos que crear una especificación especial para comunicación asíncrona, también obligaríamos a que nuestros clientes implementaran el protocolo websocket (WS), haciendo más tedioso el mantenimiento y la universalidad de nuestro API.

Por lo que a menos que nuestro caso de uso sea especifico bidireccional por el mismo ámbito y canal (WebSockets) se aconseja el uso de Server-Sent Events.

Codigo fuente y Demo