mirror of
https://github.com/socketio/socket.io.git
synced 2026-04-30 03:00:39 -04:00
Compare commits
29 Commits
socket.io-
...
engine.io@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
439a8f669c | ||
|
|
fc11285e14 | ||
|
|
b059af6b12 | ||
|
|
4e85378a46 | ||
|
|
d1f5aa9372 | ||
|
|
b7853771af | ||
|
|
25d877cd9f | ||
|
|
d4bc787731 | ||
|
|
1fa1f46cd4 | ||
|
|
4f7edb46ec | ||
|
|
85b26e5c99 | ||
|
|
e4d016bd5b | ||
|
|
8b93a18681 | ||
|
|
e6c722edbe | ||
|
|
8b0ab0a9d9 | ||
|
|
b25738c416 | ||
|
|
f6301588ca | ||
|
|
37aad11417 | ||
|
|
ba9cd6900d | ||
|
|
84c2fb7821 | ||
|
|
07cbe1510d | ||
|
|
44ed73f539 | ||
|
|
da04267ffc | ||
|
|
74599a6b9e | ||
|
|
d48718cb67 | ||
|
|
9978574e4f | ||
|
|
e9e5bed4f2 | ||
|
|
9581f9bcfd | ||
|
|
579d43f33f |
5
.github/workflows/build-examples.yml
vendored
5
.github/workflows/build-examples.yml
vendored
@@ -31,12 +31,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
|
||||
- name: Build ${{ matrix.example }}
|
||||
run: |
|
||||
|
||||
5
.github/workflows/ci-browser.yml
vendored
5
.github/workflows/ci-browser.yml
vendored
@@ -20,12 +20,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@@ -55,6 +55,7 @@ jobs:
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
@@ -67,12 +68,12 @@ jobs:
|
||||
|
||||
- name: Run tests with uws (engine.io)
|
||||
run: npm run test:uws --workspace=engine.io
|
||||
if: ${{ matrix.node-version == '18' }}
|
||||
if: ${{ matrix.node-version == '24' }}
|
||||
|
||||
- name: Run tests with fetch instead of XHR (engine.io-client)
|
||||
run: npm run test:node-fetch --workspace=engine.io-client
|
||||
if: ${{ matrix.node-version == '18' }}
|
||||
if: ${{ matrix.node-version == '24' }}
|
||||
|
||||
- name: Run tests with Node.js native WebSocket (engine.io-client)
|
||||
run: npm run test:node-builtin-ws --workspace=engine.io-client
|
||||
if: ${{ matrix.node-version == '22' }}
|
||||
if: ${{ matrix.node-version == '24' }}
|
||||
|
||||
5
.github/workflows/publish.yml
vendored
5
.github/workflows/publish.yml
vendored
@@ -17,13 +17,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Use Node.js 24
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
52
SECURITY.md
52
SECURITY.md
@@ -37,33 +37,35 @@ We will get back to you as soon as possible and publish a fix if necessary.
|
||||
|
||||
From the transitive dependencies:
|
||||
|
||||
| Date | Dependency | Description | CVE number |
|
||||
|---------------|--------------------|---------------------------------------------------------------------------------------------------------------|------------------|
|
||||
| January 2016 | `ws` | [Buffer vulnerability](https://github.com/advisories/GHSA-2mhh-w6q8-5hxw) | `CVE-2016-10518` |
|
||||
| January 2016 | `ws` | [DoS due to excessively large websocket message](https://github.com/advisories/GHSA-6663-c963-2gqg) | `CVE-2016-10542` |
|
||||
| November 2017 | `ws` | [DoS in the `Sec-Websocket-Extensions` header parser](https://github.com/advisories/GHSA-5v72-xg48-5rpm) | `-` |
|
||||
| February 2020 | `engine.io` | [Resource exhaustion](https://github.com/advisories/GHSA-j4f2-536g-r55m) | `CVE-2020-36048` |
|
||||
| January 2021 | `socket.io-parser` | [Resource exhaustion](https://github.com/advisories/GHSA-xfhh-g9f5-x4m4) | `CVE-2020-36049` |
|
||||
| May 2021 | `ws` | [ReDoS in `Sec-Websocket-Protocol` header](https://github.com/advisories/GHSA-6fc8-4gx4-v693) | `CVE-2021-32640` |
|
||||
| January 2022 | `engine.io` | [Uncaught exception](https://github.com/advisories/GHSA-273r-mgr4-v34f) | `CVE-2022-21676` |
|
||||
| October 2022 | `socket.io-parser` | [Insufficient validation when decoding a Socket.IO packet](https://github.com/advisories/GHSA-qm95-pgcg-qqfq) | `CVE-2022-2421` |
|
||||
| November 2022 | `engine.io` | [Uncaught exception](https://github.com/advisories/GHSA-r7qp-cfhv-p84w) | `CVE-2022-41940` |
|
||||
| May 2023 | `engine.io` | [Uncaught exception](https://github.com/advisories/GHSA-q9mw-68c2-j6m5) | `CVE-2023-31125` |
|
||||
| May 2023 | `socket.io-parser` | [Insufficient validation when decoding a Socket.IO packet](https://github.com/advisories/GHSA-cqmj-92xf-r6r9) | `CVE-2023-32695` |
|
||||
| June 2024 | `ws` | [DoS when handling a request with many HTTP headers](https://github.com/advisories/GHSA-3h5v-q93c-6h6q) | `CVE-2024-37890` |
|
||||
| Date | Dependency | Description | CVE number |
|
||||
|---------------|--------------------|-------------------------------------------------------------------------------------------------------------------------|------------------|
|
||||
| January 2016 | `ws` | [Buffer vulnerability](https://github.com/advisories/GHSA-2mhh-w6q8-5hxw) | `CVE-2016-10518` |
|
||||
| January 2016 | `ws` | [DoS due to excessively large websocket message](https://github.com/advisories/GHSA-6663-c963-2gqg) | `CVE-2016-10542` |
|
||||
| November 2017 | `ws` | [DoS in the `Sec-Websocket-Extensions` header parser](https://github.com/advisories/GHSA-5v72-xg48-5rpm) | `-` |
|
||||
| February 2020 | `engine.io` | [Resource exhaustion](https://github.com/advisories/GHSA-j4f2-536g-r55m) | `CVE-2020-36048` |
|
||||
| January 2021 | `socket.io-parser` | [Resource exhaustion](https://github.com/advisories/GHSA-xfhh-g9f5-x4m4) | `CVE-2020-36049` |
|
||||
| May 2021 | `ws` | [ReDoS in `Sec-Websocket-Protocol` header](https://github.com/advisories/GHSA-6fc8-4gx4-v693) | `CVE-2021-32640` |
|
||||
| January 2022 | `engine.io` | [Uncaught exception](https://github.com/advisories/GHSA-273r-mgr4-v34f) | `CVE-2022-21676` |
|
||||
| October 2022 | `socket.io-parser` | [Insufficient validation when decoding a Socket.IO packet](https://github.com/advisories/GHSA-qm95-pgcg-qqfq) | `CVE-2022-2421` |
|
||||
| November 2022 | `engine.io` | [Uncaught exception](https://github.com/advisories/GHSA-r7qp-cfhv-p84w) | `CVE-2022-41940` |
|
||||
| May 2023 | `engine.io` | [Uncaught exception](https://github.com/advisories/GHSA-q9mw-68c2-j6m5) | `CVE-2023-31125` |
|
||||
| May 2023 | `socket.io-parser` | [Insufficient validation when decoding a Socket.IO packet](https://github.com/advisories/GHSA-cqmj-92xf-r6r9) | `CVE-2023-32695` |
|
||||
| June 2024 | `ws` | [DoS when handling a request with many HTTP headers](https://github.com/advisories/GHSA-3h5v-q93c-6h6q) | `CVE-2024-37890` |
|
||||
| March 2026 | `socket.io-parser` | [Unbounded number of binary attachments](https://github.com/socketio/socket.io/security/advisories/GHSA-677m-j7p3-52f9) | `CVE-2026-33151` |
|
||||
|
||||
### For the `socket.io-client` package
|
||||
|
||||
From the transitive dependencies:
|
||||
|
||||
| Date | Dependency | Description | CVE number |
|
||||
|---------------|--------------------|---------------------------------------------------------------------------------------------------------------|------------------|
|
||||
| January 2016 | `ws` | [Buffer vulnerability](https://github.com/advisories/GHSA-2mhh-w6q8-5hxw) | `CVE-2016-10518` |
|
||||
| January 2016 | `ws` | [DoS due to excessively large websocket message](https://github.com/advisories/GHSA-6663-c963-2gqg) | `CVE-2016-10542` |
|
||||
| October 2016 | `engine.io-client` | [Insecure Defaults Allow MITM Over TLS](https://github.com/advisories/GHSA-4r4m-hjwj-43p8) | `CVE-2016-10536` |
|
||||
| November 2017 | `ws` | [DoS in the `Sec-Websocket-Extensions` header parser](https://github.com/advisories/GHSA-5v72-xg48-5rpm) | `-` |
|
||||
| January 2021 | `socket.io-parser` | [Resource exhaustion](https://github.com/advisories/GHSA-xfhh-g9f5-x4m4) | `CVE-2020-36049` |
|
||||
| May 2021 | `ws` | [ReDoS in `Sec-Websocket-Protocol` header](https://github.com/advisories/GHSA-6fc8-4gx4-v693) | `CVE-2021-32640` |
|
||||
| October 2022 | `socket.io-parser` | [Insufficient validation when decoding a Socket.IO packet](https://github.com/advisories/GHSA-qm95-pgcg-qqfq) | `CVE-2022-2421` |
|
||||
| May 2023 | `socket.io-parser` | [Insufficient validation when decoding a Socket.IO packet](https://github.com/advisories/GHSA-cqmj-92xf-r6r9) | `CVE-2023-32695` |
|
||||
| June 2024 | `ws` | [DoS when handling a request with many HTTP headers](https://github.com/advisories/GHSA-3h5v-q93c-6h6q) | `CVE-2024-37890` |
|
||||
| Date | Dependency | Description | CVE number |
|
||||
|---------------|--------------------|-------------------------------------------------------------------------------------------------------------------------|------------------|
|
||||
| January 2016 | `ws` | [Buffer vulnerability](https://github.com/advisories/GHSA-2mhh-w6q8-5hxw) | `CVE-2016-10518` |
|
||||
| January 2016 | `ws` | [DoS due to excessively large websocket message](https://github.com/advisories/GHSA-6663-c963-2gqg) | `CVE-2016-10542` |
|
||||
| October 2016 | `engine.io-client` | [Insecure Defaults Allow MITM Over TLS](https://github.com/advisories/GHSA-4r4m-hjwj-43p8) | `CVE-2016-10536` |
|
||||
| November 2017 | `ws` | [DoS in the `Sec-Websocket-Extensions` header parser](https://github.com/advisories/GHSA-5v72-xg48-5rpm) | `-` |
|
||||
| January 2021 | `socket.io-parser` | [Resource exhaustion](https://github.com/advisories/GHSA-xfhh-g9f5-x4m4) | `CVE-2020-36049` |
|
||||
| May 2021 | `ws` | [ReDoS in `Sec-Websocket-Protocol` header](https://github.com/advisories/GHSA-6fc8-4gx4-v693) | `CVE-2021-32640` |
|
||||
| October 2022 | `socket.io-parser` | [Insufficient validation when decoding a Socket.IO packet](https://github.com/advisories/GHSA-qm95-pgcg-qqfq) | `CVE-2022-2421` |
|
||||
| May 2023 | `socket.io-parser` | [Insufficient validation when decoding a Socket.IO packet](https://github.com/advisories/GHSA-cqmj-92xf-r6r9) | `CVE-2023-32695` |
|
||||
| June 2024 | `ws` | [DoS when handling a request with many HTTP headers](https://github.com/advisories/GHSA-3h5v-q93c-6h6q) | `CVE-2024-37890` |
|
||||
| March 2026 | `socket.io-parser` | [Unbounded number of binary attachments](https://github.com/socketio/socket.io/security/advisories/GHSA-677m-j7p3-52f9) | `CVE-2026-33151` |
|
||||
@@ -296,7 +296,7 @@ A payload is a series of encoded packets tied together. The payload encoding for
|
||||
<length1>:<packet1>[<length2>:<packet2>[...]]
|
||||
```
|
||||
* length: length of the packet in __characters__
|
||||
* packet: actual packets as descriped above
|
||||
* packet: actual packets as described above
|
||||
|
||||
When XHR2 is not supported, the same encoding principle is used also when
|
||||
binary data is sent, but it is sent as base64 encoded strings. For the purposes of decoding, an identifier `b` is
|
||||
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -3499,7 +3499,6 @@
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
@@ -16038,11 +16037,12 @@
|
||||
}
|
||||
},
|
||||
"packages/engine.io": {
|
||||
"version": "6.6.4",
|
||||
"version": "6.6.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/node": ">=10.0.0",
|
||||
"@types/ws": "^8.5.12",
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.7.2",
|
||||
@@ -16056,7 +16056,7 @@
|
||||
}
|
||||
},
|
||||
"packages/engine.io-client": {
|
||||
"version": "6.6.3",
|
||||
"version": "6.6.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
@@ -16082,7 +16082,7 @@
|
||||
}
|
||||
},
|
||||
"packages/socket.io": {
|
||||
"version": "4.8.1",
|
||||
"version": "4.8.3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.4",
|
||||
@@ -16098,7 +16098,7 @@
|
||||
}
|
||||
},
|
||||
"packages/socket.io-adapter": {
|
||||
"version": "2.5.5",
|
||||
"version": "2.5.6",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "~4.4.1",
|
||||
@@ -16106,7 +16106,7 @@
|
||||
}
|
||||
},
|
||||
"packages/socket.io-client": {
|
||||
"version": "4.8.1",
|
||||
"version": "4.8.3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
@@ -16164,7 +16164,7 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"packages/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"version": "4.2.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
const parser = require('.');
|
||||
|
||||
parser.encodePayload([
|
||||
{
|
||||
type: 'message',
|
||||
data: '€',
|
||||
},
|
||||
{
|
||||
type: 'message',
|
||||
data: Buffer.from([1, 2, 3, 4]),
|
||||
},
|
||||
], true, console.log);
|
||||
@@ -1,44 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const browsers = require("socket.io-browsers");
|
||||
|
||||
const zuulConfig = (module.exports = {
|
||||
ui: "mocha-bdd",
|
||||
|
||||
// test on localhost by default
|
||||
local: true,
|
||||
open: true,
|
||||
|
||||
concurrency: 2, // ngrok only accepts two tunnels by default
|
||||
// if browser does not sends output in 120s since last output:
|
||||
// stop testing, something is wrong
|
||||
browser_output_timeout: 120 * 1000,
|
||||
browser_open_timeout: 60 * 4 * 1000,
|
||||
// we want to be notified something is wrong asap, so no retry
|
||||
browser_retries: 1,
|
||||
|
||||
browserify: [
|
||||
{
|
||||
plugin: ["tsify", {
|
||||
target: "es5"
|
||||
}],
|
||||
transform: {
|
||||
name: "babelify",
|
||||
presets: ["@babel/preset-env"]
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (process.env.CI === "true") {
|
||||
zuulConfig.local = false;
|
||||
zuulConfig.tunnel = {
|
||||
type: "ngrok",
|
||||
bind_tls: true
|
||||
};
|
||||
}
|
||||
|
||||
const isPullRequest =
|
||||
process.env.TRAVIS_PULL_REQUEST &&
|
||||
process.env.TRAVIS_PULL_REQUEST !== "false";
|
||||
zuulConfig.browsers = isPullRequest ? browsers.pullRequest : browsers.all;
|
||||
@@ -1,51 +1,83 @@
|
||||
# Changelog
|
||||
|
||||
| Version | Release date |
|
||||
|------------------------------------------------------------------------------------------------------|----------------|
|
||||
| [6.6.5](#665-2025-12-22) | December 2025 |
|
||||
| [6.6.4](#664-2025-01-28) | January 2025 |
|
||||
| [6.6.3](#663-2025-01-23) | January 2025 |
|
||||
| [6.6.2](#662-2024-10-09) | October 2024 |
|
||||
| [6.6.1](#661-2024-09-21) | September 2024 |
|
||||
| [6.6.0](#660-2024-06-21) | June 2024 |
|
||||
| [6.5.5](#655-2024-06-18) (from the [6.5.x](https://github.com/socketio/engine.io/tree/6.5.x) branch) | June 2024 |
|
||||
| [3.6.2](#362-2024-06-18) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | June 2024 |
|
||||
| [6.5.4](#654-2023-11-09) | November 2023 |
|
||||
| [6.5.3](#653-2023-10-06) | October 2023 |
|
||||
| [6.5.2](#652-2023-08-01) | August 2023 |
|
||||
| [6.5.1](#651-2023-06-27) | June 2023 |
|
||||
| [6.5.0](#650-2023-06-16) | June 2023 |
|
||||
| [6.4.2](#642-2023-05-02) | May 2023 |
|
||||
| [6.4.1](#641-2023-02-20) | February 2023 |
|
||||
| [6.4.0](#640-2023-02-06) | February 2023 |
|
||||
| [6.3.1](#631-2023-01-12) | January 2023 |
|
||||
| [6.3.0](#630-2023-01-10) | January 2023 |
|
||||
| [3.6.1](#361-2022-11-20) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | November 2022 |
|
||||
| [6.2.1](#621-2022-11-20) | November 2022 |
|
||||
| [3.6.0](#360-2022-06-06) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | June 2022 |
|
||||
| [6.2.0](#620-2022-04-17) | April 2022 |
|
||||
| [6.1.3](#613-2022-02-23) | February 2022 |
|
||||
| [6.1.2](#612-2022-01-18) | January 2022 |
|
||||
| [6.1.1](#611-2022-01-11) | January 2021 |
|
||||
| [6.1.0](#610-2021-11-08) | November 2022 |
|
||||
| [6.0.1](#601-2021-11-06) | November 2021 |
|
||||
| [**6.0.0**](#600-2021-10-08) | October 2021 |
|
||||
| [5.2.0](#520-2021-08-29) | August 2021 |
|
||||
| [5.1.1](#511-2021-05-16) | May 2021 |
|
||||
| [5.1.0](#510-2021-05-04) | May 2021 |
|
||||
| [**5.0.0**](#500-2021-03-10) | March 2021 |
|
||||
| [4.1.1](#411-2021-02-02) | February 2021 |
|
||||
| [4.1.0](#410-2021-01-14) | January 2021 |
|
||||
| [4.0.6](#406-2021-01-04) | January 2021 |
|
||||
| [3.5.0](#350-2020-12-30) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | December 2020 |
|
||||
| [4.0.5](#405-2020-12-07) | December 2020 |
|
||||
| [4.0.4](#404-2020-11-17) | November 2020 |
|
||||
| [4.0.3](#403-2020-11-17) | November 2020 |
|
||||
| [4.0.2](#402-2020-11-09) | November 2020 |
|
||||
| [4.0.1](#401-2020-10-21) | October 2020 |
|
||||
| [**4.0.0**](#400-2020-09-10) | September 2020 |
|
||||
| [3.4.2](#342-2020-06-04) | June 2020 |
|
||||
| [3.4.1](#341-2020-04-17) | April 2020 |
|
||||
| Version | Release date | `ws` version |
|
||||
|------------------------------------------------------------------------------------------------------|----------------|--------------|
|
||||
| [6.6.6](#666-2026-03-10) | March 2026 | `"` |
|
||||
| [6.6.5](#665-2025-12-22) | December 2025 | `~8.18.3` |
|
||||
| [6.6.4](#664-2025-01-28) | January 2025 | `"` |
|
||||
| [6.6.3](#663-2025-01-23) | January 2025 | `"` |
|
||||
| [6.6.2](#662-2024-10-09) | October 2024 | `"` |
|
||||
| [6.6.1](#661-2024-09-21) | September 2024 | `"` |
|
||||
| [6.6.0](#660-2024-06-21) | June 2024 | `"` |
|
||||
| [6.5.5](#655-2024-06-18) (from the [6.5.x](https://github.com/socketio/engine.io/tree/6.5.x) branch) | June 2024 | `~8.17.1` |
|
||||
| [3.6.2](#362-2024-06-18) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | June 2024 | `~7.5.10` |
|
||||
| [6.5.4](#654-2023-11-09) | November 2023 | `"` |
|
||||
| [6.5.3](#653-2023-10-06) | October 2023 | `"` |
|
||||
| [6.5.2](#652-2023-08-01) | August 2023 | `"` |
|
||||
| [6.5.1](#651-2023-06-27) | June 2023 | `"` |
|
||||
| [6.5.0](#650-2023-06-16) | June 2023 | `"` |
|
||||
| [6.4.2](#642-2023-05-02) | May 2023 | `"` |
|
||||
| [6.4.1](#641-2023-02-20) | February 2023 | `"` |
|
||||
| [6.4.0](#640-2023-02-06) | February 2023 | `"` |
|
||||
| [6.3.1](#631-2023-01-12) | January 2023 | `"` |
|
||||
| [6.3.0](#630-2023-01-10) | January 2023 | `~8.11.0` |
|
||||
| [3.6.1](#361-2022-11-20) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | November 2022 | `"` |
|
||||
| [6.2.1](#621-2022-11-20) | November 2022 | `"` |
|
||||
| [3.6.0](#360-2022-06-06) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | June 2022 | `"` |
|
||||
| [6.2.0](#620-2022-04-17) | April 2022 | `"` |
|
||||
| [6.1.3](#613-2022-02-23) | February 2022 | `"` |
|
||||
| [6.1.2](#612-2022-01-18) | January 2022 | `"` |
|
||||
| [6.1.1](#611-2022-01-11) | January 2021 | `"` |
|
||||
| [6.1.0](#610-2021-11-08) | November 2022 | `"` |
|
||||
| [6.0.1](#601-2021-11-06) | November 2021 | `"` |
|
||||
| [**6.0.0**](#600-2021-10-08) | October 2021 | `~8.2.3` |
|
||||
| [5.2.0](#520-2021-08-29) | August 2021 | `"` |
|
||||
| [5.1.1](#511-2021-05-16) | May 2021 | `"` |
|
||||
| [5.1.0](#510-2021-05-04) | May 2021 | `"` |
|
||||
| [**5.0.0**](#500-2021-03-10) | March 2021 | `"` |
|
||||
| [4.1.1](#411-2021-02-02) | February 2021 | `"` |
|
||||
| [4.1.0](#410-2021-01-14) | January 2021 | `"` |
|
||||
| [4.0.6](#406-2021-01-04) | January 2021 | `"` |
|
||||
| [3.5.0](#350-2020-12-30) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | December 2020 | `~7.4.2` |
|
||||
| [4.0.5](#405-2020-12-07) | December 2020 | `"` |
|
||||
| [4.0.4](#404-2020-11-17) | November 2020 | `"` |
|
||||
| [4.0.3](#403-2020-11-17) | November 2020 | `"` |
|
||||
| [4.0.2](#402-2020-11-09) | November 2020 | `"` |
|
||||
| [4.0.1](#401-2020-10-21) | October 2020 | `"` |
|
||||
| [**4.0.0**](#400-2020-09-10) | September 2020 | `"` |
|
||||
| [3.4.2](#342-2020-06-04) | June 2020 | `"` |
|
||||
| [3.4.1](#341-2020-04-17) | April 2020 | `^7.1.2` |
|
||||
|
||||
|
||||
## [6.6.7](https://github.com/socketio/socket.io/compare/engine.io@6.6.6...engine.io@6.6.7) (2026-04-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* close HTTP requests with invalid content type ([fc11285](https://github.com/socketio/socket.io/commit/fc11285e14964c2132d122164bf130c355f60671))
|
||||
* handle invalid packets when upgrading to WebTransport ([1fa1f46](https://github.com/socketio/socket.io/commit/1fa1f46cd420ac5b57bb4c04c959b58f3c79158c))
|
||||
* prevent WebTransport connections when a middleware is registered ([d1f5aa9](https://github.com/socketio/socket.io/commit/d1f5aa93722a7f1ed729b96f771daf92a3dfdaf7))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [`ws@~8.18.3`](https://github.com/websockets/ws/releases/tag/8.18.3) (no change)
|
||||
|
||||
|
||||
|
||||
## [6.6.6](https://github.com/socketio/socket.io/compare/engine.io@6.6.5...engine.io@6.6.6) (2026-03-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add `@types/ws` as dependency ([#5458](https://github.com/socketio/socket.io/issues/5458)) ([07cbe15](https://github.com/socketio/socket.io/commit/07cbe1510ded7e5460cb82e026e2533e50e30eaf))
|
||||
* **uws** emit initial_headers and headers events in uServer ([#5460](https://github.com/socketio/socket.io/issues/5460)) ([44ed73f](https://github.com/socketio/socket.io/commit/44ed73f53995d35ef0c8d10df6806d5687238282))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [`ws@~8.18.3`](https://github.com/websockets/ws/releases/tag/8.18.3) (no change)
|
||||
|
||||
|
||||
|
||||
## [6.6.5](https://github.com/socketio/socket.io/compare/engine.io@6.6.4...engine.io@6.6.5) (2025-12-22)
|
||||
@@ -123,7 +155,7 @@ See also: https://github.com/advisories/GHSA-pxg6-pf52-xh8x
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* do not reset the hearbeat timer on each packet ([5359bae](https://github.com/socketio/engine.io/commit/5359bae683e2a25742bd4989d0355a8fc10d294e))
|
||||
* do not reset the heartbeat timer on each packet ([5359bae](https://github.com/socketio/engine.io/commit/5359bae683e2a25742bd4989d0355a8fc10d294e))
|
||||
* **websocket:** use bound callbacks ([9a68c8c](https://github.com/socketio/engine.io/commit/9a68c8ce93cc1bc0bc1a30548558da49860f4acd))
|
||||
|
||||
|
||||
@@ -520,9 +552,9 @@ Please upgrade as soon as possible.
|
||||
|
||||
* decrease the default value of maxHttpBufferSize ([58e274c](https://github.com/socketio/engine.io/commit/58e274c437e9cbcf69fd913c813aad8fbd253703))
|
||||
|
||||
This change reduces the default value from 100 mb to a more sane 1 mb.
|
||||
This change reduces the default value from 100 MB to a saner 1 MB.
|
||||
|
||||
This helps protect the server against denial of service attacks by malicious clients sending huge amounts of data.
|
||||
This helps protect the server against denial-of-service attacks by malicious clients sending huge amounts of data.
|
||||
|
||||
See also: https://github.com/advisories/GHSA-j4f2-536g-r55m
|
||||
|
||||
@@ -540,7 +572,7 @@ See also: https://github.com/advisories/GHSA-j4f2-536g-r55m
|
||||
So that clients in HTTP long-polling can decide how many packets they have to send to stay under the maxHttpBufferSize
|
||||
value.
|
||||
|
||||
This is a backward compatible change which should not mandate a new major revision of the protocol (we stay in v4), as
|
||||
This is a backward compatible change that should not mandate a new major revision of the protocol (we stay in v4), as
|
||||
we only add a field in the JSON-encoded handshake data:
|
||||
|
||||
```
|
||||
@@ -626,7 +658,7 @@ The codebase was migrated to TypeScript ([c0d6eaa](https://github.com/socketio/e
|
||||
|
||||
An ES module wrapper was also added ([401f4b6](https://github.com/socketio/engine.io/commit/401f4b60693fb6702c942692ce42e5bb701d81d7)).
|
||||
|
||||
Please note that the communication protocol was not updated, so a v5 client will be able to reach a v6 server (and vice-versa).
|
||||
Please note that the communication protocol was not updated, so a v5 client will be able to reach a v6 server (and vice versa).
|
||||
|
||||
Reference: https://github.com/socketio/engine.io-protocol
|
||||
|
||||
|
||||
@@ -166,6 +166,11 @@ function parseSessionId(data: string): string | undefined {
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// Object.hasOwn() was introduced in Node.js 16.9
|
||||
function hasOwn(obj: Record<string, any>, key: string): boolean {
|
||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||
}
|
||||
|
||||
export abstract class BaseServer extends EventEmitter {
|
||||
public opts: ServerOptions;
|
||||
|
||||
@@ -298,7 +303,7 @@ export abstract class BaseServer extends EventEmitter {
|
||||
// sid check
|
||||
const sid = req._query.sid;
|
||||
if (sid) {
|
||||
if (!this.clients.hasOwnProperty(sid)) {
|
||||
if (!hasOwn(this.clients, sid)) {
|
||||
debug('unknown sid "%s"', sid);
|
||||
return fn(Server.errors.UNKNOWN_SID, {
|
||||
sid,
|
||||
@@ -398,9 +403,9 @@ export abstract class BaseServer extends EventEmitter {
|
||||
*/
|
||||
public close() {
|
||||
debug("closing all open clients");
|
||||
for (let i in this.clients) {
|
||||
if (this.clients.hasOwnProperty(i)) {
|
||||
this.clients[i].close(true);
|
||||
for (const sid in this.clients) {
|
||||
if (hasOwn(this.clients, sid)) {
|
||||
this.clients[sid].close(true);
|
||||
}
|
||||
}
|
||||
this.cleanup();
|
||||
@@ -524,6 +529,15 @@ export abstract class BaseServer extends EventEmitter {
|
||||
}
|
||||
|
||||
public async onWebTransportSession(session: any) {
|
||||
if (this.middlewares.length > 0) {
|
||||
// middlewares expect an IncomingMessage argument, which cannot be created from the WebTransport session object
|
||||
// see also: https://github.com/fails-components/webtransport/issues/448
|
||||
debug(
|
||||
"closing session since WebTransport is not compatible with middlewares",
|
||||
);
|
||||
return session.close();
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
debug(
|
||||
"the client failed to establish a bidirectional stream in the given period",
|
||||
@@ -583,7 +597,7 @@ export abstract class BaseServer extends EventEmitter {
|
||||
|
||||
const sid = parseSessionId(value.data);
|
||||
|
||||
if (!sid) {
|
||||
if (!sid || !hasOwn(this.clients, sid)) {
|
||||
debug("invalid WebTransport handshake");
|
||||
return session.close();
|
||||
}
|
||||
@@ -750,18 +764,20 @@ export class Server extends BaseServer {
|
||||
/**
|
||||
* Handles an Engine.IO HTTP request.
|
||||
*
|
||||
* @param {EngineRequest} req
|
||||
* @param {IncomingMessage} req
|
||||
* @param {ServerResponse} res
|
||||
*/
|
||||
public handleRequest(req: EngineRequest, res: ServerResponse) {
|
||||
public handleRequest(req: IncomingMessage, res: ServerResponse) {
|
||||
debug('handling "%s" http request "%s"', req.method, req.url);
|
||||
this.prepare(req);
|
||||
req.res = res;
|
||||
const engineRequest = req as EngineRequest;
|
||||
|
||||
this.prepare(engineRequest);
|
||||
engineRequest.res = res;
|
||||
|
||||
const callback: ErrorCallback = (errorCode, errorContext) => {
|
||||
if (errorCode !== undefined) {
|
||||
this.emit("connection_error", {
|
||||
req,
|
||||
req: engineRequest,
|
||||
code: errorCode,
|
||||
message: Server.errorMessages[errorCode],
|
||||
context: errorContext,
|
||||
@@ -770,25 +786,27 @@ export class Server extends BaseServer {
|
||||
return;
|
||||
}
|
||||
|
||||
if (req._query.sid) {
|
||||
if (engineRequest._query.sid) {
|
||||
debug("setting new request for existing client");
|
||||
this.clients[req._query.sid].transport.onRequest(req);
|
||||
this.clients[engineRequest._query.sid].transport.onRequest(
|
||||
engineRequest,
|
||||
);
|
||||
} else {
|
||||
const closeConnection = (errorCode, errorContext) =>
|
||||
abortRequest(res, errorCode, errorContext);
|
||||
this.handshake(
|
||||
req._query.transport as TransportName,
|
||||
req,
|
||||
engineRequest._query.transport as TransportName,
|
||||
engineRequest,
|
||||
closeConnection,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
this._applyMiddlewares(req, res, (err) => {
|
||||
this._applyMiddlewares(engineRequest, res, (err) => {
|
||||
if (err) {
|
||||
callback(Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
|
||||
} else {
|
||||
this.verify(req, false, callback);
|
||||
this.verify(engineRequest, false, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -797,17 +815,19 @@ export class Server extends BaseServer {
|
||||
* Handles an Engine.IO HTTP Upgrade.
|
||||
*/
|
||||
public handleUpgrade(
|
||||
req: EngineRequest,
|
||||
req: IncomingMessage,
|
||||
socket: Duplex,
|
||||
upgradeHead: Buffer,
|
||||
) {
|
||||
this.prepare(req);
|
||||
const engineRequest = req as EngineRequest;
|
||||
|
||||
const res = new WebSocketResponse(req, socket);
|
||||
this.prepare(engineRequest);
|
||||
|
||||
const res = new WebSocketResponse(engineRequest, socket);
|
||||
const callback: ErrorCallback = (errorCode, errorContext) => {
|
||||
if (errorCode !== undefined) {
|
||||
this.emit("connection_error", {
|
||||
req,
|
||||
req: engineRequest,
|
||||
code: errorCode,
|
||||
message: Server.errorMessages[errorCode],
|
||||
context: errorContext,
|
||||
@@ -824,18 +844,22 @@ export class Server extends BaseServer {
|
||||
res.writeHead();
|
||||
|
||||
// delegate to ws
|
||||
this.ws.handleUpgrade(req, socket, head, (websocket) => {
|
||||
this.onWebSocket(req, socket, websocket);
|
||||
this.ws.handleUpgrade(engineRequest, socket, head, (websocket) => {
|
||||
this.onWebSocket(engineRequest, socket, websocket);
|
||||
});
|
||||
};
|
||||
|
||||
this._applyMiddlewares(req, res as unknown as ServerResponse, (err) => {
|
||||
if (err) {
|
||||
callback(Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
|
||||
} else {
|
||||
this.verify(req, true, callback);
|
||||
}
|
||||
});
|
||||
this._applyMiddlewares(
|
||||
engineRequest,
|
||||
res as unknown as ServerResponse,
|
||||
(err) => {
|
||||
if (err) {
|
||||
callback(Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
|
||||
} else {
|
||||
this.verify(engineRequest, true, callback);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -933,7 +957,7 @@ export class Server extends BaseServer {
|
||||
server.on("request", (req, res) => {
|
||||
if (check(req)) {
|
||||
debug('intercepting request for path "%s"', path);
|
||||
this.handleRequest(req as EngineRequest, res);
|
||||
this.handleRequest(req, res);
|
||||
} else {
|
||||
let i = 0;
|
||||
const l = listeners.length;
|
||||
@@ -946,7 +970,7 @@ export class Server extends BaseServer {
|
||||
if (~this.opts.transports.indexOf("websocket")) {
|
||||
server.on("upgrade", (req, socket, head) => {
|
||||
if (check(req)) {
|
||||
this.handleUpgrade(req as EngineRequest, socket, head);
|
||||
this.handleUpgrade(req, socket, head);
|
||||
} else if (false !== options.destroyUpgrade) {
|
||||
// default node behavior is to disconnect when no handlers
|
||||
// but by adding a handler, we prevent that
|
||||
|
||||
@@ -136,7 +136,8 @@ export class Polling extends Transport {
|
||||
const isBinary = "application/octet-stream" === req.headers["content-type"];
|
||||
|
||||
if (isBinary && this.protocol === 4) {
|
||||
return this.onError("invalid content");
|
||||
this.onError("invalid content");
|
||||
return res.writeStatus("400 Bad Request").end();
|
||||
}
|
||||
|
||||
this.dataReq = req;
|
||||
|
||||
@@ -122,7 +122,8 @@ export class Polling extends Transport {
|
||||
const isBinary = "application/octet-stream" === req.headers["content-type"];
|
||||
|
||||
if (isBinary && this.protocol === 4) {
|
||||
return this.onError("invalid content");
|
||||
this.onError("invalid content");
|
||||
return res.writeHead(400).end();
|
||||
}
|
||||
|
||||
this.dataReq = req;
|
||||
|
||||
@@ -226,9 +226,23 @@ export class uServer extends BaseServer {
|
||||
}
|
||||
}
|
||||
|
||||
// emit headers events for WebSocket upgrades
|
||||
const additionalHeaders = {};
|
||||
const isInitialRequest = !id;
|
||||
|
||||
if (isInitialRequest) {
|
||||
this.emit("initial_headers", additionalHeaders, req);
|
||||
}
|
||||
|
||||
this.emit("headers", additionalHeaders, req);
|
||||
|
||||
// calling writeStatus() triggers the flushing of any header added in a middleware
|
||||
req.res.writeStatus("101 Switching Protocols");
|
||||
|
||||
Object.keys(additionalHeaders).forEach((key) => {
|
||||
req.res.writeHeader(key, additionalHeaders[key]);
|
||||
});
|
||||
|
||||
res.upgrade(
|
||||
{
|
||||
transport,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "engine.io",
|
||||
"version": "6.6.5",
|
||||
"version": "6.6.7",
|
||||
"description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server",
|
||||
"type": "commonjs",
|
||||
"main": "./build/engine.io.js",
|
||||
@@ -33,6 +33,7 @@
|
||||
"dependencies": {
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/node": ">=10.0.0",
|
||||
"@types/ws": "^8.5.12",
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.7.2",
|
||||
|
||||
@@ -60,6 +60,32 @@ exports.listen = (opts, fn) => {
|
||||
return e;
|
||||
};
|
||||
|
||||
exports.listenAsync = function listenAsync(opts = {}) {
|
||||
return new Promise((resolve) => {
|
||||
const engine = exports.listen(opts, (port) => {
|
||||
resolve({
|
||||
port,
|
||||
close: () => {
|
||||
engine.close();
|
||||
if (engine.httpServer) {
|
||||
engine.httpServer.close();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.runHandshake = async function runHandshake(port) {
|
||||
const res = await fetch(
|
||||
`http://localhost:${port}/engine.io/?EIO=4&transport=polling`,
|
||||
);
|
||||
const data = await res.text();
|
||||
return {
|
||||
sid: JSON.parse(data.substring(1)).sid,
|
||||
};
|
||||
};
|
||||
|
||||
exports.ClientSocket = Socket;
|
||||
|
||||
exports.createPartialDone = (done, count) => {
|
||||
|
||||
@@ -7,7 +7,13 @@ const path = require("path");
|
||||
const exec = require("child_process").exec;
|
||||
const zlib = require("zlib");
|
||||
const { Server, Socket, attach } = require("..");
|
||||
const { ClientSocket, listen, createPartialDone } = require("./common");
|
||||
const {
|
||||
ClientSocket,
|
||||
listen,
|
||||
listenAsync,
|
||||
runHandshake,
|
||||
createPartialDone,
|
||||
} = require("./common");
|
||||
const expect = require("expect.js");
|
||||
const request = require("superagent");
|
||||
const cookieMod = require("cookie");
|
||||
@@ -581,7 +587,7 @@ describe("server", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should not suggest upgrades when none are availble", (done) => {
|
||||
it("should not suggest upgrades when none are available", (done) => {
|
||||
listen({ transports: ["polling"] }, (port) => {
|
||||
const socket = new ClientSocket(`ws://localhost:${port}`, {});
|
||||
socket.on("handshake", (obj) => {
|
||||
@@ -1458,6 +1464,26 @@ describe("server", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it("should abort the polling data request if the content type is invalid", async () => {
|
||||
const { port, close } = await listenAsync();
|
||||
const { sid } = await runHandshake(port);
|
||||
|
||||
const res = await fetch(
|
||||
`http://localhost:${port}/engine.io/?EIO=4&transport=polling&sid=${sid}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/octet-stream",
|
||||
},
|
||||
body: Buffer.of(1, 2, 3),
|
||||
},
|
||||
);
|
||||
|
||||
expect(res.status).to.eql(400);
|
||||
|
||||
close();
|
||||
});
|
||||
|
||||
// tests https://github.com/LearnBoost/engine.io-client/issues/207
|
||||
// websocket test, transport error
|
||||
it("should trigger transport close before open for ws", (done) => {
|
||||
@@ -3598,10 +3624,7 @@ describe("server", () => {
|
||||
});
|
||||
|
||||
it("should emit a 'initial_headers' event (websocket)", function (done) {
|
||||
if (
|
||||
process.env.EIO_WS_ENGINE === "eiows" ||
|
||||
process.env.EIO_WS_ENGINE === "uws"
|
||||
) {
|
||||
if (process.env.EIO_WS_ENGINE === "eiows") {
|
||||
return this.skip();
|
||||
}
|
||||
const partialDone = createPartialDone(done, 2);
|
||||
@@ -3644,10 +3667,7 @@ describe("server", () => {
|
||||
});
|
||||
|
||||
it("should emit several 'headers' events per connection", function (done) {
|
||||
if (
|
||||
process.env.EIO_WS_ENGINE === "eiows" ||
|
||||
process.env.EIO_WS_ENGINE === "uws"
|
||||
) {
|
||||
if (process.env.EIO_WS_ENGINE === "eiows") {
|
||||
return this.skip();
|
||||
}
|
||||
const partialDone = createPartialDone(done, 4);
|
||||
|
||||
@@ -302,6 +302,122 @@ describe("WebTransport", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should close a connection that sends an invalid upgrade", (done) => {
|
||||
setupServer(
|
||||
{
|
||||
transports: ["polling", "websocket", "webtransport"],
|
||||
},
|
||||
async ({ engine, h3Server, certificate }) => {
|
||||
const httpServer = await createHttpServer(h3Server.port);
|
||||
engine.attach(httpServer);
|
||||
|
||||
request(`http://localhost:${h3Server.port}/engine.io/`)
|
||||
.query({ EIO: 4, transport: "polling" })
|
||||
.end(async (_, res) => {
|
||||
const payload = JSON.parse(res.text.substring(1));
|
||||
|
||||
expect(payload.upgrades).to.eql(["websocket", "webtransport"]);
|
||||
|
||||
const client = new WebTransport(
|
||||
`https://127.0.0.1:${h3Server.port}/engine.io/`,
|
||||
{
|
||||
serverCertificateHashes: [
|
||||
{
|
||||
algorithm: "sha-256",
|
||||
value: certificate.hash,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
await client.ready;
|
||||
|
||||
const stream = await client.createBidirectionalStream();
|
||||
const writer = stream.writable.getWriter();
|
||||
|
||||
await writer.write(Uint8Array.of(31));
|
||||
await writer.write(
|
||||
TEXT_ENCODER.encode(`0{"sid":"11111111111111111111"}`),
|
||||
);
|
||||
|
||||
client.closed.then(() => {
|
||||
success(engine, h3Server, done);
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should close a connection that sends an invalid upgrade (bis)", (done) => {
|
||||
setupServer(
|
||||
{
|
||||
transports: ["polling", "websocket", "webtransport"],
|
||||
},
|
||||
async ({ engine, h3Server, certificate }) => {
|
||||
const httpServer = await createHttpServer(h3Server.port);
|
||||
engine.attach(httpServer);
|
||||
|
||||
request(`http://localhost:${h3Server.port}/engine.io/`)
|
||||
.query({ EIO: 4, transport: "polling" })
|
||||
.end(async (_, res) => {
|
||||
const payload = JSON.parse(res.text.substring(1));
|
||||
|
||||
expect(payload.upgrades).to.eql(["websocket", "webtransport"]);
|
||||
|
||||
const client = new WebTransport(
|
||||
`https://127.0.0.1:${h3Server.port}/engine.io/`,
|
||||
{
|
||||
serverCertificateHashes: [
|
||||
{
|
||||
algorithm: "sha-256",
|
||||
value: certificate.hash,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
await client.ready;
|
||||
|
||||
const stream = await client.createBidirectionalStream();
|
||||
const writer = stream.writable.getWriter();
|
||||
|
||||
await writer.write(Uint8Array.of(20));
|
||||
await writer.write(TEXT_ENCODER.encode(`0{"sid":"__proto__"}`));
|
||||
|
||||
client.closed.then(() => {
|
||||
success(engine, h3Server, done);
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("should refuse the connection when a middleware is registered", (done) => {
|
||||
setupServer({}, async ({ engine, h3Server, certificate }) => {
|
||||
engine.use((req, res, next) => next());
|
||||
|
||||
engine.on("connection", () => {
|
||||
done(new Error("should not happen"));
|
||||
});
|
||||
|
||||
const client = new WebTransport(
|
||||
`https://127.0.0.1:${h3Server.port}/engine.io/`,
|
||||
{
|
||||
serverCertificateHashes: [
|
||||
{
|
||||
algorithm: "sha-256",
|
||||
value: certificate.hash,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
await client.closed;
|
||||
|
||||
success(engine, h3Server, done);
|
||||
});
|
||||
});
|
||||
|
||||
it("should send ping/pong packets", (done) => {
|
||||
setup(
|
||||
{
|
||||
|
||||
@@ -438,11 +438,7 @@ export abstract class ClusterAdapter extends Adapter {
|
||||
});
|
||||
this.addOffsetIfNecessary(packet, opts, offset);
|
||||
} catch (e) {
|
||||
return debug(
|
||||
"[%s] error while broadcasting message: %s",
|
||||
this.uid,
|
||||
e.message,
|
||||
);
|
||||
debug("[%s] error while broadcasting message: %s", this.uid, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ const NODES_COUNT = 3;
|
||||
|
||||
class EventEmitterAdapter extends ClusterAdapterWithHeartbeat {
|
||||
private offset = 1;
|
||||
public shouldFailPublish = false;
|
||||
|
||||
constructor(
|
||||
nsp,
|
||||
@@ -27,6 +28,9 @@ class EventEmitterAdapter extends ClusterAdapterWithHeartbeat {
|
||||
}
|
||||
|
||||
protected doPublish(message: ClusterMessage): Promise<string> {
|
||||
if (this.shouldFailPublish) {
|
||||
return Promise.reject(new Error("publish failed"));
|
||||
}
|
||||
this.eventBus.emit("message", message);
|
||||
return Promise.resolve(String(++this.offset));
|
||||
}
|
||||
@@ -152,6 +156,19 @@ describe("cluster adapter", () => {
|
||||
servers[0].local.emit("test");
|
||||
});
|
||||
|
||||
it("broadcasts to local clients even when publishAndReturnOffset throws", (done) => {
|
||||
const adapter = servers[0].of("/").adapter as EventEmitterAdapter;
|
||||
adapter.shouldFailPublish = true;
|
||||
|
||||
clientSockets[0].on("test", (arg1) => {
|
||||
expect(arg1).to.eql(1);
|
||||
adapter.shouldFailPublish = false;
|
||||
done();
|
||||
});
|
||||
|
||||
servers[0].emit("test", 1);
|
||||
});
|
||||
|
||||
it("broadcasts with multiple acknowledgements", (done) => {
|
||||
clientSockets[0].on("test", (cb) => cb(1));
|
||||
clientSockets[1].on("test", (cb) => cb(2));
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
| Version | Release date | Bundle size (UMD min+gzip) |
|
||||
|-------------------------------------------------------------------------------------------------------------|----------------|----------------------------|
|
||||
| [4.8.2](#482-2025-12-22) | December 2024 | `14.4 KB` |
|
||||
| [4.8.3](#483-2025-12-23) | December 2025 | `14.4 KB` |
|
||||
| [4.8.2](#482-2025-12-22) | December 2025 | `14.4 KB` |
|
||||
| [4.8.1](#481-2024-10-25) | October 2024 | `14.4 KB` |
|
||||
| [4.8.0](#480-2024-09-21) | September 2024 | `14.4 KB` |
|
||||
| [4.7.5](#475-2024-03-14) | March 2024 | `14.6 KB` |
|
||||
@@ -51,6 +52,18 @@
|
||||
| [2.1.0](#210-2018-03-29) | March 2018 | `18.7 KB` |
|
||||
|
||||
|
||||
## [4.8.3](https://github.com/socketio/socket.io/compare/socket.io-client@4.8.2...socket.io-client@4.8.3) (2025-12-23)
|
||||
|
||||
There were some minor bug fixes on the server side, which mandate a client bump.
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [`engine.io-client@~6.6.1`](https://github.com/socketio/engine.io-client/releases/tag/6.5.2) (no change)
|
||||
- [`ws@~8.18.3`](https://github.com/websockets/ws/releases/tag/8.18.3) ([diff](https://github.com/websockets/ws/compare/8.17.1...8.18.3))
|
||||
|
||||
|
||||
|
||||
## [4.8.2](https://github.com/socketio/socket.io/compare/socket.io-client@4.8.1...socket.io-client@4.8.2) (2025-12-22)
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Socket.IO v4.8.2
|
||||
* Socket.IO v4.8.3
|
||||
* (c) 2014-2025 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
2
packages/socket.io-client/dist/socket.io.js
vendored
2
packages/socket.io-client/dist/socket.io.js
vendored
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Socket.IO v4.8.2
|
||||
* Socket.IO v4.8.3
|
||||
* (c) 2014-2025 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Socket.IO v4.8.2
|
||||
* Socket.IO v4.8.3
|
||||
* (c) 2014-2025 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Socket.IO v4.8.2
|
||||
* Socket.IO v4.8.3
|
||||
* (c) 2014-2025 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io-client",
|
||||
"version": "4.8.2",
|
||||
"version": "4.8.3",
|
||||
"description": "Realtime application framework client",
|
||||
"keywords": [
|
||||
"realtime",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "socket.io-client",
|
||||
"version": "4.8.2",
|
||||
"version": "4.8.3",
|
||||
"type": "module"
|
||||
}
|
||||
|
||||
@@ -1,37 +1,66 @@
|
||||
# Changelog
|
||||
|
||||
| Version | Release date |
|
||||
|-------------------------------------------------------------------------------------------------------------|----------------|
|
||||
| [4.2.5](#425-2025-12-23) | December 2025 |
|
||||
| [3.3.4](#334-2024-07-22) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | July 2024 |
|
||||
| [4.2.4](#424-2023-05-31) | May 2023 |
|
||||
| [3.4.3](#343-2023-05-22) (from the [3.4.x](https://github.com/socketio/socket.io-parser/tree/3.4.x) branch) | May 2023 |
|
||||
| [4.2.3](#423-2023-05-22) | May 2023 |
|
||||
| [4.2.2](#422-2023-01-19) | January 2023 |
|
||||
| [3.3.3](#333-2022-11-09) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | November 2022 |
|
||||
| [3.4.2](#342-2022-11-09) (from the [3.4.x](https://github.com/socketio/socket.io-parser/tree/3.4.x) branch) | November 2022 |
|
||||
| [4.0.5](#405-2022-06-27) (from the [4.0.x](https://github.com/socketio/socket.io-parser/tree/4.0.x) branch) | June 2022 |
|
||||
| [4.2.1](#421-2022-06-27) | June 2022 |
|
||||
| [4.2.0](#420-2022-04-17) | April 2022 |
|
||||
| [4.1.2](#412-2022-02-17) | February 2022 |
|
||||
| [3.3.3](#333-2022-11-09) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | November 2022 |
|
||||
| [3.4.2](#342-2022-11-09) (from the [3.4.x](https://github.com/socketio/socket.io-parser/tree/3.4.x) branch) | November 2022 |
|
||||
| [4.0.5](#405-2022-06-27) (from the [4.0.x](https://github.com/socketio/socket.io-parser/tree/4.0.x) branch) | June 2022 |
|
||||
| [4.2.1](#421-2022-06-27) | June 2022 |
|
||||
| [4.2.0](#420-2022-04-17) | April 2022 |
|
||||
| [4.1.2](#412-2022-02-17) | February 2022 |
|
||||
| [4.1.1](#411-2021-10-14) | October 2021 |
|
||||
| [4.1.0](#410-2021-10-11) | October 2021 |
|
||||
| [4.0.4](#404-2021-01-15) | January 2021 |
|
||||
| [3.3.2](#332-2021-01-09) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | January 2021 |
|
||||
| [4.0.3](#403-2021-01-05) | January 2021 |
|
||||
| [4.0.2](#402-2020-11-25) | November 2020 |
|
||||
| [4.0.1](#401-2020-11-05) | November 2020 |
|
||||
| [3.3.1](#331-2020-09-30) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | September 2020 |
|
||||
| [**4.0.0**](#400-2020-09-28) | September 2020 |
|
||||
| [3.4.1](#341-2020-05-13) | May 2020 |
|
||||
| [3.4.0](#340-2019-09-20) | September 2019 |
|
||||
| [3.3.0](#330-2018-11-07) | November 2018 |
|
||||
| Version | Release date |
|
||||
|-----------------------------------------------------------------------------------------------------------------------|----------------|
|
||||
| [3.4.4](#344-2026-03-17) (from the [3.4.x](https://github.com/socketio/socket.io/tree/socket.io-parser/3.4.x) branch) | March 2026 |
|
||||
| [3.3.5](#335-2026-03-17) (from the [3.3.x](https://github.com/socketio/socket.io/tree/socket.io-parser/3.3.x) branch) | March 2026 |
|
||||
| [4.2.6](#426-2026-03-17) | March 2026 |
|
||||
| [4.2.5](#425-2025-12-23) | December 2025 |
|
||||
| [3.3.4](#334-2024-07-22) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | July 2024 |
|
||||
| [4.2.4](#424-2023-05-31) | May 2023 |
|
||||
| [3.4.3](#343-2023-05-22) (from the [3.4.x](https://github.com/socketio/socket.io-parser/tree/3.4.x) branch) | May 2023 |
|
||||
| [4.2.3](#423-2023-05-22) | May 2023 |
|
||||
| [4.2.2](#422-2023-01-19) | January 2023 |
|
||||
| [3.3.3](#333-2022-11-09) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | November 2022 |
|
||||
| [3.4.2](#342-2022-11-09) (from the [3.4.x](https://github.com/socketio/socket.io-parser/tree/3.4.x) branch) | November 2022 |
|
||||
| [4.0.5](#405-2022-06-27) (from the [4.0.x](https://github.com/socketio/socket.io-parser/tree/4.0.x) branch) | June 2022 |
|
||||
| [4.2.1](#421-2022-06-27) | June 2022 |
|
||||
| [4.2.0](#420-2022-04-17) | April 2022 |
|
||||
| [4.1.2](#412-2022-02-17) | February 2022 |
|
||||
| [3.3.3](#333-2022-11-09) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | November 2022 |
|
||||
| [3.4.2](#342-2022-11-09) (from the [3.4.x](https://github.com/socketio/socket.io-parser/tree/3.4.x) branch) | November 2022 |
|
||||
| [4.0.5](#405-2022-06-27) (from the [4.0.x](https://github.com/socketio/socket.io-parser/tree/4.0.x) branch) | June 2022 |
|
||||
| [4.2.1](#421-2022-06-27) | June 2022 |
|
||||
| [4.2.0](#420-2022-04-17) | April 2022 |
|
||||
| [4.1.2](#412-2022-02-17) | February 2022 |
|
||||
| [4.1.1](#411-2021-10-14) | October 2021 |
|
||||
| [4.1.0](#410-2021-10-11) | October 2021 |
|
||||
| [4.0.4](#404-2021-01-15) | January 2021 |
|
||||
| [3.3.2](#332-2021-01-09) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | January 2021 |
|
||||
| [4.0.3](#403-2021-01-05) | January 2021 |
|
||||
| [4.0.2](#402-2020-11-25) | November 2020 |
|
||||
| [4.0.1](#401-2020-11-05) | November 2020 |
|
||||
| [3.3.1](#331-2020-09-30) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | September 2020 |
|
||||
| [**4.0.0**](#400-2020-09-28) | September 2020 |
|
||||
| [3.4.1](#341-2020-05-13) | May 2020 |
|
||||
| [3.4.0](#340-2019-09-20) | September 2019 |
|
||||
| [3.3.0](#330-2018-11-07) | November 2018 |
|
||||
|
||||
## [3.4.4](https://github.com/socketio/socket.io-parser/compare/3.4.3...3.4.4) (2026-03-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add a limit to the number of binary attachments ([719f9eb](https://github.com/socketio/socket.io/commit/719f9ebab0772ffb882bd614b387e585c1aa75d4))
|
||||
|
||||
|
||||
|
||||
## [3.3.5](https://github.com/socketio/socket.io-parser/compare/3.3.4...3.3.5) (2026-03-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add a limit to the number of binary attachments ([9d39f1f](https://github.com/socketio/socket.io/commit/9d39f1f080510f036782f2177fac701cc041faaf))
|
||||
|
||||
|
||||
|
||||
## [4.2.6](https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.5...socket.io-parser@4.2.6) (2026-03-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **parser:** add a limit to the number of binary attachments ([3fff7ca](https://github.com/socketio/socket.io/commit/3fff7cafa98f1ba5840475b6917c651fe841a943))
|
||||
|
||||
|
||||
|
||||
## [4.2.5](https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.4...socket.io-parser@4.2.5) (2025-12-23)
|
||||
|
||||
@@ -135,6 +135,20 @@ interface DecoderReservedEvents {
|
||||
decoded: (packet: Packet) => void;
|
||||
}
|
||||
|
||||
type JSONReviver = (this: any, key: string, value: any) => any;
|
||||
|
||||
export interface DecoderOptions {
|
||||
/**
|
||||
* Custom reviver to pass down to JSON.parse()
|
||||
*/
|
||||
reviver?: JSONReviver;
|
||||
/**
|
||||
* Maximum number of binary attachments per packet
|
||||
* @default 10
|
||||
*/
|
||||
maxAttachments?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A socket.io Decoder instance
|
||||
*
|
||||
@@ -142,14 +156,20 @@ interface DecoderReservedEvents {
|
||||
*/
|
||||
export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
|
||||
private reconstructor: BinaryReconstructor;
|
||||
private opts: Required<DecoderOptions>;
|
||||
|
||||
/**
|
||||
* Decoder constructor
|
||||
*
|
||||
* @param {function} reviver - custom reviver to pass down to JSON.stringify
|
||||
*/
|
||||
constructor(private reviver?: (this: any, key: string, value: any) => any) {
|
||||
constructor(opts?: DecoderOptions | JSONReviver) {
|
||||
super();
|
||||
this.opts = Object.assign(
|
||||
{
|
||||
reviver: undefined,
|
||||
maxAttachments: 10,
|
||||
},
|
||||
typeof opts === "function" ? { reviver: opts } : opts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +244,13 @@ export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
|
||||
if (buf != Number(buf) || str.charAt(i) !== "-") {
|
||||
throw new Error("Illegal attachments");
|
||||
}
|
||||
p.attachments = Number(buf);
|
||||
const n = Number(buf);
|
||||
if (!isInteger(n) || n < 0) {
|
||||
throw new Error("Illegal attachments");
|
||||
} else if (n > this.opts.maxAttachments) {
|
||||
throw new Error("too many attachments");
|
||||
}
|
||||
p.attachments = n;
|
||||
}
|
||||
|
||||
// look up namespace (if any)
|
||||
@@ -271,7 +297,7 @@ export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
|
||||
|
||||
private tryParse(str) {
|
||||
try {
|
||||
return JSON.parse(str, this.reviver);
|
||||
return JSON.parse(str, this.opts.reviver);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io-parser",
|
||||
"version": "4.2.5",
|
||||
"version": "4.2.6",
|
||||
"description": "socket.io protocol parser",
|
||||
"homepage": "https://github.com/socketio/socket.io/tree/main/packages/socket.io-client#readme",
|
||||
"repository": {
|
||||
|
||||
@@ -107,6 +107,56 @@ describe("socket.io-parser", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("throws an error when receiving too many attachments", () => {
|
||||
const decoder = new Decoder({ maxAttachments: 2 });
|
||||
|
||||
expect(() => {
|
||||
decoder.add(
|
||||
'53-["hello",{"_placeholder":true,"num":0},{"_placeholder":true,"num":1},{"_placeholder":true,"num":2}]',
|
||||
);
|
||||
}).to.throwException(/^too many attachments$/);
|
||||
});
|
||||
|
||||
it("decodes with a custom reviver", () => {
|
||||
const decoder = new Decoder((key, value) => {
|
||||
if (key === "a") {
|
||||
return value.toUpperCase();
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
decoder.on("decoded", (packet) => {
|
||||
expect(packet.data).to.eql(["b", { a: "VAL" }]);
|
||||
resolve();
|
||||
});
|
||||
|
||||
decoder.add('2["b",{"a":"val"}]');
|
||||
});
|
||||
});
|
||||
|
||||
it("decodes with a custom reviver (options object)", () => {
|
||||
const decoder = new Decoder({
|
||||
reviver: (key, value) => {
|
||||
if (key === "a") {
|
||||
return value.toUpperCase();
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
decoder.on("decoded", (packet) => {
|
||||
expect(packet.data).to.eql(["b", { a: "VAL" }]);
|
||||
resolve();
|
||||
});
|
||||
|
||||
decoder.add('2["b",{"a":"val"}]');
|
||||
});
|
||||
});
|
||||
|
||||
it("throw an error upon parsing error", () => {
|
||||
const isInvalidPayload = (str) =>
|
||||
expect(() => new Decoder().add(str)).to.throwException(
|
||||
@@ -125,6 +175,16 @@ describe("socket.io-parser", () => {
|
||||
isInvalidPayload('2["connect"]');
|
||||
isInvalidPayload('2["disconnect","123"]');
|
||||
|
||||
const isInvalidAttachmentCount = (str) =>
|
||||
expect(() => new Decoder().add(str)).to.throwException(
|
||||
/^Illegal attachments$/,
|
||||
);
|
||||
|
||||
isInvalidAttachmentCount("5");
|
||||
isInvalidAttachmentCount("51");
|
||||
isInvalidAttachmentCount("5a-");
|
||||
isInvalidAttachmentCount("51.23-");
|
||||
|
||||
expect(() => new Decoder().add("999")).to.throwException(
|
||||
/^unknown packet type 9$/,
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
| Version | Release date |
|
||||
|--------------------------------------------------------------------------------------------------|----------------|
|
||||
| [4.8.3](#483-2025-12-23) | December 2025 |
|
||||
| [4.8.2](#482-2025-12-22) | December 2025 |
|
||||
| [4.8.1](#481-2024-10-25) | October 2024 |
|
||||
| [4.8.0](#480-2024-09-21) | September 2024 |
|
||||
@@ -50,6 +51,21 @@
|
||||
| [2.1.0](#210-2018-03-29) | March 2018 |
|
||||
|
||||
|
||||
## [4.8.3](https://github.com/socketio/socket.io/compare/socket.io@4.8.2...socket.io@4.8.3) (2025-12-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* do not throw when calling io.close() on a stopped server ([9581f9b](https://github.com/socketio/socket.io/commit/9581f9bcfd0c0fa8cb16eae1604c6a727af21efa))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [`engine.io@~6.6.0`](https://github.com/socketio/engine.io/releases/tag/6.6.0) (no change)
|
||||
- [`ws@~8.18.3`](https://github.com/websockets/ws/releases/tag/8.18.3) (no change)
|
||||
|
||||
|
||||
|
||||
## [4.8.2](https://github.com/socketio/socket.io/compare/socket.io@4.8.1...socket.io@4.8.2) (2025-12-22)
|
||||
|
||||
The `url.parse()` function is now deprecated and has been replaced by `new URL()` (see [8af7019](https://github.com/socketio/socket.io/commit/8af70195bb8c5bc3efe9685997ab6373fb8b1ca9)).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Socket.IO v4.8.2
|
||||
* Socket.IO v4.8.3
|
||||
* (c) 2014-2025 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Socket.IO v4.8.2
|
||||
* Socket.IO v4.8.3
|
||||
* (c) 2014-2025 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Socket.IO v4.8.2
|
||||
* Socket.IO v4.8.3
|
||||
* (c) 2014-2025 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Socket.IO v4.8.2
|
||||
* Socket.IO v4.8.3
|
||||
* (c) 2014-2025 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,9 @@ import type {
|
||||
FirstNonErrorArg,
|
||||
EventNamesWithError,
|
||||
} from "./typed-events";
|
||||
import debugModule from "debug";
|
||||
|
||||
const debug = debugModule("socket.io:broadcast-operator");
|
||||
|
||||
export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
|
||||
implements TypedEventBroadcaster<EmitEvents>
|
||||
@@ -235,6 +238,17 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
timedOut = true;
|
||||
|
||||
debug("operation has timed out");
|
||||
// @ts-expect-error
|
||||
const packetId = packet.id;
|
||||
|
||||
if (packetId !== undefined) {
|
||||
this.adapter.nsp.sockets.forEach((socket) => {
|
||||
socket.acks.delete(packetId);
|
||||
});
|
||||
}
|
||||
|
||||
ack.apply(this, [
|
||||
new Error("operation has timed out"),
|
||||
this.flags.expectSingleResponse ? null : responses,
|
||||
@@ -246,6 +260,13 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
|
||||
let expectedClientCount = 0;
|
||||
|
||||
const checkCompleteness = () => {
|
||||
debug(
|
||||
"responses: servers: %d / %d ; clients: %d / %d",
|
||||
actualServerCount,
|
||||
expectedServerCount,
|
||||
responses.length,
|
||||
expectedClientCount,
|
||||
);
|
||||
if (
|
||||
!timedOut &&
|
||||
expectedServerCount === actualServerCount &&
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import http from "http";
|
||||
import { createServer } from "http"; // 'node:' prefix was added in Node.js 16
|
||||
import type {
|
||||
Server as HTTPServer,
|
||||
IncomingMessage,
|
||||
ServerResponse,
|
||||
} from "http";
|
||||
import type { Server as HTTPSServer } from "https";
|
||||
import type { Http2SecureServer, Http2Server } from "http2";
|
||||
import { createReadStream } from "fs";
|
||||
@@ -58,7 +63,7 @@ type ParentNspNameMatchFn = (
|
||||
type AdapterConstructor = typeof Adapter | ((nsp: Namespace) => Adapter);
|
||||
|
||||
type TServerInstance =
|
||||
| http.Server
|
||||
| HTTPServer
|
||||
| HTTPSServer
|
||||
| Http2SecureServer
|
||||
| Http2Server;
|
||||
@@ -276,8 +281,8 @@ export class Server<
|
||||
*/
|
||||
_connectTimeout: number;
|
||||
private _corsMiddleware: (
|
||||
req: http.IncomingMessage,
|
||||
res: http.ServerResponse,
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
next: () => void,
|
||||
) => void;
|
||||
|
||||
@@ -301,7 +306,7 @@ export class Server<
|
||||
if (
|
||||
"object" === typeof srv &&
|
||||
srv instanceof Object &&
|
||||
!(srv as Partial<http.Server>).listen
|
||||
!(srv as Partial<HTTPServer>).listen
|
||||
) {
|
||||
opts = srv as Partial<ServerOptions>;
|
||||
srv = undefined;
|
||||
@@ -493,7 +498,7 @@ export class Server<
|
||||
if ("number" == typeof srv) {
|
||||
debug("creating http server and binding to %d", srv);
|
||||
const port = srv;
|
||||
srv = http.createServer((req, res) => {
|
||||
srv = createServer((_req, res) => {
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
});
|
||||
@@ -591,7 +596,7 @@ export class Server<
|
||||
): void {
|
||||
// initialize engine
|
||||
debug("creating engine.io instance with opts %j", opts);
|
||||
this.eio = attach(srv as http.Server, opts);
|
||||
this.eio = attach(srv as HTTPServer, opts);
|
||||
|
||||
// attach static file serving
|
||||
if (this._serveClient) this.attachServe(srv);
|
||||
@@ -638,7 +643,7 @@ export class Server<
|
||||
* @param res
|
||||
* @private
|
||||
*/
|
||||
private serve(req: http.IncomingMessage, res: http.ServerResponse): void {
|
||||
private serve(req: IncomingMessage, res: ServerResponse): void {
|
||||
const filename = req.url!.replace(this._path, "").replace(/\?.*$/, "");
|
||||
const isMap = dotMapRegex.test(filename);
|
||||
const type = isMap ? "map" : "source";
|
||||
@@ -678,8 +683,8 @@ export class Server<
|
||||
*/
|
||||
private static sendFile(
|
||||
filename: string,
|
||||
req: http.IncomingMessage,
|
||||
res: http.ServerResponse,
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
): void {
|
||||
const readStream = createReadStream(
|
||||
path.join(__dirname, "../client-dist/", filename),
|
||||
@@ -831,14 +836,13 @@ export class Server<
|
||||
restoreAdapter();
|
||||
|
||||
if (this.httpServer) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
this.httpServer.close((err) => {
|
||||
fn && fn(err);
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
debug("server was not running");
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -22,8 +22,6 @@ export type EventNames<Map extends EventsMap> = keyof Map & (string | symbol);
|
||||
|
||||
/**
|
||||
* Returns a union type containing all the keys of an event map that have an acknowledgement callback.
|
||||
*
|
||||
* That also have *some* data coming in.
|
||||
*/
|
||||
export type EventNamesWithAck<
|
||||
Map extends EventsMap,
|
||||
@@ -32,11 +30,11 @@ export type EventNamesWithAck<
|
||||
Last<Parameters<Map[K]>> | Map[K],
|
||||
K,
|
||||
K extends (
|
||||
Last<Parameters<Map[K]>> extends (...args: any[]) => any
|
||||
? FirstNonErrorArg<Last<Parameters<Map[K]>>> extends void
|
||||
? never
|
||||
: K
|
||||
: never
|
||||
Parameters<Map[K]> extends never[]
|
||||
? never
|
||||
: Last<Parameters<Map[K]>> extends (...args: any[]) => any
|
||||
? K
|
||||
: never
|
||||
)
|
||||
? K
|
||||
: never
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io",
|
||||
"version": "4.8.2",
|
||||
"version": "4.8.3",
|
||||
"description": "node.js realtime framework server",
|
||||
"keywords": [
|
||||
"realtime",
|
||||
|
||||
@@ -70,6 +70,27 @@ describe("close", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should not throw when the underlying HTTP server is not running (callback)", (done) => {
|
||||
const httpServer = createServer();
|
||||
const io = new Server(httpServer);
|
||||
|
||||
io.close((err) => {
|
||||
expect((err as Error & { code: string }).code).to.eql(
|
||||
"ERR_SERVER_NOT_RUNNING",
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not throw when the underlying HTTP server is not running (Promise)", (done) => {
|
||||
const httpServer = createServer();
|
||||
const io = new Server(httpServer);
|
||||
|
||||
io.close()
|
||||
.then(() => done())
|
||||
.catch((e) => done(e));
|
||||
});
|
||||
|
||||
describe("graceful close", () => {
|
||||
function fixture(filename) {
|
||||
return (
|
||||
|
||||
@@ -534,6 +534,11 @@ describe("messaging many", () => {
|
||||
// @ts-ignore
|
||||
expect(err.responses).to.contain(1, 2);
|
||||
|
||||
for (const [, serverSocket] of io.of("/").sockets) {
|
||||
// @ts-ignore accessing private acks map to verify cleanup
|
||||
expect(serverSocket.acks.size).to.be(0);
|
||||
}
|
||||
|
||||
success(done, io, socket1, socket2, socket3);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -265,15 +265,11 @@ describe("server", () => {
|
||||
interface ServerToClientEventsWithMultipleWithAck {
|
||||
ackFromServer: (a: boolean, b: string) => Promise<boolean[]>;
|
||||
ackFromServerSingleArg: (a: boolean, b: string) => Promise<string[]>;
|
||||
// This should technically be `undefined[]`, but this doesn't work currently *only* with emitWithAck
|
||||
// you can use an empty callback with emit, but not emitWithAck
|
||||
onlyCallback: () => Promise<undefined>;
|
||||
}
|
||||
interface ServerToClientEventsWithAck {
|
||||
ackFromServer: (a: boolean, b: string) => Promise<boolean>;
|
||||
ackFromServerSingleArg: (a: boolean, b: string) => Promise<string>;
|
||||
// This doesn't work currently *only* with emitWithAck
|
||||
// you can use an empty callback with emit, but not emitWithAck
|
||||
onlyCallback: () => Promise<undefined>;
|
||||
}
|
||||
describe("Emitting Types", () => {
|
||||
@@ -420,8 +416,9 @@ describe("server", () => {
|
||||
sio.timeout(0).emitWithAck("noArgs");
|
||||
// @ts-expect-error - "helloFromServer" doesn't have a callback and is thus excluded
|
||||
sio.timeout(0).emitWithAck("helloFromServer");
|
||||
// @ts-expect-error - "onlyCallback" doesn't have a callback and is thus excluded
|
||||
sio.timeout(0).emitWithAck("onlyCallback");
|
||||
expectType<
|
||||
ToEmitWithAck<ServerToClientEventsWithMultipleWithAck, "onlyCallback">
|
||||
>(sio.timeout(0).emitWithAck<"onlyCallback">);
|
||||
expectType<
|
||||
ToEmitWithAck<
|
||||
ServerToClientEventsWithMultipleWithAck,
|
||||
@@ -447,7 +444,7 @@ describe("server", () => {
|
||||
nio.emit<"noArgs">,
|
||||
);
|
||||
expectType<ToEmit<ServerToClientEventsNoAck, "helloFromServer">>(
|
||||
// These errors will dissapear once the TS version is updated from 4.7.4
|
||||
// These errors will disappear once the TS version is updated from 4.7.4
|
||||
// the TSD instance is using a newer version of TS than the workspace version
|
||||
// to enable the ability to compare against `any`
|
||||
sio.emit<"helloFromServer">,
|
||||
@@ -496,10 +493,12 @@ describe("server", () => {
|
||||
s.emitWithAck("noArgs");
|
||||
// @ts-expect-error - "helloFromServer" doesn't have a callback and is thus excluded
|
||||
s.emitWithAck("helloFromServer");
|
||||
// @ts-expect-error - "onlyCallback" doesn't have a callback and is thus excluded
|
||||
s.emitWithAck("onlyCallback");
|
||||
// @ts-expect-error - "onlyCallback" doesn't have a callback and is thus excluded
|
||||
s.timeout(0).emitWithAck("onlyCallback");
|
||||
expectType<
|
||||
ToEmitWithAck<ServerToClientEventsWithAck, "onlyCallback">
|
||||
>(s.emitWithAck<"onlyCallback">);
|
||||
expectType<
|
||||
ToEmitWithAck<ServerToClientEventsWithAck, "onlyCallback">
|
||||
>(s.timeout(0).emitWithAck<"onlyCallback">);
|
||||
expectType<
|
||||
ToEmitWithAck<ServerToClientEventsWithAck, "ackFromServerSingleArg">
|
||||
>(s.emitWithAck<"ackFromServerSingleArg">);
|
||||
|
||||
Reference in New Issue
Block a user