857 Commits

Author SHA1 Message Date
Damien Arrachequesne
63f181cc12 feat: serve client bundles with CORS headers
The version of the `cors` package matches the one used by `engine.io`.

Related: https://github.com/socketio/socket.io/issues/3552
2023-06-20 14:32:59 +02:00
SyedTayyabUlMazhar
e5c62cad60 fix: remove the Partial modifier from the socket.data type (#4740)
Wrapping SocketData with Partial causes issues when reading data even
if you've made sure to pass all values. If someone want to make their
type Partial or make only a few properties optional, they can do so in
their own type instead.

Related: https://github.com/socketio/socket.io/issues/4537
2023-06-20 07:49:02 +02:00
Damien Arrachequesne
15af22fc22 refactor: add a noop handler for the error event
We should reduce the scope of the "event" error in the next major
version, as it is overloaded today:

- it can be sent by the client (`socket.emit("error")`, which is a perfectly valid event name)
- it can be emitted when the connection encounters an error (an invalid packet for example)
- it can be emitted when a packet is rejected in a middleware (`socket.use()`)

Related: https://github.com/socketio/socket.io/issues/2047
2023-05-24 10:47:52 +02:00
Damien Arrachequesne
0d0a7a22b5 fix: properly handle manually created dynamic namespaces
Namespaces that match the regex of a parent namespace will now be added
as a child of this namespace:

```js
const parentNamespace = io.of(/^\/dynamic-\d+$/);
const childNamespace = io.of("/dynamic-101");
```

Related:

- https://github.com/socketio/socket.io/issues/4615
- https://github.com/socketio/socket.io/issues/4164
- https://github.com/socketio/socket.io/issues/4015
- https://github.com/socketio/socket.io/issues/3960
2023-02-20 01:19:01 +01:00
Damien Arrachequesne
2a8565fd1e refactor: catch errors when trying to restore the connection state 2023-02-20 01:18:08 +01:00
Igor Lins e Silva
d0b22c6302 fix(types): fix nodenext module resolution compatibility (#4625)
The import added in [1] was invalid, because it used an non-exported
class.

Related: https://github.com/socketio/socket.io/issues/4621

[1]: d4a9b2cdcb
2023-02-20 01:15:35 +01:00
Damien Arrachequesne
d8143cc067 refactor: do not persist session if connection state recovery if disabled
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
2023-02-06 18:03:34 +01:00
Damien Arrachequesne
3734b74b45 revert: feat: expose current offset to allow deduplication
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.
2023-02-06 17:36:37 +01:00
Edouard Benauw
8aa94991ce feat: add description to the disconnecting and disconnect events (#4622)
See also: b862924b7f
2023-02-04 09:03:01 +01:00
Damien Arrachequesne
4e64123862 feat: expose current offset to allow deduplication
Related: 655dce9755
2023-02-04 08:56:55 +01:00
Damien Arrachequesne
115a9819fd refactor: do not include the pid by default
So that the client knows whether the connection state recovery feature
is enabled.

See also: 54d5ee05a6
2023-01-25 09:39:22 +01:00
Waldemar Schlegel
0c0eb00163 fix: add timeout method to remote socket (#4558)
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
2023-01-24 09:24:19 +01:00
Damien Arrachequesne
f8640d9451 refactor: export DisconnectReason type
Related: https://github.com/socketio/socket.io/issues/4556
2023-01-23 09:27:32 +01:00
Damien Arrachequesne
93d446a545 refactor: add charset when serving the bundle files
Reference: https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Type

Related: https://github.com/socketio/socket.io/discussions/4589
2023-01-23 09:27:06 +01:00
Damien Arrachequesne
184f3cf7af feat: add promise-based acknowledgements
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
2023-01-23 09:06:25 +01:00
Steve Baum
5d9220b69a feat: add the ability to clean up empty child namespaces (#4602)
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
2023-01-23 07:56:14 +01:00
Damien Arrachequesne
f3ada7d8cc fix(typings): properly type emits with timeout
When emitting with a timeout (added in version 4.4.0), the "err"
argument was not properly typed and would require to split the client
and server typings. It will now be automatically inferred as an Error
object.

Workaround for previous versions:

```ts
type WithTimeoutAck<isEmitter extends boolean, args extends any[]> = isEmitter extends true ? [Error, ...args] : args;

interface ClientToServerEvents<isEmitter extends boolean = false> {
    withAck: (data: { argName: boolean }, callback: (...args: WithTimeoutAck<isEmitter, [string]>) => void) => void;
}

interface ServerToClientEvents<isEmitter extends boolean = false> {

}

const io = new Server<ClientToServerEvents, ServerToClientEvents<true>>(3000);

io.on("connection", (socket) => {
    socket.on("withAck", (val, cb) => {
        cb("123");
    });
});

const socket: Socket<ServerToClientEvents, ClientToServerEvents<true>> = ioc("http://localhost:3000");

socket.timeout(100).emit("withAck", { argName: true }, (err, val) => {
  // ...
});
```

Related: https://github.com/socketio/socket.io-client/issues/1555
2023-01-19 11:48:18 +01:00
Damien Arrachequesne
54d5ee05a6 feat: implement connection state recovery
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
2023-01-12 12:21:56 +01:00
Tristan F
d4a9b2cdcb refactor(typings): add types for io.engine (#4591)
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
2023-01-11 10:45:57 +01:00
Damien Arrachequesne
d3d0a2d5be fix(typings): accept an HTTP2 server in the constructor
Related:

- https://github.com/socketio/socket.io/issues/4434
- https://github.com/socketio/socket.io/issues/4494
2022-10-14 11:24:36 +02:00
Damien Arrachequesne
8fae95dd18 docs: add jsdoc for each public method 2022-10-14 10:30:08 +02:00
Damien Arrachequesne
e6f6b906db docs: add deprecation notice for the allSockets() method 2022-10-13 15:02:23 +02:00
Damien Arrachequesne
e357daf585 fix(typings): apply types to "io.timeout(...).emit()" calls
Typed events were not applied when calling "io.timeout(...).emit()".

Related: https://github.com/socketio/socket.io-client/issues/1555#issuecomment-1277289733

Reference: https://socket.io/docs/v4/typescript/
2022-10-13 14:50:09 +02:00
Damien Arrachequesne
10fa4a2690 refactor: add list of possible disconnection reasons
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
2022-09-13 08:25:13 +02:00
Damien Arrachequesne
ba497ee3eb fix(uws): prevent the server from crashing after upgrade
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
2022-09-02 23:42:26 +01:00
Daniel Rosenwasser
134226e96c refactor: add missing constraints (#4431)
See also: https://github.com/microsoft/TypeScript/issues/49489
2022-09-01 21:13:55 +01:00
Damien Arrachequesne
9890b036cf chore: bump dependencies
Production:

- socket.io-parser: ~4.0.4 => ~4.2.0

Development:

- superagent: ^6.1.0 => ^8.0.0
- tsd: ^0.17.0 => ^0.21.0

Related: https://github.com/socketio/socket.io/issues/3709
2022-06-27 09:16:08 +02:00
Damien Arrachequesne
18f3fdab12 fix: prevent the socket from joining a room after disconnection
Calling `socket.join()` after disconnection would lead to a memory
leak, because the room was never removed from the memory:

```js
io.on("connection", (socket) => {
  socket.disconnect();
  socket.join("room1"); // leak
});
```

Related:

- https://github.com/socketio/socket.io/issues/4067
- https://github.com/socketio/socket.io/issues/4380
2022-05-25 23:18:42 +02:00
Damien Arrachequesne
30430f0985 fix: forward the local flag to the adapter when using fetchSockets()
Related:

- https://github.com/socketio/socket.io/issues/4359
- https://github.com/socketio/socket.io-redis-adapter/issues/454
2022-05-03 15:41:24 +02:00
h110m
9b43c9167c fix(typings): add HTTPS server to accepted types (#4351) 2022-05-03 08:15:22 +02:00
Damien Arrachequesne
0b35dc77c0 refactor: make the protocol implementation stricter
This commit handles several edge cases that were silently ignored
before:

- receiving several CONNECT packets during a session
- receiving any packet without CONNECT packet first
2022-03-31 12:24:31 +02:00
Damien Arrachequesne
531104d332 feat: add support for catch-all listeners for outgoing packets
This is similar to `onAny()`, but for outgoing packets.

Syntax:

```js
socket.onAnyOutgoing((event, ...args) => {
  console.log(event);
});
```
2022-03-31 10:35:09 +02:00
Damien Arrachequesne
8b204570a9 feat: broadcast and expect multiple acks
Syntax:

```js
io.timeout(1000).emit("some-event", (err, responses) => {
  // ...
});
```

The adapter exposes two additional methods:

- `broadcastWithAck(packets, opts, clientCountCallback, ack)`

Similar to `broadcast(packets, opts)`, but:

* `clientCountCallback()` is called with the number of clients that
  received the packet (can be called several times in a cluster)
* `ack()` is called for each client response

- `serverCount()`

It returns the number of Socket.IO servers in the cluster (1 for the
in-memory adapter).

Those two methods will be implemented in the other adapters (Redis,
Postgres, MongoDB, ...).

Related:

- https://github.com/socketio/socket.io/issues/1811
- https://github.com/socketio/socket.io/issues/4163
- https://github.com/socketio/socket.io-redis-adapter/issues/445
2022-03-31 07:49:09 +02:00
Chris Swithinbank
02c87a8561 fix(typings): ensure compatibility with TypeScript 3.x (#4259)
Labeled tuple elements were added in TypeScript 4.0.

Reference: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#labeled-tuple-elements

Related: 44e20ba5bf
2022-01-25 01:25:05 +01:00
Orkhan Alikhanov
770ee5949f fix(types): make RemoteSocket.data type safe (#4234)
Related:

- https://github.com/socketio/socket.io/issues/4229
- fe8730ca0f
2022-01-06 07:14:55 +01:00
Damien Arrachequesne
3bf5d92735 refactor: add note about fetchSockets() for parent namespaces
Related: https://github.com/socketio/socket.io/issues/4235
2022-01-05 08:50:40 +01:00
Shayan Yousefi
fc82e44f73 refactor(typings): export Event type (#4215)
So that it can be used by the end users:

```ts
const myMiddleware = ([eventName, ...args]: Event, next: (err?: Error) => void) => {
  console.log(eventName); // inferred as string
  next();
}

io.on("connection", (socket) => {
  socket.use(myMiddleware);
});
```
2022-01-05 08:08:18 +01:00
Orkhan Alikhanov
f2b8de7191 fix(typings): pass SocketData type to custom namespaces (#4233)
The `SocketData` type was only available on the main namespace.

Related: https://github.com/socketio/socket.io/issues/4229
See also: fe8730ca0f
2022-01-04 09:09:42 +01:00
Damien Arrachequesne
b839a3b400 fix: prevent double ack when emitting with a timeout
The ack was not properly removed upon timeout, and could be called
twice.

Related: f0ed42f18c
2021-11-18 14:03:07 +01:00
Damien Arrachequesne
f0ed42f18c feat: add timeout feature
Usage:

```js
socket.timeout(5000).emit("my-event", (err) => {
  if (err) {
    // the client did not acknowledge the event in the given delay
  }
});
```
2021-11-16 20:07:53 +01:00
Damien Arrachequesne
2da82103d2 test: add test for volatile packet with binary
See also: 88eee5948a
2021-11-16 15:57:32 +01:00
Damien Arrachequesne
02b0f73e2c fix: only set 'connected' to true after middleware execution
The Socket instance is only considered connected when the "connection"
event is emitted, and not during the middleware(s) execution.

```js
io.use((socket, next) => {
  console.log(socket.connected); // prints "false"
  next();
});

io.on("connection", (socket) => {
  console.log(socket.connected); // prints "true"
});
```

Related: https://github.com/socketio/socket.io/issues/4129
2021-11-12 07:31:52 +01:00
Damien Arrachequesne
c0d8c5ab23 feat: add an implementation based on uWebSockets.js
Usage:

```js
const { App } = require("uWebSockets.js");
const { Server } = require("socket.io");

const app = new App();
const server = new Server();

server.attachApp(app);

app.listen(3000);
```

The Adapter prototype is updated so we can benefit from the publish
functionality of uWebSockets.js, so this will apply to all adapters
extending the default adapter.

Reference: https://github.com/uNetworking/uWebSockets.js

Related:

- https://github.com/socketio/socket.io/issues/3601
- https://github.com/socketio/engine.io/issues/578
2021-11-12 07:01:55 +01:00
Nikita Kolmogorov
fe8730ca0f feat: add type information to socket.data (#4159)
Usage:

```js
interface SocketData {
  name: string;
  age: number;
}

const io = new Server<ClientToServerEvents, ServerToClientEvents, InterServerEvents, SocketData>();

io.on("connection", (socket) => {
  socket.data.name = "john";
  socket.data.age = 42;
});
```
2021-11-08 15:21:48 +01:00
Sebastiaan Marynissen
9d86397243 fix: fix race condition in dynamic namespaces (#4137)
Using an async operation with `io.use()` could lead to the creation of
several instances of a same namespace, each of them overriding the
previous one.

Example:

```js
io.use(async (nsp, auth, next) => {
  await anOperationThatTakesSomeTime();
  next();
});
```

Related: https://github.com/socketio/socket.io/pull/4136
2021-10-24 07:46:29 +02:00
Naseem
44e20ba5bf refactor: add event type for use() (#4138) 2021-10-24 07:19:43 +02:00
Josh Field
0ef2a4d02c fix: fix server attachment (#4127)
The check excluded an HTTPS server from being properly attached.

Related: https://github.com/socketio/socket.io/issues/4124
2021-10-16 23:58:55 +02:00
Damien Arrachequesne
60edecb3bd feat: serve ESM bundle
Related:

- 0661564dc2
- https://github.com/socketio/socket.io-client/issues/1198
2021-10-13 18:17:12 +02:00
Damien Arrachequesne
eb5fdbd03e chore: bump engine.io to version 6.0.0
Release notes: https://github.com/socketio/engine.io/releases/tag/6.0.0
Diff: https://github.com/socketio/engine.io/compare/5.2.0...6.0.0
2021-10-12 00:05:10 +02:00
douira
033c5d399a fix(typings): add name field to cookie option (#4099)
Reference: 18a6eb89fb/lib/server.js (L355)
2021-09-20 09:13:38 +02:00