We are happy to announce the release of Engine.IO v4, the low-level engine that powers Socket.IO.

Major breaking changes

Heartbeat mechanism reversal

We have received a lot of reports from users that experience random disconnects due to ping timeout, even though their Internet connection is up and the remote server is reachable. It should be noted that in that case the client reconnects right away, but still it was an annoying issue.

After analysis, it seems to be caused by delayed timers on the client-side. Those timers are used in the ping-pong mechanism which helps to ensure the connection between the server and the client is still healthy. A delay on the client-side meant the client sent the ping packet too late, and the server considered that the connection was closed.

That’s why the ping packets will now be sent by the server, and the client will respond with a pong packet.

Packet encoding

We have also changed the way the data is encoded. It previously relied on the string length in Javascript, which meant it was harder to implement clients in other programming languages that do not share the same encoding (more details here).

Technically speaking, it impacts the way an array of packets is encoded when using XHR polling (WebSocket is not affected, as each packet is sent in its own WebSocket frame).

socket.send("€€");
socket.send("й");

// v3 encoding output
"3:4€€2:4й"

// 3 "4€€".length (the JS specific part, 6 bytes)
// : separator
// 4 the "message" packet type
// €€ the content of the first packet
// 2 "4й".length (again, the JS specific part)
// : separator
// 4 the "message" packet type
// й the content of the second packet

// v4 encoding output
"4€€\x1e4й"

// 4 the "message" packet type
// €€ the content of the first packet
// \x1e separator
// 4 the "message" packet type
// й the content of the second packet

You can find more details about the updates to the protocol here.

Other changes

CORS handling

We now use the cors package to handle the CORS configuration. The handlePreflightRequest option was removed:

const { Server } = require('engine.io');

// before
new Server({
handlePreflightRequest: (req, res) => {
res.writeHead(200, {
"Access-Control-Allow-Origin": 'https://example.com',
"Access-Control-Allow-Methods": 'GET',
"Access-Control-Allow-Headers": 'Authorization',
"Access-Control-Allow-Credentials": true
});
res.end();
}
});

// after
new Server({
cors: {
origin: "https://example.com",
methods: ["GET"],
allowedHeaders: ["Authorization"],
credentials: true
}
});

Please see here for all available options.

In Engine.IO v3, the io cookie was sent by default. This cookie can be used to enable sticky-session, which is required when you have several servers (more information here).

However, this cookie is not needed in some cases (i.e. single server deployment, sticky-session based on IP) so it must now be explicitly enabled:

const { Server } = require('engine.io');

// before
new Server({
cookieName: "io",
cookieHttpOnly: false,
cookiePath: "/custom"
});

// after
new Server({
cookie: {
name: "test",
httpOnly: false,
path: "/custom"
}
});

All other options (domain, maxAge, sameSite, …) are now supported. Please see here for the complete list of options.

The default value of maxHttpBufferSize was decreased

This option defines how many bytes or characters a message can be, before closing the connection. It was reduced from 100MB to 1MB.

const { Server } = require('engine.io');

new Server({
maxHttpBufferSize: 1e8 // defaults to 1e6
});

perMessageDeflate is now disabled by default

This option defines whether the WebSocket permessage-deflate extension is enabled or not. This feature, while useful in some cases, adds some extra memory overhead for each WebSocket connection, and could result in huge memory usage in production deployments. It is now disabled by default.

const { Server } = require('engine.io');

new Server({
perMessageDeflate: true // defaults to false
});

Support for Node.js 8 was dropped

We are dropping support for Node.js 8, which is now End-of-Life. It will not be tested in the CI anymore, so please upgrade as soon as possible!

How to upgrade

As detailed in the Major breaking changes section, this release contains several backward incompatible changes, and as such v3 clients will not be able to connect to a v4 server (and vice versa).

In order to upgrade a live production environment, you will need to have both a group of v3 servers and v4 servers in parallel, and route the traffic based on either:

  • the EIO query parameter
  • the path (by using a different path for the v4 servers)
  • or the domain if you use a different domain for the v4 servers

So that it shall be a seamless migration for your end users.

What’s next

This release will be included in Socket.IO v3 in the near future. We will publish a more detailed roadmap in the next blog post.

Besides, I (@darrachequesne) am glad to announce that I will be dedicated full time to the project for the next months. This is possible thanks to our awesome sponsors.

Again, a big shout out to Pia Mancini and all the team behind the Open Collective platform, which helps making Open Source Software sustainable in the long term.

Let’s discuss!

The project is now part of the beta of Github Discussions. Depending on the feedback of the community, it might replace the Slack channel in the future.

If you have any question about the release, let’s discuss about it here.

Stay safe!

Caught a mistake? Edit this page on GitHub