The "drain" event (added in [1]) had two different meanings:
- the transport is ready to be written
- the packets are sent over the wire
For the WebSocket and the WebTransport transports, those two events
happen at the same time, but this is not the case for the HTTP
long-polling transport:
- the transport is ready to be written when the client sends a GET request
- the packets are sent over the wire when the server responds to the GET request
Which caused an issue with send callbacks during an upgrade, since the
packets were written but the client would not open a new GET request.
There are now two distinct events: "ready" and "drain"
Related: https://github.com/socketio/engine.io/issues/695
[1]: 2a93f06e27
This bug only exists for polling transport connections running on top
of uWS.
If the remote client abruptly disconnects (thus aborting the request)
while the server is waiting on an asynchronous operation such as
compression, the server may attempt to write a response via the aborted
response object. This causes an uncaught exception to be thrown.
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
The class used to accumulate the response headers did not expose the
exact same API as its wrapped type, which could lead to the following
error in some rare cases:
> TypeError: Cannot read properties of undefined (reading 'end')
> at Polling.onDataRequest (build/transports-uws/polling.js:109:53)
> at Polling.onRequest (build/transports-uws/polling.js:47:18)
> at callback (build/userver.js:94:56)
> at uServer.verify (build/server.js:152:9)
Related: https://github.com/socketio/socket.io/issues/4643
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>
When binding to an uWebSockets.js application, the server could crash
with the following error:
```
TypeError: res.onData is not a function
at Polling.onDataRequest (build/transports-uws/polling.js:133:13)
at Polling.onRequest (build/transports-uws/polling.js:47:18)
at callback (build/userver.js:80:56)
```
Related: https://github.com/socketio/engine.io/issues/637
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