This behavior was added in [1]. However, there are two problems:
- a new timer is allocated every time a packet is received, which is
wasteful
- the next heartbeat is not actually delayed, since it's the timeout
timer which gets reset, and not the interval timer
Note: delaying the next heartbeat would be a breaking change.
[1]: be7b4e7478
Instead of allocating one temporary function for each WebSocket
`send()` call.
Regarding the test removal, the permessage-deflate threshold was
implemented in the "ws" package in [1], so it's not needed anymore.
[1]: 6b3904b42d
The wsPreEncoded option was added in the `socket.io-adapter` package
when broadcasting a message to multiple clients.
It was removed in [1] and is now superseded by the `wsPreEncodedFrame`
option, which directly computes the WebSocket frame once for all
clients (see [2]).
[1]: 88eee5948a
[2]: 5f7b47d40f
With the `websocket` transport, the callbacks which indicate that the
packets are actually written were not properly called.
Example:
```js
socket.send("hello", () => {
// the message has been written to the underlying transport
});
```
The bug was caused by the `websocket` transport (and `webtransport` as
well) having its `supportsFraming` property set to `true`, despite
having been changed in [1] to emit a single `drain` event for each
batch of messages written to the transport like the `polling` transport
always did. Note that although [1] is partially reverted in [2], the
new `drain` event behavior is preserved as called out in that commit's
message.
The `supportsFraming` attribute was introduced in [3] (amended by [4])
as a way to distinguish transports that emit one `drain` per message
from those that emit one `drain` per batch. Since the delivery of
`send` callbacks depends on matching `drain` events with
`transport.send` calls, that distinction is vital to correct behavior.
However, now that all transports have converged to "one `drain` per
batch" behavior, this `supportsFraming` property can be retired (and
the code for calling callbacks simplified).
[1]: https://github.com/socketio/engine.io/pull/618
[2]: a65a047526
[3]: https://github.com/socketio/engine.io/pull/130
[4]: https://github.com/socketio/engine.io/pull/132
Related: https://github.com/socketio/engine.io/issues/698
A reference to the initial IncomingMessage object (the first HTTP
request of the session) is kept in memory by default (`socket.request`),
so its attached ServerResponse object (`req.res`) would not be
garbage-collected. This will now be the case.
Note: the IncomingMessage object is needed in two cases:
- when working with the `express-session` middleware (`request.session`)
- when fetching the certificate of the client with `request.socket.getPeerCertificate()`
That's why removing it would be a breaking change.
This header should not be needed since the client already includes a
cache busting query parameter ("t"), but a misconfigured CDN could
ignore the query parameters and cache the server response.
Related: https://github.com/socketio/socket.io/issues/4842
Refreshing the page with a client connected with WebTransport would
trigger the following exception:
> node:internal/process/promises:288
> triggerUncaughtException(err, true /* fromPromise */);
> ^
>
> [UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "0".] {
> code: 'ERR_UNHANDLED_REJECTION'
> }
Related: https://github.com/socketio/engine.io/issues/688
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
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
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