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)
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
This reverts commit [1], which was included in `engine.io@5.1.0` and
`socket.io@4.1.0`.
The WebSocket connection was closed before all packets were written
out, so for example when calling `socket.disconnect(true)` on the
Socket.IO server (which disconnect from all namespaces and close the
connection), the client would receive only the first disconnect packet
and kept trying to reconnect to the other namespaces.
The only difference with the previous implementation (pre 5.1.0) is
that the "drain" event gets only called once at the end, and not after
each packet.
[1]: ad5306aeae
Related: https://github.com/socketio/engine.io/issues/648
The package does not have a default export, so importing it from a
project using ES modules would break in some cases.
> Cannot destructure property 'Server' of '_engineIo.default'
Related: https://github.com/socketio/engine.io/issues/657
This major bump creates a lot of noise, but it is necessary for
prettier to be able to parse new syntax such as:
- typed imports: `import { type xxx } from ...`
- private attributes: `class A { #b; #c() {} }`
The "addTrailingSlash" option allows to control whether a trailing
slash is added to the path of the HTTP requests:
- true (default): "/engine.io/"
- false: "/engine.io"
Related: 21a6e1219a
Signed-off-by: iifawzi <iifawzie@gmail.com>
This optimization is only applied if:
- the permessage-deflate extension is disabled (which is the default)
- the "ws" package is used (which is the default)
In that case, the WebSocket frame will only be computed once, when
broadcasting to multiple clients.
Related: 5f7b47d40f
A query parameter with a "@" character could be incorrectly parsed.
Example: "/foo?bar=@example.com" => host: example.com
The parse() method is also used in the `socket.io-client` package, to
extract the namespace and the query parameters.
Notes:
- this bug does not seem exploitable, as an attacker would need to
inject the query parameter in the code executed by the client.
- we might use the URL object in the next major version, but that
means dropping support for some platforms such as IE
Reference: https://caniuse.com/url
Thanks to Li Jiantao of STAR Labs (@starlabs_sg) for the responsible
disclosure.
The named import is not supported in some cases:
> node_modules/socket.io-adapter/dist/index.js:170
> packetOpts.wsPreEncodedFrame = ws_1.WebSocket.Sender.frame(data, {
> ^
>
> TypeError: Cannot read properties of undefined (reading 'Sender')
> at RedisAdapter._encode (/.../node_modules/socket.io-adapter/dist/index.js:170:59)
> at RedisAdapter.broadcast (/.../node_modules/socket.io-adapter/dist/index.js:117:37)
Related: https://github.com/socketio/socket.io-redis-adapter/issues/478
Note:
- only packets without binary attachments are affected
- the permessage-deflate extension must be disabled (which is the default)
Previous attempt:
- wsPreEncoded option: 5579d40c24
- fix for binary packets: a33e42bb7b
- revert: 88eee5948a