Streaming

Discussing the choice of a push technology: SSE vs WebSockets

As we presented our new product streamdata.io at DevoxxFr, we were often asked why we choose SSE vs Websockets (SSE stands for Server-Sent Event) as our Push protocol. This post may help you understand our choice and test what best suits your needs. We’ll start first with a short description of the two protocols.

SSE: Server-Sent Event

HTTP based API dedicated to Push.
Implemented in recent browsers (Firefox, Chrome, Safari & Opera).
Allows the server to send data to a client (one-way communication) and event source: 

WebSockets

TCP based protocol providing full duplex communication link between client and server. Discover more information on Websockets.

1 – The problematic

At the beginning of the development of the Proxy, we had used SSE to communicate with the client for several reasons:
– At first glance more suitable because once the connection is established, the client need not send data to the server. The bi-directional link is not useful in our case.
– Easier to implement because, unlike the WebSockets, SSE does not need to define a message exchange protocol (onOpen, onMessage…)
– SSE is based on HTTP, it is more compliant with various elements of existing IT infrastructure (load balancers, firewalls…)

However, during the development, we encountered several problems related to the use of SSE:
– Unable to add specific headers with Javascript SSE libraries
– Unable to detect the disconnection of a client before trying to send data

Headers issue

In order to route the requests to the Information System, the proxy must be able to forward the headers of the requests (port number, OAuth authentication…). In addition, to secure the transactions going through the proxy, we will set up an authentication mechanism based on security tokens that will also be routed to the Proxy.

Yet, the current implementations of EventSource object in JavaScript (base object for SSE communication) do not allow to override the headers. This problem does not exist with native SSE libraries (Java or ObjectiveC).

Disconnection detection

When a client is connected to the server via SSE, the server is not notified if the client disconnects. Disconnection will be detected by the server only when trying to send data to the client and getting an error report mentioning that the connection was lost.

This is a problem in our case because the server performs regular polling to the information system, and we want to stop this polling at the earliest to avoid inducing unnecessary load on the IS when no client is connected. Sometimes, if the data source is not very dynamic, it may take several pollings before the data is changed so that the server tries to send a diff to client.

This is problematic, so we considered different solutions that will be presented next.

2 – Considered options

SSE with Polyfill

When using SSE in Javascript, we depend on browser implementation. At the time of writing this blog post, SSE is supported by Firefox, Chrome, Safari, and Opera, but not Internet Explorer (see here. A solution to override this implementation is to write a Polyfill. 

Today, there are several open-source Polyfills that provide some fallback mechanisms to support SSE over all browsers, even those that don’t provide a native implementation. One of the most widely used among those Polyfills redefines the SSE implementation even for browsers that support it natively and replace it with long polling.

We do not want such an implementation because with a source whose data changes very frequently, long polling is less efficient than the standard polling strategy.

If we go for this solution, we will, therefore, write our own Polyfill that uses the native implementation when it is available, and fallback mechanisms when it is not.

Pros

– Rather simple to implement. We can rely on existing Polyfill to implement the logic that suits us.
– As we offer a Javascript SDK facilitating the use of the proxy in client applications, it is easy to include the Polyfill in the SDK.

Cons

– A Polyfill can be hard to maintain with all browsers’ versions support.

SSE with query parameters

When browsing the web, we noticed that many Javascript developers were facing the same issue as us regarding headers and SSE, and most of them go for passing information into query parameters.

Pros

– Very simple to implement

Cons

– Adding query parameters will dramatically increase the size of URLs. But URLs cannot be infinite. There is no universal maximum size, but 2048 characters is a reasonable limit.
– Today we consider that this limit is not blocking. However, it will be noted in our documentation as a known limitation.
– To avoid some code writing to users to concatenate all parameters in the URL, we will provide a minimalist Javascript SDK that will hide this complexity.

SSE with OPTIONS request

To avoid passing parameters to each request, one option would be that the client sends an OPTIONS request when first opening the connection, containing all the settings for the subsequent requests. The proxy then would record these parameters and automatically add all requests from during the session.

Pros

– Very simple to implement.
– Cancels the constraint on URLs length as parameters are passed only once.

Cons

– This introduces state-full sessions server-side, which can cause problems when we will need to replicate the Proxy installation on many servers to guarantee continuity of service and ensure scalability.

Add a WebSocket proxy between streamdata.io Proxy and the client

The last option we considered was adding a WebSocket proxy in front of streamdata.io Proxy. The client communicates with the proxy in WebSocket. Then the WebSocket proxy communicates with streamadata.io proxy with the SSE. The WebSocket proxy can add headers as it uses the Java SSE library.

Pros

– Once implemented, this proxy will be very simple to maintain.
– WebSocket are more widely supported by web browsers than SSE.
– The fallback mechanisms for browsers that do not support WebSockets are very easy to set up with SockJS.
– The WebSocket proxy can detect client disconnections.

Cons

– Complicated to implement.
– Induces considerable complexity at the IT infrastructure level.
– Requires defining a protocol for WebSocket communication between the client and the WebSocket proxy.
– WebSocket connection does not allow to set up a sticky URL load balancing strategy between client and WebSocket proxy.
– WebSockets are not always supported by the firewall, proxy or load balancers. This will thus induce some constraint on the IT infrastructure.

How to address the disconnection detection issue?

To address this issue, we can set up a heartbeat mechanism sent by the server at regular intervals. With SSE, if you send a blank character, it does not induce network overload or additional processing at the client level and will detect disconnection without waiting for data to be sent.

3 – Adopted solution

First, we have implemented the solution using query parameters. The limitations of this solution are non-blocking for our beta version. It will simply be explained clearly in the documentation.

To simplify the use of the proxy for developers, a minimalist Javascript SDK is provided to mask the complexity of passing information as query parameters.

As we are providing a Javascript SDK, we could easily integrate a Polyfill providing fallback mechanisms for (and only for) browsers that do not support SSE.

The disconnection detection issue is addressed with the heartbeat option.

Are you ready to navigate the new streaming API landscape?