Commit Graph

158 Commits

Author SHA1 Message Date
Damien Arrachequesne
6d8a0bea49 refactor: move the req attribute to the polling class 2024-06-21 14:03:22 +02:00
Damien Arrachequesne
c310b7b6b6 refactor: improve types 2024-06-21 14:03:01 +02:00
Damien Arrachequesne
362bc78191 fix: properly call the send callback during upgrade
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
2024-06-21 11:47:41 +02:00
Damien Arrachequesne
9a68c8ce93 perf(websocket): use bound callbacks
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
2024-06-17 17:47:46 +02:00
Damien Arrachequesne
ef1c4c8bb7 refactor: remove the wsPreEncoded option
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
2024-06-13 23:55:16 +02:00
Jonathan Perret
fc21c4a05f fix: fix websocket and webtransport send callbacks (#699)
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
2024-06-13 23:02:22 +02:00
Damien Arrachequesne
f27a6c3501 refactor: remove useless reference
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.
2023-11-09 11:45:43 +01:00
Damien Arrachequesne
9545b44b3c refactor: add cache-control header in the polling response
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
2023-10-05 17:19:08 +02:00
Damien Arrachequesne
ff1c861548 fix(webtransport): properly handle abruptly closed connections
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
2023-10-05 16:57:33 +02:00
Damien Arrachequesne
a306db09e8 fix(webtransport): add proper framing
WebTransport being a stream-based protocol, the chunking boundaries are
not necessarily preserved. That's why we need a header indicating the
type of the payload (plain text or binary) and its length.

We will use a format inspired by the WebSocket frame:

- first bit indicates whether the payload is binary
- the next 7 bits are either:
  - 125 or less: that's the length of the payload
  - 126: the next 2 bytes represent the length of the payload
  - 127: the next 8 bytes represent the length of the payload

Reference: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#decoding_payload_length

Related:

- https://github.com/socketio/engine.io/issues/687
- https://github.com/socketio/engine.io/issues/688
2023-08-02 01:00:42 +02:00
Damien Arrachequesne
123b68c04f feat: add support for WebTransport
Reference: https://developer.mozilla.org/en-US/docs/Web/API/WebTransport
2023-06-11 09:42:45 +02:00
Damien Arrachequesne
911d0e3575 refactor: return HTTP 400 upon invalid request overlap
In both cases, the error comes from the client as it should not send
multiple concurrent requests, so a HTTP 4xx code is mandated.

Related: https://github.com/socketio/engine.io/issues/650
2023-05-01 07:42:43 +02:00
Damien Arrachequesne
a65a047526 fix: wait for all packets to be sent before closing the WebSocket connection
This reverts commit [1], which was included in `engine.io@5.1.0` and
`socket.io@4.1.0`.

The WebSocket connection was closed before all packets were written
out, so for example when calling `socket.disconnect(true)` on the
Socket.IO server (which disconnect from all namespaces and close the
connection), the client would receive only the first disconnect packet
and kept trying to reconnect to the other namespaces.

The only difference with the previous implementation (pre 5.1.0) is
that the "drain" event gets only called once at the end, and not after
each packet.

[1]: ad5306aeae

Related: https://github.com/socketio/engine.io/issues/648
2023-01-10 16:42:12 +01:00
Damien Arrachequesne
bc98bf1232 refactor: bump prettier to version 2.8.1
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() {} }`
2023-01-10 15:22:57 +01:00
Damien Arrachequesne
33dc073172 docs: add some TODOs for the next major release 2023-01-10 14:57:58 +01:00
Damien Arrachequesne
5e34722b0b perf: add the wsPreEncodedFrame option
This optimization is only applied if:

- the permessage-deflate extension is disabled (which is the default)
- the "ws" package is used (which is the default)

In that case, the WebSocket frame will only be computed once, when
broadcasting to multiple clients.

Related: 5f7b47d40f
2023-01-09 10:34:25 +01:00
Damien Arrachequesne
e24b27b8ef refactor: return an HTTP 413 response for too large payloads
Before this, the connection was closed abrutly with an HTTP 502
response.

See also: f8100f9237

Related: https://github.com/socketio/socket.io/issues/4293
2022-02-28 07:21:53 +01:00
Damien Arrachequesne
64d5754511 chore: bump ws
Release notes: https://github.com/websockets/ws/releases/tag/8.0.0
2021-10-08 15:05:51 +02:00
Damien Arrachequesne
c0d6eaa1ba chore: migrate to TypeScript
Related: https://github.com/socketio/engine.io/issues/510
2021-10-08 14:55:30 +02:00
Damien Arrachequesne
7706b123df perf: add a "wsPreEncoded" writing option
This option will be used when broadcasting a packet to multiple clients,
in order to only encode the packet once.

Usage:

```js
socket.write("test", {
  wsPreEncoded: "4test"
});
```

Note: pre-encoding the content with HTTP long-polling is a bit harder,
since the concatenation of the packets is specific to each client.
2021-05-04 08:44:06 +02:00
Branislav Katreniak
ad5306aeae perf(websocket): fix write back-pressure (#618)
This change should reduce memory usage when many packets are emitted to
many clients in a burst.

Co-authored-by: Branislav Katreniak <bkatreniak@slido.com>
2021-05-04 08:35:23 +02:00
Damien Arrachequesne
4c0aa73e06 refactor: remove "self" references 2021-04-30 14:38:31 +02:00
Damien Arrachequesne
252754353a feat: add the "initial_headers" and "headers" events
Those events will be emitted before the response headers are written to
the socket:

- "initial_headers": on the first request of the connection
- "headers": on all requests (HTTP long-polling and WebSocket upgrade)

Syntax:

```js

