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.
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
Connection state recovery allows a client to reconnect after a
temporary disconnection and restore its state:
- id
- rooms
- data
- missed packets
Usage:
```js
import { Server } from "socket.io";
const io = new Server({
connectionStateRecovery: {
// default values
maxDisconnectionDuration: 2 * 60 * 1000,
skipMiddlewares: true,
},
});
io.on("connection", (socket) => {
console.log(socket.recovered); // whether the state was recovered or not
});
```
Here's how it works:
- the server sends a session ID during the handshake (which is
different from the current `id` attribute, which is public and can be
freely shared)
- the server also includes an offset in each packet (added at the end
of the data array, for backward compatibility)
- upon temporary disconnection, the server stores the client state for
a given delay (implemented at the adapter level)
- upon reconnection, the client sends both the session ID and the last
offset it has processed, and the server tries to restore the state
A few notes:
- the base adapter exposes two additional methods, persistSession() and
restoreSession(), that must be implemented by the other adapters in
order to allow the feature to work within a cluster
See: f5294126a8
- acknowledgements are not affected, because it won't work if the
client reconnects on another server (as the ack id is local)
- any disconnection that lasts longer than the
`maxDisconnectionDuration` value will result in a new session, so users
will still need to care for the state reconciliation between the server
and the client
Related: https://github.com/socketio/socket.io/discussions/4510
Note:
- only packets without binary attachments are affected
- the permessage-deflate extension must be disabled (which is the default)
Related:
- 5f7b47d40f
- 5e34722b0b
This adds typings for the socket.io engine field, which offers better
IntelliSense when retrieving the server, as well as more confidence on
the developer-side of what types of fields are entering the server.
Related: https://github.com/socketio/socket.io/issues/4590
Note: some disconnection reasons could be merged in the next major
release, i.e. the Deno impl does not have "forced server close" and
"server shutting down"
Related: https://github.com/socketio/socket.io/issues/4387
This should fix a rare case where the Engine.IO connection was upgraded
to WebSocket while the Socket.IO socket was disconnected, which would
result in the following exception:
> TypeError: Cannot read properties of undefined (reading 'forEach')
> at subscribe (/node_modules/socket.io/dist/uws.js:87:11)
> at Socket.<anonymous> (/node_modules/socket.io/dist/uws.js:28:17)
> at Socket.emit (node:events:402:35)
> at WebSocket.onPacket (/node_modules/engine.io/build/socket.js:214:22)
> at WebSocket.emit (node:events:390:28)
> at WebSocket.onPacket (/node_modules/engine.io/build/transport.js:92:14)
> at WebSocket.onData (/node_modules/engine.io/build/transport.js:101:14)
> at message (/node_modules/engine.io/build/userver.js:56:30)
Related: https://github.com/socketio/socket.io/issues/4443