A specially crafted request could lead to the following exception:
> TypeError: Cannot read properties of undefined (reading 'handlesUpgrades')
> at Server.onWebSocket (build/server.js:515:67)
This bug was introduced in [1], released in version 5.1.0 and included
in version 4.1.0 of the `socket.io` parent package. Older versions are
not impacted.
[1]: 7096e98a02
In order to prevent issues like:
> error TS2345: Argument of type 'RequestHandler<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' is not assignable to parameter of type 'Middleware'.
> Types of parameters 'req' and 'req' are incompatible.
> Type 'IncomingMessage' is missing the following properties from type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>': get, header, accepts, acceptsCharsets, and 29 more.
>
> io.engine.use(sessionMiddleware);
~~~~~~~~~~~~~~~~~
Related: https://github.com/socketio/socket.io/issues/4644
We could also have use the RequestHandler type from the
@types/express-serve-static-core package, but that would add 5 new
dependencies.
See also: https://github.com/socketio/engine.io/issues/673
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
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>
Before this change, receiving an HTTP2 upgrade would make the server
crash:
> Error: read ECONNRESET
> at TCP.onStreamRead (node:internal/stream_base_commons:217:20) {
> errno: -104,
> code: 'ECONNRESET',
> syscall: 'read'
> }
This can be reproduced with Node.js v14.15.3, v16.18.1 and v18.12.1.
When binding to an uWebSockets.js App, there was an unhandled case that
could crash the server:
```
curl "http://localhost:3000/engine.io/?EIO=4&transport=websocket"
```
would result in:
```
Error: Returning from a request handler without responding or attaching an abort handler is forbidden!
terminate called without an active exception
```
Note: this does not apply to the default server based on ws, because
the error was caught elsewhere in the source code.
Related: https://github.com/socketio/socket.io/issues/4250
**IMPORTANT SECURITY FIX**
A malicious client could send a specially crafted HTTP request,
triggering an uncaught exception and killing the Node.js process:
> RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear
> at Receiver.getInfo (/.../node_modules/ws/lib/receiver.js:176:14)
> at Receiver.startLoop (/.../node_modules/ws/lib/receiver.js:136:22)
> at Receiver._write (/.../node_modules/ws/lib/receiver.js:83:10)
> at writeOrBuffer (internal/streams/writable.js:358:12)
This bug was introduced by [1], included in `engine.io@4.0.0`, so
previous releases are not impacted.
[1]: f3c291fa61
Thanks to Marcus Wejderot from Mevisio for the responsible disclosure.