server.on("initial_headers", (headers, req) => {
  headers["test"] = "123";
  headers["set-cookie"] = "mycookie=456";
});

server.on("headers", (headers, req) => {
  headers["test"] = "789";
});
```

Related:

- https://github.com/socketio/engine.io/issues/557
- https://github.com/socketio/socket.io/issues/3630
2021-04-30 14:38:11 +02:00
Damien Arrachequesne
663d326d18 feat: add support for v3.x clients
In order to ease the migration to Socket.IO v3, the Engine.IO server
can now communicate with v3.x clients.

```js
const eioServer = require("engine.io")(httpServer, {
  allowEIO3: true // false by default
});
```

If `allowEIO3` is false, the v3.x clients will now receive an HTTP 400
response ("Unsupported protocol version").

Note: the code of the v3 parser has been imported from [1] and
browser-related dependencies were removed.

[1]: https://github.com/socketio/engine.io-parser/tree/2.2.1

Related:

- https://github.com/socketio/engine.io-protocol/issues/35
- https://github.com/socketio/socket.io-protocol/issues/21
2021-01-14 01:44:52 +01:00
Damien Arrachequesne
c099338e04 refactor: remove binary handling for the polling transport
Since Engine.IO v4, the binary payloads are always encoded as base64
with the polling transport.

See https://github.com/socketio/engine.io-protocol#difference-between-v3-and-v4
2020-10-22 00:12:05 +02:00
Damien Arrachequesne
ed29e5955d chore: bump engine.io-parser version
See also: https://github.com/socketio/engine.io-protocol#difference-between-v3-and-v4

Release: https://github.com/socketio/engine.io-parser/releases/tag/4.0.0
Diff: https://github.com/socketio/engine.io-parser/compare/2.2.0...4.0.0
2020-09-09 00:00:25 +02:00
Damien Arrachequesne
61b949259e feat: use the cors module to handle cross-origin requests
We'll now rely on the standard cors module (https://github.com/expressjs/cors),
instead of the custom implementation that is error-prone and not
really user-friendly.

Breaking change: the handlePreflightRequest option is removed by the
change.

Before:

```
new Server({
  handlePreflightRequest: (req, res) => {
    res.writeHead(200, {
      "Access-Control-Allow-Origin": 'https://example.com',
      "Access-Control-Allow-Methods": 'GET',
      "Access-Control-Allow-Headers": 'Authorization',
      "Access-Control-Allow-Credentials": true
    });
    res.end();
  }
})
```

After:

```
new Server({
  cors: {
    origin: "https://example.com",
    methods: ["GET"],
    allowedHeaders: ["Authorization"],
    credentials: true
  }
})
```
2020-02-11 07:54:25 +01:00
Damien Arrachequesne
33564b2391 refactor: use prettier to format code 2020-01-14 21:58:38 +01:00
Damien Arrachequesne
da93fb6ef3 refactor: migrate to ES6 syntax 2020-01-12 22:47:18 +01:00
Damien Arrachequesne
ec4e12a063 [revert] Allow configuration of Access-Control-Allow-Origin value (#511)
This reverts commit ebf1a96f42.

Related: https://github.com/socketio/socket.io/issues/3381
2018-11-29 22:49:34 +01:00
Jacco Flenter
ebf1a96f42 [feat] Allow configuration of Access-Control-Allow-Origin value (#511)
It's now possible to specify an origins value (default value is '*') when initialising the engine. This value will be returned as the Access-Control-Allow-Origin value.

Related: #449
2018-11-02 07:38:16 +01:00
Damien Arrachequesne
be3833bddd [refactor] Use Buffer.concat([]) to construct an empty buffer (#555) 2018-02-27 22:03:31 +01:00
Damien Arrachequesne
fd20b91208 [test] Use npm scripts instead of gulp (#530) 2017-09-01 14:21:57 +02:00
Raúl Montes
7f63d38b2a [fix] Fix undeterministic error in polling buffer processing (#529)
Ensure all assignments for chunks are Buffer when they should, even when onData is supposed to not
be called again.
2017-09-01 13:03:48 +02:00
Harutyun Amirjanyan
3dcc2d5cbd [fix] Use workaround for setEncoding bug in node 0.10+ (#527)
The server often crashes with 'TypeError: "list" argument must be an Array of Buffers' errors,
which is caused by a bug in new versions of node, where setEncoding call does not work for messages
that are already in the queue.

This pull request makes sure that concat is never called in the non binary case, even if
setEncoding does not work properly.
2017-08-31 22:49:18 +02:00
Matt90o
57ec952ff7 [docs] Fix spelling mistake (#466)
Edited a minor spelling mistake. 'Comparesses' -> 'Compresses'
2017-01-04 06:39:47 +01:00
Gatsbill
01c6a12de5 [perf] websocket optimisation (#453) 2016-12-05 23:59:24 +01:00
Dominik Paľo
8450d03f06 [docs] Rename ServerRequest to IncomingMessage (#448)
`http.ServerRequest` did refer to the HTTP API of Nodejs prior to v0.10
2016-12-05 23:28:01 +01:00
Diga Widyaprana
7cbdd5e5d9 [style] Add ESlint (#385) 2016-10-31 02:40:58 +01:00
Kim Min-Ho
e008ffeba5 [style] Fix some typos (#439) 2016-10-25 00:58:01 +02:00
Luigi Pinca
cd97c7f412 polling: don't set the closeTimeoutTimer if the transport is upgraded 2016-01-17 20:19:03 +01:00
nkzawa
78b287742d improve X-XSS-Protection header definition 2015-12-01 23:03:44 +09:00
nkzawa
1472a79884 remove the compress option from control packets 2015-12-01 14:28:33 +09:00
nkzawa
b56603169a add threshold for permessage-deflate 2015-12-01 14:21:53 +09:00
nkzawa
5092c4fc95 don't compress control packets 2015-11-29 23:27:08 +09:00
Guillermo Rauch
b79830edd7 Merge pull request #348 from nkzawa/patch-7
Fix polling transports and add tests for closing transports
2015-11-18 10:41:15 -08:00
Guillermo Rauch
8163660cda Merge pull request #338 from nkzawa/patch-2
Call res.end() when polling error
2015-11-18 10:40:50 -08:00
Guillermo Rauch
fb811bf200 Merge pull request #305 from lbdremy/fix/set-encoding-before-listening-on-data
Set encoding of the request to utf8 before listening on data events
2015-11-16 13:27:28 -08:00
nkzawa
4bce72c160 call onClose and add close timeout on polling transport 2015-09-09 03:55:10 +09:00
nkzawa
8c018befe3 call res.end when polling error 2015-08-20 03:10:50 +09:00