When handling compression at the proxy server level, the client receives a weak ETag.
Weak ETags are prefixed with `W/`, e.g. `W/"2.2.0"`.
Upon cache validation we should take care of these too.
Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
In order to ease the migration to Socket.IO v3, the Socket.IO server
can now communicate with v2 clients.
```js
const io = require("socket.io")({
allowEIO3: true
});
```
This feature is disabled by default.
This functionality was removed in [1] (included in 3.0.0), but
catch-all listeners and socket middleware features are complementary
rather than mutually exclusive.
The only difference with the previous implementation is that passing an
error to the `next` handler will create an error on the server-side,
and not on the client-side anymore.
```js
io.on("connection", (socket) => {
socket.use(([ event, ...args ], next) => {
next(new Error("stop"));
});
socket.on("error", (err) => {
// to restore the previous behavior
socket.emit("error", err);
// or close the connection, depending on your use case
socket.disconnect(true);
});
});
```
This creates additional possibilities about custom error handlers, which
may be implemented in the future.
```js
// user-defined error handler
socket.use((err, [ event ], next) => {
// either handle it
socket.disconnect();
// or forward the error to the default error handler
next(err);
});
// default error handler
socket.use((err, _, next) => {
socket.emit("error", err);
});
```
Related: https://github.com/socketio/socket.io/issues/3678
[1]: 5c73733985
So that the following example:
```js
const io = require('socket.io')({
pingTimeout: 10000
});
io.listen(3000);
```
behaves the same as:
```js
const io = require('socket.io')(3000, {
pingTimeout: 10000
});
```
Before this change, the options in the first example were not forwarded
to the Engine.IO constructor, which is not really intuitive.
The previous syntax (which is still valid):
```js
const io = require('socket.io')();
io.listen(3000, {
pingTimeout: 10000
});
```
The previous signature was not compatible with EventEmitter.emit(). The typescript compilation threw:
```
node_modules/socket.io/dist/namespace.d.ts(89,5): error TS2416: Property 'emit' in type 'Namespace' is not assignable to the same property in base type 'EventEmitter'.
Type '(ev: string, ...args: any[]) => Namespace' is not assignable to type '(event: string | symbol, ...args: any[]) => boolean'.
Type 'Namespace' is not assignable to type 'boolean'.
node_modules/socket.io/dist/socket.d.ts(84,5): error TS2416: Property 'emit' in type 'Socket' is not assignable to the same property in base type 'EventEmitter'.
Type '(ev: string, ...args: any[]) => this' is not assignable to type '(event: string | symbol, ...args: any[]) => boolean'.
Type 'this' is not assignable to type 'boolean'.
Type 'Socket' is not assignable to type 'boolean'.
```
Note: the emit calls cannot be chained anymore:
```js
socket.emit("hello").emit("world"); // will not work anymore
```
This commit restores the ability to send additional data in the
middleware functions, which was removed during the rewrite to
Typescript ([1]).
The only difference with the previous implementation is that the client
will now emit a "connect_error" (previously, "error") event with an
actual Error object, with both the message and an optional "data"
attribute.
```js
// server-side
io.use((socket, next) => {
const err = new Error("not authorized");
err.data = { content: "Please retry later" };
next(err);
});
// client-side
socket.on("connect_error", err => {
console.log(err.message); // not authorized
console.log(err.data.content); // Please retry later
});
```
[1]: a5581a9789
Inspired from EventEmitter2 [1]
```js
io.on("connect", socket => {
socket.onAny((event, ...args) => {});
socket.prependAny((event, ...args) => {});
socket.offAny(); // remove all listeners
socket.offAny(listener);
const listeners = socket.listenersAny();
});
```
Breaking change: the socket.use() method is removed
This method was introduced in [2] for the same feature (having a
catch-all listener), but there were two issues:
- the API is not very user-friendly, since the user has to know the structure of the packet argument
- it uses an ERROR packet, which is reserved for Namespace authentication issues (see [3])
[1]: https://github.com/EventEmitter2/EventEmitter2
[2]: https://github.com/socketio/socket.io/issues/434
[3]: https://github.com/socketio/socket.io-protocol
Depending on the adapter, Socket#join() may return:
- nothing (in-memory and Redis adapters)
- a promise (custom adapters)
Breaking change: Socket#join() and Socket#leave() do not accept a
callback argument anymore.
Before:
```js
socket.join("room1", () => {
io.to("room1").emit("hello");
});
```
After:
```
socket.join("room1");
io.to("room1").emit("hello");
// or await socket.join("room1"); for custom adapters
```
Note: the need for an asynchronous method came from the Redis adapter,
which did override the Adapter#add() method in earlier versions, but
this is not the case anymore.
Reference:
- https://github.com/socketio/socket.io/blob/2.3.0/lib/socket.js#L236-L258
- https://github.com/socketio/socket.io-adapter/blob/1.1.2/index.js#L56-L65
- 05f926e13e
Related: https://github.com/socketio/socket.io/issues/3662
In order to be able to cast it on the argument of the "connect" event:
```js
import { Socket } from "socket.io";
io.on("connect", (socket: Socket) => {
// ...
});
```
The client bundles are included in the repository in order to remove
socket.io-client from the list of production dependencies and thus to
reduce the total number of dependencies when installing the server.
This means the release of the client and the server must now be in sync
(which is almost always the case actually).
The minified build is now served:
- /<path>/socket.io.js
- /<path>/socket.io.js.map
- /<path>/socket.io.min.js
- /<path>/socket.io.min.js.map
The content will now be compressed as well.
There are two issues with the typings:
- on the client-side, the Emitter class is not properly imported (hence the @ts-ignore)
- on the server-side, the Socket class is not exported (in order to cast it in the "connect" event)