This is a follow-up commit of [1]. Without it, adapter.persistSession()
would be called even if the connection state recovery feature was
disabled.
[1]: 54d5ee05a6
This reverts commit 4e64123862.
Using the id of the socket is not possible, since it is lost upon
reconnection (unless connection recovery is successful), so we revert
the previous change.
This commit implements middlewares at the Engine.IO level, because
Socket.IO middlewares are meant for namespace authorization and are not
executed during a classic HTTP request/response cycle.
A workaround was possible by using the allowRequest option and the
"headers" event, but this feels way cleaner and works with upgrade
requests too.
Syntax:
```js
engine.use((req, res, next) => {
// do something
next();
});
// with express-session
import session from "express-session";
engine.use(session({
secret: "keyboard cat",
resave: false,
saveUninitialized: true,
cookie: { secure: true }
});
// with helmet
import helmet from "helmet";
engine.use(helmet());
```
Related:
- https://github.com/socketio/engine.io/issues/668
- https://github.com/socketio/engine.io/issues/651
- https://github.com/socketio/socket.io/issues/4609
- https://github.com/socketio/socket.io/issues/3933
- a lot of other issues asking for compatibility with express-session
For some reason, "import { type A }" does not work in some cases:
> node_modules/engine.io-client/build/esm/socket.d.ts:2:15 - error TS1005: ',' expected.
> 2 import { type Packet, type BinaryType, RawData } from "engine.io-parser";
References: https://www.typescriptlang.org/docs/handbook/modules.html#importing-types
Syntax:
```js
const socket = io({
retries: 3,
ackTimeout: 10000
});
// "my-event" will be sent up to 4 times (1 + 3), until the server sends an acknowledgement
socket.emit("my-event", (err) => {});
```
Notes:
- the order of the packets is guaranteed, as we send packets one by one
- the same packet id is reused for consecutive retries, in order to
allow deduplication on the server side
This commit adds some syntactic sugar around acknowledgements:
```js
// without timeout
const response = await socket.emitWithAck("hello", "world");
// with a specific timeout
try {
const response = await socket.timeout(1000).emitWithAck("hello", "world");
} catch (err) {
// the server did not acknowledge the event in the given delay
}
```
Note: enviroments that do not support Promises ([1]) will need to add a
polyfill in order to use this feature
See also: 184f3cf7af
[1]: https://caniuse.com/promises
Connection state recovery allows a client to reconnect after a
temporary disconnection and restore its state:
- id
- rooms
- data
- missed packets
See also: 54d5ee05a6
The RemoteSocket interface, which is returned when the client is
connected on another Socket.IO server of the cluster, was lacking the
`timeout()` method.
Syntax:
```js
const sockets = await io.fetchSockets();
for (const socket of sockets) {
if (someCondition) {
socket.timeout(1000).emit("some-event", (err) => {
if (err) {
// the client did not acknowledge the event in the given delay
}
});
}
}
```
Related: https://github.com/socketio/socket.io/issues/4595
This commit adds some syntactic sugar around acknowledgements:
- `emitWithAck()`
```js
try {
const responses = await io.timeout(1000).emitWithAck("some-event");
console.log(responses); // one response per client
} catch (e) {
// some clients did not acknowledge the event in the given delay
}
io.on("connection", async (socket) => {
// without timeout
const response = await socket.emitWithAck("hello", "world");
// with a specific timeout
try {
const response = await socket.timeout(1000).emitWithAck("hello", "world");
} catch (err) {
// the client did not acknowledge the event in the given delay
}
});
```
- `serverSideEmitWithAck()`
```js
try {
const responses = await io.timeout(1000).serverSideEmitWithAck("some-event");
console.log(responses); // one response per server (except itself)
} catch (e) {
// some servers did not acknowledge the event in the given delay
}
```
Related:
- https://github.com/socketio/socket.io/issues/4175
- https://github.com/socketio/socket.io/issues/4577
- https://github.com/socketio/socket.io/issues/4583
This commit adds a new option, "cleanupEmptyChildNamespaces". With this
option enabled (disabled by default), when a socket disconnects from a
dynamic namespace and if there are no other sockets connected to it
then the namespace will be cleaned up and its adapter will be closed.
Note: the namespace can be connected to later (it will be recreated)
Related: https://github.com/socketio/socket.io-redis-adapter/issues/480
If a client was in the process of receiving some binary attachments
when the connection was abruptly closed, then the manager would call
`decoder.destroy()` ([1]) but was then stuck in a "parse error" loop
upon reconnection (since it expected a binary attachment and not a
CONNECT packet).
[1]: a1c528b089/lib/manager.ts (L520)