mirror of
https://github.com/socketio/socket.io.git
synced 2026-04-30 03:00:39 -04:00
Compare commits
88 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 | ||
|
|
ee9aac3134 | ||
|
|
968277cef8 | ||
|
|
2bf16bd214 | ||
|
|
ad616070b8 | ||
|
|
dd71792455 | ||
|
|
bb0b480d2a | ||
|
|
161be91975 | ||
|
|
fd9d4cab5e | ||
|
|
0a99ac44a2 | ||
|
|
4338f47336 | ||
|
|
9199156758 | ||
|
|
594841617d | ||
|
|
84e7253e57 | ||
|
|
30ec4a136a | ||
|
|
e08293bc37 | ||
|
|
b837949479 | ||
|
|
118ef41b94 | ||
|
|
d19928e8d8 | ||
|
|
cdae01983a | ||
|
|
39bb72039d | ||
|
|
98741e15e9 | ||
|
|
8af70195bb | ||
|
|
d88f3f4578 | ||
|
|
f5ee981ee8 | ||
|
|
76e3a72bba | ||
|
|
a7b1938d06 | ||
|
|
54743633ff | ||
|
|
7617707ed8 | ||
|
|
599001d213 | ||
|
|
1c3e4711c1 | ||
|
|
693080cac7 | ||
|
|
5080c73e1e | ||
|
|
47ff1cd04c | ||
|
|
0ae76360f9 | ||
|
|
27fd420e75 | ||
|
|
0c431243e2 | ||
|
|
4fc25d80ec | ||
|
|
1dd729b1a1 | ||
|
|
6877512f57 | ||
|
|
cf6816afcf | ||
|
|
625fd66d73 | ||
|
|
f3e1f5ebdf | ||
|
|
e97549259e | ||
|
|
1da9cddeab | ||
|
|
6f9b198bc8 | ||
|
|
56a53bceb9 | ||
|
|
683720a67d | ||
|
|
a529eb08d6 | ||
|
|
cddb78e5fa | ||
|
|
15fd56e78d | ||
|
|
e86ef45f87 | ||
|
|
fe840e2eb3 | ||
|
|
a5a1c29082 | ||
|
|
66b4079953 | ||
|
|
be0a0e3217 | ||
|
|
43f9ee8d23 | ||
|
|
055b7840d8 | ||
|
|
6397c1bdfd | ||
|
|
ff370cfc46 |
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
|
||||
|
||||
15
.github/workflows/ci.yml
vendored
15
.github/workflows/ci.yml
vendored
@@ -20,9 +20,9 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version:
|
||||
- 18
|
||||
- 20
|
||||
- 22.17.1 # experimental TS type striping in 22.18.0 breaks the build
|
||||
- 22
|
||||
- 24
|
||||
|
||||
services:
|
||||
redis:
|
||||
@@ -49,12 +49,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
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' }}
|
||||
|
||||
15
.github/workflows/publish.yml
vendored
15
.github/workflows/publish.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# reference: https://docs.npmjs.com/generating-provenance-statements
|
||||
# reference: https://docs.npmjs.com/trusted-publishers#for-github-actions
|
||||
|
||||
name: Publish
|
||||
|
||||
@@ -17,13 +17,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
- name: Use Node.js 24
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 24
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
@@ -32,6 +33,4 @@ jobs:
|
||||
run: npm run compile --workspaces --if-present
|
||||
|
||||
- name: Publish package
|
||||
run: npm publish --workspace=${GITHUB_REF_NAME%@*} --provenance --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: npm publish --workspace=${GITHUB_REF_NAME%@*} --access public
|
||||
|
||||
25
CHANGELOG.md
25
CHANGELOG.md
@@ -2,14 +2,17 @@
|
||||
|
||||
Here are the detailed changelogs for each package in this monorepo:
|
||||
|
||||
| Package | Changelog |
|
||||
|--------------------------------|---------------------------------------------------------|
|
||||
| `engine.io` | [link](packages/engine.io/CHANGELOG.md) |
|
||||
| `engine.io-client` | [link](packages/engine.io-client/CHANGELOG.md) |
|
||||
| `engine.io-parser` | [link](packages/engine.io-parser/CHANGELOG.md) |
|
||||
| `socket.io` | [link](packages/socket.io/CHANGELOG.md) |
|
||||
| `socket.io-adapter` | [link](packages/socket.io-adapter/CHANGELOG.md) |
|
||||
| `socket.io-client` | [link](packages/socket.io-client/CHANGELOG.md) |
|
||||
| `@socket.io/cluster-engine` | [link](packages/socket.io-cluster-engine/CHANGELOG.md) |
|
||||
| `@socket.io/component-emitter` | [link](packages/socket.io-component-emitter/History.md) |
|
||||
| `socket.io-parser` | [link](packages/socket.io-parser/CHANGELOG.md) |
|
||||
| Package | Changelog |
|
||||
|------------------------------------|----------------------------------------------------------------|
|
||||
| `engine.io` | [link](packages/engine.io/CHANGELOG.md) |
|
||||
| `engine.io-client` | [link](packages/engine.io-client/CHANGELOG.md) |
|
||||
| `engine.io-parser` | [link](packages/engine.io-parser/CHANGELOG.md) |
|
||||
| `socket.io` | [link](packages/socket.io/CHANGELOG.md) |
|
||||
| `socket.io-adapter` | [link](packages/socket.io-adapter/CHANGELOG.md) |
|
||||
| `socket.io-client` | [link](packages/socket.io-client/CHANGELOG.md) |
|
||||
| `@socket.io/cluster-adapter` | [link](packages/socket.io-cluster-adapter/CHANGELOG.md) |
|
||||
| `@socket.io/cluster-engine` | [link](packages/socket.io-cluster-engine/CHANGELOG.md) |
|
||||
| `@socket.io/component-emitter` | [link](packages/socket.io-component-emitter/History.md) |
|
||||
| `socket.io-parser` | [link](packages/socket.io-parser/CHANGELOG.md) |
|
||||
| `@socket.io/postgres-emitter` | [link](packages/socket.io-postgres-emitter/CHANGELOG.md) |
|
||||
| `@socket.io/redis-streams-emitter` | [link](/packages/socket.io-redis-streams-emitter/CHANGELOG.md) |
|
||||
|
||||
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
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"prettier": "^2.8.4",
|
||||
"rollup": "^3.20.2",
|
||||
"socket.io": "^4.6.1",
|
||||
"ws": "^8.13.0"
|
||||
"ws": "^8.18.3"
|
||||
},
|
||||
"scripts": {
|
||||
"bundle": "rollup -c",
|
||||
|
||||
4589
package-lock.json
generated
4589
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -8,15 +8,17 @@
|
||||
"packages/socket.io-cluster-engine",
|
||||
"packages/engine.io-client",
|
||||
"packages/socket.io-adapter",
|
||||
"packages/socket.io-cluster-adapter",
|
||||
"packages/socket.io-parser",
|
||||
"packages/socket.io-client",
|
||||
"packages/socket.io",
|
||||
"packages/socket.io-postgres-emitter"
|
||||
"packages/socket.io-postgres-emitter",
|
||||
"packages/socket.io-redis-streams-emitter"
|
||||
],
|
||||
"overrides": {
|
||||
"@types/estree": "0.0.52",
|
||||
"@types/lodash": "4.14.189",
|
||||
"ws": "8.17.1"
|
||||
"ws": "8.18.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.7",
|
||||
@@ -31,6 +33,7 @@
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@sinonjs/fake-timers": "^11.2.2",
|
||||
"@socket.io/postgres-adapter": "^0.1.0",
|
||||
"@socket.io/redis-streams-adapter": "~0.2.2",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/expect.js": "^0.3.32",
|
||||
"@types/mocha": "^10.0.7",
|
||||
@@ -73,8 +76,9 @@
|
||||
"ts-loader": "^9.5.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsd": "^0.31.1",
|
||||
"tsx": "~4.20.6",
|
||||
"typescript": "^5.5.3",
|
||||
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.48.0",
|
||||
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.56.0",
|
||||
"wdio-geckodriver-service": "^5.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# History
|
||||
# Changelog
|
||||
|
||||
| Version | Release date | Bundle size (UMD min+gzip) |
|
||||
|-------------------------------------------------------------------------------------------------------------|----------------|----------------------------|
|
||||
| [6.6.4](#664-2025-12-23) | December 2025 | `8.7 KB` |
|
||||
| [6.6.3](#663-2025-01-23) | January 2025 | `8.7 KB` |
|
||||
| [6.6.2](#662-2024-10-23) | October 2024 | `8.7 KB` |
|
||||
| [6.6.1](#661-2024-09-21) | September 2024 | `8.7 KB` |
|
||||
@@ -39,7 +40,24 @@
|
||||
| [4.1.1](#411-2021-02-02) | February 2021 | `9.1 KB` |
|
||||
| [4.1.0](#410-2021-01-14) | January 2021 | `9.1 KB` |
|
||||
|
||||
# Release notes
|
||||
|
||||
## [6.6.4](https://github.com/socketio/socket.io/compare/engine.io-client@6.6.3...engine.io-client@6.6.4) (2025-12-23)
|
||||
|
||||
This release contains a bump of:
|
||||
|
||||
- `ws` from `~8.17.1` to `~8.18.3`
|
||||
- `debug` from `~4.3.1` to `~4.4.1`
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* properly handle port option ([#5241](https://github.com/socketio/socket.io/issues/5241)) ([1da9cdd](https://github.com/socketio/socket.io/commit/1da9cddeab0bf5ce41890d156d73af8194cef656))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [`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))
|
||||
|
||||
|
||||
|
||||
## [6.6.3](https://github.com/socketio/socket.io/compare/engine.io-client@6.6.2...engine.io-client@6.6.3) (2025-01-23)
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
172
packages/engine.io-client/dist/engine.io.js
vendored
172
packages/engine.io-client/dist/engine.io.js
vendored
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Engine.IO v6.6.3
|
||||
* Engine.IO v6.6.4
|
||||
* (c) 2014-2025 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
@@ -35,6 +35,54 @@
|
||||
writable: !1
|
||||
}), e;
|
||||
}
|
||||
function _createForOfIteratorHelper(r, e) {
|
||||
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
|
||||
if (!t) {
|
||||
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {
|
||||
t && (r = t);
|
||||
var n = 0,
|
||||
F = function () {};
|
||||
return {
|
||||
s: F,
|
||||
n: function () {
|
||||
return n >= r.length ? {
|
||||
done: !0
|
||||
} : {
|
||||
done: !1,
|
||||
value: r[n++]
|
||||
};
|
||||
},
|
||||
e: function (r) {
|
||||
throw r;
|
||||
},
|
||||
f: F
|
||||
};
|
||||
}
|
||||
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
||||
}
|
||||
var o,
|
||||
a = !0,
|
||||
u = !1;
|
||||
return {
|
||||
s: function () {
|
||||
t = t.call(r);
|
||||
},
|
||||
n: function () {
|
||||
var r = t.next();
|
||||
return a = r.done, r;
|
||||
},
|
||||
e: function (r) {
|
||||
u = !0, o = r;
|
||||
},
|
||||
f: function () {
|
||||
try {
|
||||
a || null == t.return || t.return();
|
||||
} finally {
|
||||
if (u) throw o;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
function _extends() {
|
||||
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
||||
for (var e = 1; e < arguments.length; e++) {
|
||||
@@ -1049,21 +1097,65 @@
|
||||
createDebug.namespaces = namespaces;
|
||||
createDebug.names = [];
|
||||
createDebug.skips = [];
|
||||
var i;
|
||||
var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
|
||||
var len = split.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!split[i]) {
|
||||
// ignore empty strings
|
||||
continue;
|
||||
var split = (typeof namespaces === 'string' ? namespaces : '').trim().replace(/\s+/g, ',').split(',').filter(Boolean);
|
||||
var _iterator = _createForOfIteratorHelper(split),
|
||||
_step;
|
||||
try {
|
||||
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
||||
var ns = _step.value;
|
||||
if (ns[0] === '-') {
|
||||
createDebug.skips.push(ns.slice(1));
|
||||
} else {
|
||||
createDebug.names.push(ns);
|
||||
}
|
||||
}
|
||||
namespaces = split[i].replace(/\*/g, '.*?');
|
||||
if (namespaces[0] === '-') {
|
||||
createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$'));
|
||||
} catch (err) {
|
||||
_iterator.e(err);
|
||||
} finally {
|
||||
_iterator.f();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given string matches a namespace template, honoring
|
||||
* asterisks as wildcards.
|
||||
*
|
||||
* @param {String} search
|
||||
* @param {String} template
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function matchesTemplate(search, template) {
|
||||
var searchIndex = 0;
|
||||
var templateIndex = 0;
|
||||
var starIndex = -1;
|
||||
var matchIndex = 0;
|
||||
while (searchIndex < search.length) {
|
||||
if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) {
|
||||
// Match character or proceed with wildcard
|
||||
if (template[templateIndex] === '*') {
|
||||
starIndex = templateIndex;
|
||||
matchIndex = searchIndex;
|
||||
templateIndex++; // Skip the '*'
|
||||
} else {
|
||||
searchIndex++;
|
||||
templateIndex++;
|
||||
}
|
||||
} else if (starIndex !== -1) {
|
||||
// eslint-disable-line no-negated-condition
|
||||
// Backtrack to the last '*' and try to match more characters
|
||||
templateIndex = starIndex + 1;
|
||||
matchIndex++;
|
||||
searchIndex = matchIndex;
|
||||
} else {
|
||||
createDebug.names.push(new RegExp('^' + namespaces + '$'));
|
||||
return false; // No match
|
||||
}
|
||||
}
|
||||
|
||||
// Handle trailing '*' in template
|
||||
while (templateIndex < template.length && template[templateIndex] === '*') {
|
||||
templateIndex++;
|
||||
}
|
||||
return templateIndex === template.length;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1073,7 +1165,7 @@
|
||||
* @api public
|
||||
*/
|
||||
function disable() {
|
||||
var namespaces = [].concat(_toConsumableArray(createDebug.names.map(toNamespace)), _toConsumableArray(createDebug.skips.map(toNamespace).map(function (namespace) {
|
||||
var namespaces = [].concat(_toConsumableArray(createDebug.names), _toConsumableArray(createDebug.skips.map(function (namespace) {
|
||||
return '-' + namespace;
|
||||
}))).join(',');
|
||||
createDebug.enable('');
|
||||
@@ -1088,35 +1180,37 @@
|
||||
* @api public
|
||||
*/
|
||||
function enabled(name) {
|
||||
if (name[name.length - 1] === '*') {
|
||||
return true;
|
||||
}
|
||||
var i;
|
||||
var len;
|
||||
for (i = 0, len = createDebug.skips.length; i < len; i++) {
|
||||
if (createDebug.skips[i].test(name)) {
|
||||
return false;
|
||||
var _iterator2 = _createForOfIteratorHelper(createDebug.skips),
|
||||
_step2;
|
||||
try {
|
||||
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
||||
var skip = _step2.value;
|
||||
if (matchesTemplate(name, skip)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_iterator2.e(err);
|
||||
} finally {
|
||||
_iterator2.f();
|
||||
}
|
||||
for (i = 0, len = createDebug.names.length; i < len; i++) {
|
||||
if (createDebug.names[i].test(name)) {
|
||||
return true;
|
||||
var _iterator3 = _createForOfIteratorHelper(createDebug.names),
|
||||
_step3;
|
||||
try {
|
||||
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
||||
var ns = _step3.value;
|
||||
if (matchesTemplate(name, ns)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_iterator3.e(err);
|
||||
} finally {
|
||||
_iterator3.f();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert regexp to namespace
|
||||
*
|
||||
* @param {RegExp} regxep
|
||||
* @return {String} namespace
|
||||
* @api private
|
||||
*/
|
||||
function toNamespace(regexp) {
|
||||
return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerce `val`.
|
||||
*
|
||||
@@ -1192,15 +1286,17 @@
|
||||
if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
|
||||
return false;
|
||||
}
|
||||
var m;
|
||||
|
||||
// Is webkit? http://stackoverflow.com/a/16459606/376773
|
||||
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
|
||||
// eslint-disable-next-line no-return-assign
|
||||
return typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance ||
|
||||
// Is firebug? http://stackoverflow.com/a/398120/376773
|
||||
typeof window !== 'undefined' && window.console && (window.console.firebug || window.console.exception && window.console.table) ||
|
||||
// Is firefox >= v31?
|
||||
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
|
||||
typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 ||
|
||||
typeof navigator !== 'undefined' && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 ||
|
||||
// Double check webkit in userAgent just in case we are in a worker
|
||||
typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
|
||||
}
|
||||
@@ -1276,7 +1372,7 @@
|
||||
function load() {
|
||||
var r;
|
||||
try {
|
||||
r = exports.storage.getItem('debug');
|
||||
r = exports.storage.getItem('debug') || exports.storage.getItem('DEBUG');
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
@@ -1457,7 +1553,7 @@
|
||||
return hostname.indexOf(":") === -1 ? hostname : "[" + hostname + "]";
|
||||
};
|
||||
_proto._port = function _port() {
|
||||
if (this.opts.port && (this.opts.secure && Number(this.opts.port !== 443) || !this.opts.secure && Number(this.opts.port) !== 80)) {
|
||||
if (this.opts.port && (this.opts.secure && Number(this.opts.port) !== 443 || !this.opts.secure && Number(this.opts.port) !== 80)) {
|
||||
return ":" + this.opts.port;
|
||||
} else {
|
||||
return "";
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -192,7 +192,7 @@ export abstract class Transport extends Emitter<
|
||||
private _port() {
|
||||
if (
|
||||
this.opts.port &&
|
||||
((this.opts.secure && Number(this.opts.port !== 443)) ||
|
||||
((this.opts.secure && Number(this.opts.port) !== 443) ||
|
||||
(!this.opts.secure && Number(this.opts.port) !== 80))
|
||||
) {
|
||||
return ":" + this.opts.port;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "engine.io-client",
|
||||
"description": "Client for the realtime Engine",
|
||||
"license": "MIT",
|
||||
"version": "6.6.3",
|
||||
"version": "6.6.4",
|
||||
"main": "./build/cjs/index.js",
|
||||
"module": "./build/esm/index.js",
|
||||
"exports": {
|
||||
@@ -53,9 +53,9 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"debug": "~4.4.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1",
|
||||
"ws": "~8.18.3",
|
||||
"xmlhttprequest-ssl": "~2.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -116,6 +116,32 @@ describe("Transport", () => {
|
||||
expect(polling.uri()).to.contain("https://localhost/engine.io?sid=test");
|
||||
});
|
||||
|
||||
it("should generate an https uri w/o a port (string)", () => {
|
||||
const polling = new eio.transports.polling({
|
||||
path: "/engine.io",
|
||||
hostname: "localhost",
|
||||
secure: true,
|
||||
query: { sid: "test" },
|
||||
port: "443",
|
||||
timestampRequests: false,
|
||||
});
|
||||
expect(polling.uri()).to.contain("https://localhost/engine.io?sid=test");
|
||||
});
|
||||
|
||||
it("should generate an https uri with a port", () => {
|
||||
const polling = new eio.transports.polling({
|
||||
path: "/engine.io",
|
||||
hostname: "localhost",
|
||||
secure: true,
|
||||
query: { sid: "test" },
|
||||
port: 8443,
|
||||
timestampRequests: false,
|
||||
});
|
||||
expect(polling.uri()).to.contain(
|
||||
"https://localhost:8443/engine.io?sid=test",
|
||||
);
|
||||
});
|
||||
|
||||
it("should generate a timestamped uri", () => {
|
||||
const polling = new eio.transports.polling({
|
||||
path: "/engine.io",
|
||||
|
||||
@@ -53,6 +53,26 @@ async function setup(opts, cb) {
|
||||
cb({ engine, h3Server, certificate });
|
||||
}
|
||||
|
||||
function createHttpServer(port) {
|
||||
const httpServer = createServer();
|
||||
|
||||
let retryCount = 0;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
httpServer.listen(port, () => resolve(httpServer));
|
||||
|
||||
httpServer.on("error", (e) => {
|
||||
if (e.code === "EADDRINUSE" && ++retryCount <= 3) {
|
||||
console.warn("port already in use, retrying...");
|
||||
setTimeout(() => {
|
||||
httpServer.listen(port, () => resolve(httpServer));
|
||||
}, 100);
|
||||
}
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function success(engine, h3server, done) {
|
||||
engine.close();
|
||||
h3server.stopServer();
|
||||
@@ -98,10 +118,9 @@ describe("WebTransport", () => {
|
||||
{
|
||||
transports: ["polling", "webtransport"],
|
||||
},
|
||||
({ engine, h3Server, certificate }) => {
|
||||
const httpServer = createServer();
|
||||
async ({ engine, h3Server, certificate }) => {
|
||||
const httpServer = await createHttpServer(h3Server.port);
|
||||
engine.attach(httpServer);
|
||||
httpServer.listen(h3Server.port);
|
||||
|
||||
const socket = createSocket(h3Server.port, certificate, {
|
||||
transports: ["polling", "webtransport"],
|
||||
@@ -120,10 +139,9 @@ describe("WebTransport", () => {
|
||||
{
|
||||
transports: ["polling", "websocket", "webtransport"],
|
||||
},
|
||||
({ engine, h3Server, certificate }) => {
|
||||
const httpServer = createServer();
|
||||
async ({ engine, h3Server, certificate }) => {
|
||||
const httpServer = await createHttpServer(h3Server.port);
|
||||
engine.attach(httpServer);
|
||||
httpServer.listen(h3Server.port);
|
||||
|
||||
const socket = createSocket(h3Server.port, certificate, {
|
||||
transports: ["polling", "websocket", "webtransport"],
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"scripts": {
|
||||
"compile": "rimraf ./build && tsc && tsc -p tsconfig.esm.json && ./postcompile.sh",
|
||||
"test": "npm run format:check && npm run compile && if test \"$BROWSERS\" = \"1\" ; then npm run test:browser; else npm run test:node; fi",
|
||||
"test:node": "nyc mocha -r ts-node/register test/index.ts",
|
||||
"test:node": "nyc mocha --import=tsx test/index.ts",
|
||||
"test:browser": "zuul test/index.ts --no-coverage",
|
||||
"format:check": "prettier --check 'lib/**/*.ts' 'test/**/*.ts'",
|
||||
"format:fix": "prettier --write 'lib/**/*.ts' 'test/**/*.ts'",
|
||||
|
||||
@@ -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,53 +1,95 @@
|
||||
# History
|
||||
# Changelog
|
||||
|
||||
| Version | Release date |
|
||||
|------------------------------------------------------------------------------------------------------|----------------|
|
||||
| [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)
|
||||
|
||||
The `url.parse()` function is now deprecated and has been replaced by `new URL()` (see [e08293b](https://github.com/socketio/socket.io/commit/e08293bc3735de5b824b347383e86e0b8ab9fbd5)).
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [`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))
|
||||
|
||||
|
||||
# Release notes
|
||||
|
||||
## [6.6.4](https://github.com/socketio/socket.io/compare/engine.io@6.6.3...engine.io@6.6.4) (2025-01-28)
|
||||
|
||||
@@ -113,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))
|
||||
|
||||
|
||||
@@ -510,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
|
||||
|
||||
@@ -530,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:
|
||||
|
||||
```
|
||||
@@ -616,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
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import * as qs from "querystring";
|
||||
import { parse } from "url";
|
||||
import * as base64id from "base64id";
|
||||
import transports from "./transports";
|
||||
import { EventEmitter } from "events";
|
||||
@@ -168,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;
|
||||
|
||||
@@ -300,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,
|
||||
@@ -400,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();
|
||||
@@ -526,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",
|
||||
@@ -585,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();
|
||||
}
|
||||
@@ -736,9 +748,8 @@ export class Server extends BaseServer {
|
||||
private prepare(req: EngineRequest) {
|
||||
// try to leverage pre-existing `req._query` (e.g: from connect)
|
||||
if (!req._query) {
|
||||
req._query = (
|
||||
~req.url.indexOf("?") ? qs.parse(parse(req.url).query) : {}
|
||||
) as Record<string, string>;
|
||||
const url = new URL(req.url, "https://socket.io");
|
||||
req._query = Object.fromEntries(url.searchParams.entries());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,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,
|
||||
@@ -773,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);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -800,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,
|
||||
@@ -827,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);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -936,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;
|
||||
@@ -949,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.4",
|
||||
"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,13 +33,14 @@
|
||||
"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",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"debug": "~4.4.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1"
|
||||
"ws": "~8.18.3"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "rimraf ./build && tsc",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
if (process.env.EIO_CLIENT === "3" && process.versions.node.startsWith("22")) {
|
||||
// FIXME WebSocket error with engine.io-client@3
|
||||
if (process.env.EIO_CLIENT === "3") {
|
||||
// we need the WebSocket object provided by the "ws" library to test the SSL certs and HTTP headers so we hide the now built-in WebSocket constructor
|
||||
// ref: https://nodejs.org/api/globals.html#class-websocket
|
||||
global.WebSocket = null;
|
||||
}
|
||||
|
||||
@@ -59,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) => {
|
||||
|
||||
48
packages/engine.io/test/fixtures/ca.crt
vendored
48
packages/engine.io/test/fixtures/ca.crt
vendored
@@ -1,33 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
lfbUHTmnKthFwJS0958xBq7UM7+RzyFIOg==
|
||||
MIIDBTCCAe2gAwIBAgIUPV8LcHEkiA3LPuNyt8kuDxRqjlQwDQYJKoZIhvcNAQEL
|
||||
BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAeFw0yNTEyMTUwNzQ1MDdaFw0yNjEyMTUw
|
||||
NzQ1MDdaMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQChKwRqFDrGgqMHjDUx5he6MDQ+BxlMqqpm0qu5pOCCPzSvYDFo
|
||||
iU4n3lK6uCbemv9Gdk/U99ev8LAGg2SkZAZXBpDrdZepkA84ehUHu5u7PHlXNodE
|
||||
KL7DLKsvoaYYiQ5rLyBYieDOYqtYJxtfLOeh+tmnNa+G4chpYzkll7OCeEQhbocQ
|
||||
QLdP8novscoSibp6bPmoVsat8RRru0CK9ND1v+FvJ2R7Lz2isBIr+p9ZrkYkIXa3
|
||||
OqN3wacZ+doYfAC/a4SK64Jgv+Lz6wuzsc7XVjBGEEaa1P1zd2rh70wNm8Lgmwr/
|
||||
Oq1Lv7Lg3plXY7e/7V915p96/bxbIiVHiu1JAgMBAAGjUzBRMB0GA1UdDgQWBBRN
|
||||
SIfG8SbjQTQCuymcNnQh4R5oxDAfBgNVHSMEGDAWgBRNSIfG8SbjQTQCuymcNnQh
|
||||
4R5oxDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQADHV/KfXlN
|
||||
ZBmjWWqQRCmJCUsFkn29C0RQQMILbxYkNl08DuSQw68+PsA2YeB4aUikmW5sRHYF
|
||||
TgLXxgZ+aaGH4WtedumD4RGodcFbxI2WLIilKD4nH0FmB4I9bkULgMyyOZ0g+Vc7
|
||||
ekynqzYtzQBgK+HLWtIWZRTM/BR5IAzt/SdAVoQwL3EHzlc57Q9rRqYeBiyse3lC
|
||||
8Ojb9ZLhwv/hihWNd+mFKeWzLAGJIB439AUzKcg96YDKB3Nwosa16g0xslwxvAL/
|
||||
cKJQ3mh7pIOX3iv9YV/uifRvdCiI/e2p00YjZiS3ggdravSGjLLypw6HFH3hFnw7
|
||||
2ZzA88NgFQXi
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
79
packages/engine.io/test/fixtures/ca.key
vendored
79
packages/engine.io/test/fixtures/ca.key
vendored
@@ -1,51 +1,28 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQChKwRqFDrGgqMH
|
||||
jDUx5he6MDQ+BxlMqqpm0qu5pOCCPzSvYDFoiU4n3lK6uCbemv9Gdk/U99ev8LAG
|
||||
g2SkZAZXBpDrdZepkA84ehUHu5u7PHlXNodEKL7DLKsvoaYYiQ5rLyBYieDOYqtY
|
||||
JxtfLOeh+tmnNa+G4chpYzkll7OCeEQhbocQQLdP8novscoSibp6bPmoVsat8RRr
|
||||
u0CK9ND1v+FvJ2R7Lz2isBIr+p9ZrkYkIXa3OqN3wacZ+doYfAC/a4SK64Jgv+Lz
|
||||
6wuzsc7XVjBGEEaa1P1zd2rh70wNm8Lgmwr/Oq1Lv7Lg3plXY7e/7V915p96/bxb
|
||||
IiVHiu1JAgMBAAECggEAALEu5oNDpERXNeRF5Me4tYkOHd1ye3ApRmeRLOkwxFEq
|
||||
6dM04qe2PUEbF9/P3YybVpUG42V10jhyqNft+tCihbTlNkjZRC/R6zL09ZA9/ZTB
|
||||
HhMQM6zU1XYqt83clO1zAecHfknYQ6lC9RVe8E6YiDq7ZQngT65rQWktdaJ0eOGS
|
||||
awJiaEXhUsrcW7lSmeysvaTOqFm7RwkytH/hAoPWmPC6qvN+LR18aj5KXd3R4cRK
|
||||
j+EWr2EXkKWx18hxI+5Y2icWO5lckzh4dfBoWrTgLGrXQdpnw8jWqTZ6431rqv6g
|
||||
3Ao2NmRUUGz3eyY5CmXdl+vdbfZTC5xxSDhvB5NLrwKBgQC+QnqP0TZ6xO3Ov0gy
|
||||
EAemCn8sEL+ByuJSJdBydLSRfghH1WfRdPzXWN7274zosNxUpPeft9Dq44YQYX6R
|
||||
gUJ39QnmUVDwBVwFSw1ZgQjBvZJTr0sp4iBb9hEGhSLlpnrA4qZZQC28A+eKkmTu
|
||||
QR0qMPjlqiacfFISBJwCSll26wKBgQDY2ziwueYuQuNy/jJ8+lFKvIMrv34+Htp/
|
||||
ZNGxDN1Fthq9JRHUP4KL9UIVGlgIt6hwgOXIR0dw2pt7ffF1SiDST/PvBe5Allex
|
||||
uvGfTeh0kTv6T/JAPnqBS5uM3fCnQaF/bbTllw4elKHB4liysh+Gq5svU7mV7Xop
|
||||
1RmGKn6HmwKBgQC791u0uEH1mpdDOdFuvE2CKj6n30gER9e+xuMgINLAJt6xcVGn
|
||||
KsgdTQzCs7nnrcuPyIdoASdi2DP7/QYZZLWxY6JLLC0lZHYcOKDQu11WYx6slLNS
|
||||
hrfngrwhT+lBL295HrKv3GsSpFzdl3IlvKi+pTFRXP/WfDBs4qbq0F+AzwKBgDcB
|
||||
5K9veGPjs65HrKbnGBfNGbjPKka3rNUDze0LRlWYi8/Ox2b/dS3rWIfh1tLfQ2rG
|
||||
R4M2EXke+rGokMcftpOilE3dQ0I+4J/Eu+Wc7YokDQLBpMGHF6wUcbCZ26GRlFWu
|
||||
jmRunLZFHYMA1178r+KJRSQMKNdPFd3moELYJKBhAoGBALAJg2x1B1TpLvJf3iI3
|
||||
BeuASfYKpp3X3cyUy0ojxdYIEHHZ87TINojldHVd+t+80M8FG7YgQNTWtx0Wz587
|
||||
EslppRo2r8qusax9KjaZ1uf04jWxR/v88dvpspSi3IV/J3rNhf3xz1gEFdRXvtQc
|
||||
Ek+aWNHKp5nqpuY6HWUacI96
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
54
packages/engine.io/test/fixtures/ca.key.org
vendored
54
packages/engine.io/test/fixtures/ca.key.org
vendored
@@ -1,54 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-EDE3-CBC,47A0291BAB679B36
|
||||
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
||||
35
packages/engine.io/test/fixtures/client.crt
vendored
35
packages/engine.io/test/fixtures/client.crt
vendored
@@ -1,22 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
I3jNu1mm750TEWWMhPk=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
23
packages/engine.io/test/fixtures/client.csr
vendored
23
packages/engine.io/test/fixtures/client.csr
vendored
@@ -1,12 +1,15 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBrTCCARYCAQAwbTELMAkGA1UEBhMCRkkxEzARBgNVBAgTClNvbWUtU3RhdGUx
|
||||
ETAPBgNVBAcTCEhlbHNpbmtpMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0
|
||||
eSBMdGQxEzARBgNVBAMTCkZvbyBDbGllbnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0A
|
||||
MIGJAoGBAPrpGfw/w74wrXew/EggQwdcNFmF5EuoY2U2xfNoEtacpKVX5bGLU50K
|
||||
Ka0CHhkBVG3UmQr2ZAQAZ3ELxNHAMxLTb4RQrUfXO+HvsSThlwdbynkJUSbncVOY
|
||||
76OEv7sFBhdbXKL9dREG/bGbdD3kjvz3lqCmswUjweI678d2L+INAgMBAAGgADAN
|
||||
BgkqhkiG9w0BAQUFAAOBgQDKGUqjkUxGOisFN70X7ZOW7H99veR9QlixKl5e0W+7
|
||||
UtJ+GUtH2WQEb4F72+ruHrdDWQI1VaH9hPOvTRCjlgXiT0RHXpGPbJK/Nc+Eq5dm
|
||||
kuk/tQeXv6+S1fgYOm0w09rE7pBjQtuAybB55lGZ7k84UE2xTc97Ru14nYFCsZ4z
|
||||
RA==
|
||||
MIICVjCCAT4CAQAwETEPMA0GA1UEAwwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEAt6j+z+Gsbq6QNmrHk2xjRT2r9tQSQKjD2s0m5K6Vh7UQ
|
||||
5SnmOtlSdREepZNhcNbDEEZ8hEH01jVtsYvwbr/Z2yQii7qDw0CxaRypMAXP4LuD
|
||||
ejvM1HHYZggUMUW3DQfAcFiMy7zIISKpXfBmU9EolnX3fzj/ta2VQluVlqrVdUdT
|
||||
ttNiSh+shPwkULe9z93W37lwQw7OJI+QjNcTurBrHdhL/Yhz6yzX6/t5jmZxrHSM
|
||||
SdaDJGcEplQqnGCfGhqtoLWyuZmSXaoxxvSsneC073BCEuVhJlZBIPRm2ZK2MOVK
|
||||
XcgCEi9ipNF+o6WXLLAKApS53atSDsW/6WFJutN4yQIDAQABoAAwDQYJKoZIhvcN
|
||||
AQELBQADggEBAK/qIcDwg0kFcriPBU9+LGbpedbtw0naT3PVXfYYq3TzjBtPW+4c
|
||||
f5QmAhrIMkF0xQ1YdDsx9U/jQ93xL8OCo8+pbiQOYS0uVTqi/7UL9ggkD0aCGCZs
|
||||
j1FEWbumouW9ZuQg6gsZ9XoZmXPTKZivIpalOTV460vQvhM4Fspx0i7LowiS9ijH
|
||||
BGG8QEr1ZihhXQloJSnjwVMY8qqVwqvJDidkN7eSjZTL2yOBzgiigpk8IMe1bNrb
|
||||
dIoJVEjzFooRa08MPt+BZJ7xiuCyKZe6aVmV0XZOcnHChSc8jmSRXZdNpikGdQRX
|
||||
1BamYMxhR2x8QYX0DiJYZwpgUaG4EOHsqb8=
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
|
||||
43
packages/engine.io/test/fixtures/client.key
vendored
43
packages/engine.io/test/fixtures/client.key
vendored
@@ -1,15 +1,28 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXQIBAAKBgQD66Rn8P8O+MK13sPxIIEMHXDRZheRLqGNlNsXzaBLWnKSlV+Wx
|
||||
i1OdCimtAh4ZAVRt1JkK9mQEAGdxC8TRwDMS02+EUK1H1zvh77Ek4ZcHW8p5CVEm
|
||||
53FTmO+jhL+7BQYXW1yi/XURBv2xm3Q95I7895agprMFI8HiOu/Hdi/iDQIDAQAB
|
||||
AoGBAOFHTXd4YO1wky82Dy1LGiOPm8kNOC7d33BOv2iN9uwN9J4nzymbqNUE/OpD
|
||||
TnaxBPcfvNFk6+PT4QxUvsB8ytzDMZ3YC4xyJf5GPP/hfzyWCRjB557WZl1cx7nC
|
||||
2gA93PBZE7WT1SySXmjsiC7o/2T/0cUaawXOBczHP8oXoEkBAkEA/c1MHs13ojxh
|
||||
oOj/ibCpYpd2Zv5Hrc5tsh+otDdIrb79IAHnNw7WhMkLs6cLk1MY6jLeCvQtjlUY
|
||||
H5C/6Ez84QJBAP0VZMgWPw3FVNXPrj833OA6XjyWO+TADpnlrahuDQqWnR3C29Uc
|
||||
Iq/ApVX2pt2cNIZpiuJ4BYNc44cHjvu6vq0CQQChan1cJc9NhluNLELBfnLsOmpa
|
||||
bKSH3P8VR19TZsm5fvub7Lnx4WT7xKXFl5scEsCIyts/WjbTDDmwca4r/zLhAkB4
|
||||
wkeHbY4CnSDgsKr9AUPEPjWPBURo3vdYmY4mKvTQE5O+iqboZfdrEyoQ/ZMbdRhe
|
||||
9mdNrmU7DAyI9qNUHAQ1AkAlq/vdkrcq5SRR9uti/1M0/Jaw7l3JutBaW93kdvXx
|
||||
BX568ezO1PQtXwVSv+uJEkDoST1bkvhqt7hlMu/RkmfG
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3qP7P4axurpA2
|
||||
aseTbGNFPav21BJAqMPazSbkrpWHtRDlKeY62VJ1ER6lk2Fw1sMQRnyEQfTWNW2x
|
||||
i/Buv9nbJCKLuoPDQLFpHKkwBc/gu4N6O8zUcdhmCBQxRbcNB8BwWIzLvMghIqld
|
||||
8GZT0SiWdfd/OP+1rZVCW5WWqtV1R1O202JKH6yE/CRQt73P3dbfuXBDDs4kj5CM
|
||||
1xO6sGsd2Ev9iHPrLNfr+3mOZnGsdIxJ1oMkZwSmVCqcYJ8aGq2gtbK5mZJdqjHG
|
||||
9Kyd4LTvcEIS5WEmVkEg9GbZkrYw5UpdyAISL2Kk0X6jpZcssAoClLndq1IOxb/p
|
||||
YUm603jJAgMBAAECggEAHlPTr/QuCyDcQ4AicJ+nTNnAOcQPN4omvUy/LWf+3Sfz
|
||||
IERo9jLIwPgQvXq2znFISLm6+gQCMBUmhfj7vO4FRYCUC8rC000tfpPzBDERgKS2
|
||||
M0sIqdQazc5rty8x6P7ssiCNL19/FKmqmg6GdzTEpQPZ2LJK129QhGKnMvgGw5S5
|
||||
TA1UUfiF3JoJLN9YY/EAvrWx6uML4JCsjkjzwG2rXG9S9IOIuULLYyK+d+383GUI
|
||||
c/okVKILR8pUpFr+y5oor7Dcxa2ApkcRzTvFXvLoMyD7SHkmVLB6OdvgMvK+rVFH
|
||||
eCqlxiKXAzy9FG1L/EL5pYJwUSKp4itv//WPbvAkYQKBgQC7raVpaYoQ9i+QiIR5
|
||||
XQijEnZvPdJShqWtvLF65P8WQYdMvQFrO7HM9lSILNzL2291TssWBr5p9MuwbKmc
|
||||
eKisYwzJWwyDsJiShYBBlAKZOgZ2l8U6m8SGgYUU7/OcVekwrIdm87xi7wsd52aW
|
||||
ZhifzL7E/Zc5pEFdGKmYkRWoTQKBgQD6hOITk8dV461zrwS7Ev65psmWyx2SGUmq
|
||||
EIBi0fgE5wov2CXT6jR8mjJgvDjefG4lw92rT8CLsyaguoi2fjktbtOfXLMKVCF4
|
||||
K2r9KlD/44+WLKBYLKNCffvI+ZcdQqTvUJfZUH7k44fTP25ByqMyj6FNsC0xhL5W
|
||||
h1k57W4QbQKBgQCO9LYKlVmVkfCpJ1PBHhx2GpocIfsS4X1R2WlXMxca3M8ypMVv
|
||||
6QiGFxxMnd+RaJR9xobrQDfKfayptht8FGxm4wvaVIAzz1BoS0hmq1dPX+OMcg7J
|
||||
ld0cl4vHZv8pFkeJy+FXilD1CyBsptZ9uUcIcezeIEwQnA0t3JtSmzDkAQKBgE63
|
||||
zJYcPGmXphwQparm2BWb/AIfBsaunYALuVvT4FwiAQhxoclJ8X4psaTF4BTBApdp
|
||||
SVEYOUdsXrnogybc2LiW93Y+2Z9oOjAZbH/qeRM+/RJSKXwrYo11KtXG3535H/x2
|
||||
1ZAahBUGDdrqNooD024Cxcu0jS7fKDPmSbozCf+tAoGAZ3NwWzj3LpArnfOrDr7Z
|
||||
rXiUvCmSjPUBF99vN080rU8fBILdQTdz5DdE3K0xWDEiwAk1NZbuwy4Fx02aHG9f
|
||||
5CkmGEHOsLGT6n6BHrjuSCtvbNOAGLhSBbBJJkFOf3ktJXU64P38M7OP0j11Tzkv
|
||||
VNurVfn19LNgoLisW5c9tEs=
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
18
packages/engine.io/test/fixtures/client.key.orig
vendored
18
packages/engine.io/test/fixtures/client.key.orig
vendored
@@ -1,18 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-EDE3-CBC,081B123E7DBC9687
|
||||
|
||||
c8gE0LQMMtV7GhUcP7GHBotlNNAsbyG/gERB//BFlvRPjs43ALvm5xMGUkcsZkw9
|
||||
H6ljvvgyRkud06x1mGxqlZ4fLlty0/mdXf6791R9gA/9bKLakHGlwnoVUrEPIeh3
|
||||
Iu/3IF0Uz8o3uljSp9eehR8sW5V4o+Z1MszQ+GkcpCyYnDPykyN52QLTM3RcUJ0v
|
||||
2iJrgxYEAyMA14iIpYM8IsFDYIBvwVfQxyBZnQ+8dDQgVOokz56g+/9emgOsVjWj
|
||||
bdUKZ7+95RLxCq0hjJd8GnoDn7qtxIPOGVqYcdEwwNyzO43FnpjOFhNDWwsrNrgf
|
||||
vUHKhZRHB99CkuCvGg4fjVbFd/1/n3eE5VmiQuqoHzPnXH3RDIx2rZSjFo/NkzYx
|
||||
4XzZ4YOe6vOP7kR18CL+bl32WneMELoh+TPLmsSC3rIP48M8+oQINSZaBrl59Ocw
|
||||
NW36xgDNSQoia2splVNo51vtZomq1Hb3co6hD43D4xnrc6Aqucm3BsW6UCc/PVKv
|
||||
HAXLCb+3awIy5NJSYb0qRETE2rKB6LjmKfILtOrto6QYSlkJmQUxqoPXgRAWWNlw
|
||||
1Ngws84+6UjmGWDBlpZHn0hcO/B7KJAAS/xNSFYDoSu6dAbabxTI/dZCWhw1aN5c
|
||||
QeYPihCi66F6Vuq/QT89dHtZE4IMPH7R95Yp18tCcyVGxaEiphw3HJYepMxoJesQ
|
||||
YH8tWQwvD5LaADzNJIKBxMjCOK23GuXWQLJJRf4QWiXaQar69qXULxBT3iqBp/rQ
|
||||
VKsQByBwJlEo/YSEFjhhMgo0zSbqIRAY1XCDo+dgB2IB1KPAobhSCQ==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
BIN
packages/engine.io/test/fixtures/client.pfx
vendored
BIN
packages/engine.io/test/fixtures/client.pfx
vendored
Binary file not shown.
22
packages/engine.io/test/fixtures/generate_certs.sh
vendored
Executable file
22
packages/engine.io/test/fixtures/generate_certs.sh
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Generate CA private key and certificate
|
||||
openssl genrsa -out ca.key 2048
|
||||
openssl req -x509 -new -nodes -key ca.key -days 365 -out ca.crt -subj "/CN=Test CA"
|
||||
|
||||
# Generate server key and certificate request
|
||||
openssl genrsa -out server.key 2048
|
||||
openssl req -new -key server.key -out server.csr -subj "/CN=localhost"
|
||||
|
||||
# Generate client key and certificate request
|
||||
openssl genrsa -out client.key 2048
|
||||
openssl req -new -key client.key -out client.csr -subj "/CN=client"
|
||||
|
||||
# Sign server certificate with CA
|
||||
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365
|
||||
|
||||
# Sign client certificate with CA
|
||||
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365
|
||||
|
||||
# Generate client PFX files
|
||||
openssl pkcs12 -export -passout pass: -out client.pfx -inkey client.key -in client.crt -certfile ca.crt
|
||||
35
packages/engine.io/test/fixtures/server.crt
vendored
35
packages/engine.io/test/fixtures/server.crt
vendored
@@ -1,22 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDtDCCAZwCFCt+tjtA9647yZp8eNZurQtNp4x/MA0GCSqGSIb3DQEBDQUAMEUx
|
||||
CzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRl
|
||||
cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjIxMTE4MjEyNDExWhcNMzIxMTE1MjEy
|
||||
NDExWjBsMQswCQYDVQQGEwJGSTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UE
|
||||
BxMISGVsc2lua2kxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDES
|
||||
MBAGA1UEAxMJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA
|
||||
QLNPDILqMIulvPga6ogci69xYaSp50U7Tv8Hww3m4D9w2ubyGqR7dDW20iGJI7tT
|
||||
Ncv/UtLqgknEiCBXkq/MDr5gdXNI0V/j+qAOqfJSVxvxxHxLrt7ivjpZHbO7B3K7
|
||||
bkWmCxAFzWAlE5U9FXX1b8VfetJv+cDEaXsYwuYfowIDAQABMA0GCSqGSIb3DQEB
|
||||
DQUAA4ICAQCDSuFPJ++HY5WBhpnumbZ7+T0ReWKaerdNnQ2Xgrna5mZfB2xeRkvY
|
||||
XeJ9VBCpGgEZKCKkhZCjomn/kLkYzRk4Sqr1ivN5NWl6G/9UTttHdRa3xiR1NhKI
|
||||
AMYghpel30w5e+cWtsdR06P2FvZMuiMFCyqsbPf1xcEIAXN7HJDswq6g0ppTVZ4L
|
||||
sXljG/J0vp+jAst4XKGLaGqnt8JaBnpNX9NO2Up3h5j7Pa4Nhm/LZ3Ku5ZVDmS1d
|
||||
B98Bsgr6tQSSyPNfZW0tGXELsNX1I+wUFw9IXFadRTHkhjeT/GhFw3i12uY7rqzm
|
||||
uJegTtWDkp1QOajhYhLD9WGXb9teldkAAgZawD6ax/uAzqx/4mBFvsUa3FMcua8k
|
||||
HF9P2lLzKAcyaKt1cvlfUYmDVZ2Gh+9PgM8SqRpMIqK5jMRvFgemxJXS9BMBrQLp
|
||||
TCvgRwQZD4mUloRlGNewKfJ0oQ1rY29vwdjTL8+BBS/GR8EuzYnqJG/D2nK0guIN
|
||||
ze+cSDghA5N2pp/ffnpLWmkIDO+fsGAj3eApLhbPQ1xCXnEv6fOjgUmnxdt41m8d
|
||||
+pEVBICohnvYgoEERDNAi1onJlBd/eyk0Jn37QiwqhQyrmfgwncvlt2SyzS1IZ7s
|
||||
cEYreG6QHghBhgYiYo0FMuDCjT6g6Ga+T8nOp0xpZtGEWvHwjLjxvQ==
|
||||
MIICrTCCAZUCFGdEcsibkaFr2RamsAlBpOyqm58fMA0GCSqGSIb3DQEBCwUAMBIx
|
||||
EDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjUxMjE1MDc0NTA3WhcNMjYxMjE1MDc0NTA3
|
||||
WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQDJw97tTsSTtZrOVjd89NKW548SyaB14Ghuqpfc3KxGa5OMuaXQU9aY
|
||||
nnoUCEU/YlS76t+ShZsQB9XR5UGy7T6ACJodm2tLsMmaNt3OX8JaXUeIY+UB9qPo
|
||||
aG8dLBenHays+dud2O2QUF7QhvRvVO98NrGpVaYVpes5lEQhvzKjaiK+Nc51gndR
|
||||
daAmvCPWINIBegUwRbbsAgNQaW8UEdxYWhVF8MDa//JPR7rTlHSFq6Or8SIvR1D/
|
||||
xAtvEXyAqPlkedIjh6Or2YCEIR2Lx+9Ky0Yti5vmh3PqZG6g7lMDOhIg/fkO2NfT
|
||||
9F+z6q7QL5+yOHzJgBepC7V5nQSQMQr9AgMBAAEwDQYJKoZIhvcNAQELBQADggEB
|
||||
AJh7TfVR5lBVtWepx5ghWrWDrqEXw7nJp4ljaZ/tp+Ufb1LasMflbAnShvnhPAg4
|
||||
A5/7GkCUjZOMc5QV2MYQGIPtyRdM1rdPG6EuR8SUA/+GxIbaLo7iDpAKpcvCdFfS
|
||||
pd44wgDBbzbTtw3nRGy6RXm1vB2FyjvGWVXYL3XoE2r/xQPgaf93jeglbgo9C8Tj
|
||||
MQ2NdR4GPBtjqZNDmer6p2Jr31pkS4exT7oNysnZfoXll8F4PHos7Wc5VYo/2qQy
|
||||
uftfQqYIZLaZKzjR32Gsr6Z5yb8GlISWnRss5ebD6+wzh8sr4fat0SY/vy/3oq5V
|
||||
0lwYgNPo/OuwNMA7Eo/iNPY=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
22
packages/engine.io/test/fixtures/server.csr
vendored
22
packages/engine.io/test/fixtures/server.csr
vendored
@@ -1,11 +1,15 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBrDCCARUCAQAwbDELMAkGA1UEBhMCRkkxEzARBgNVBAgTClNvbWUtU3RhdGUx
|
||||
ETAPBgNVBAcTCEhlbHNpbmtpMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0
|
||||
eSBMdGQxEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
|
||||
gYkCgYEAwECzTwyC6jCLpbz4GuqIHIuvcWGkqedFO07/B8MN5uA/cNrm8hqke3Q1
|
||||
ttIhiSO7UzXL/1LS6oJJxIggV5KvzA6+YHVzSNFf4/qgDqnyUlcb8cR8S67e4r46
|
||||
WR2zuwdyu25FpgsQBc1gJROVPRV19W/FX3rSb/nAxGl7GMLmH6MCAwEAAaAAMA0G
|
||||
CSqGSIb3DQEBBQUAA4GBAClj/K2DAH5S64T6s7jervmk4N956Ho3aTLBgE+ReXLj
|
||||
btcTdk3vFbQApAlG6MrSKys4HjpKpP/RENx3Js0HHeb8ELmWtIQNxRhwIpl0K5AD
|
||||
xorKj+mwngLtVyARb/M7O3E8jYHzBPzpsolKWIY4AavYdmHu+Zhgm4hPKUcW+bAv
|
||||
MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEAycPe7U7Ek7WazlY3fPTSluePEsmgdeBobqqX3Nys
|
||||
RmuTjLml0FPWmJ56FAhFP2JUu+rfkoWbEAfV0eVBsu0+gAiaHZtrS7DJmjbdzl/C
|
||||
Wl1HiGPlAfaj6GhvHSwXpx2srPnbndjtkFBe0Ib0b1TvfDaxqVWmFaXrOZREIb8y
|
||||
o2oivjXOdYJ3UXWgJrwj1iDSAXoFMEW27AIDUGlvFBHcWFoVRfDA2v/yT0e605R0
|
||||
haujq/EiL0dQ/8QLbxF8gKj5ZHnSI4ejq9mAhCEdi8fvSstGLYub5odz6mRuoO5T
|
||||
AzoSIP35DtjX0/Rfs+qu0C+fsjh8yYAXqQu1eZ0EkDEK/QIDAQABoAAwDQYJKoZI
|
||||
hvcNAQELBQADggEBAI9mZrnDPk8PaljHRTLYlYNtL0+gVpav+deUnAzbjA5bxdaw
|
||||
t0xRptSDg3sFYFJWJ0HaqJltxSmdjj20MxmSCrexFOVXpISNsC/uZOs2CFWsDBdp
|
||||
kL6pIDHmxJF2q3dA7kZMeElSqUohYlA5t5rHp6w8/Vb0HnHAlke4kPd+bbo0MyFt
|
||||
mL2txXAz9AU8a5sG44x05VsSahtWSWeDNFbvaX27FhNDHxBMAFCj3niRQklMpCGG
|
||||
uX+OWr42iNJLhjyR7zsJ26tf1gP6+kFoAz2kRNC9qyQyTAzXNEydg6Yff1TuZv2h
|
||||
ktXz5unhsb91xO2iGT8PSWDHcVReSZRdyhwjYrI=
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
|
||||
43
packages/engine.io/test/fixtures/server.key
vendored
43
packages/engine.io/test/fixtures/server.key
vendored
@@ -1,15 +1,28 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXgIBAAKBgQDAQLNPDILqMIulvPga6ogci69xYaSp50U7Tv8Hww3m4D9w2uby
|
||||
GqR7dDW20iGJI7tTNcv/UtLqgknEiCBXkq/MDr5gdXNI0V/j+qAOqfJSVxvxxHxL
|
||||
rt7ivjpZHbO7B3K7bkWmCxAFzWAlE5U9FXX1b8VfetJv+cDEaXsYwuYfowIDAQAB
|
||||
AoGBAL7tQmXl2fmz/mu5kHhCpKwcuT6TpxEo4aN132aY+qxn1flBHAwiE2mbTmDi
|
||||
rHViq/2GNrK5UUed3p60RdJSlgwIkyqtcGxWhUJGYCR/hU60qeeLp3MhhOoOFbiV
|
||||
YTDsoC7V/SuWbX+1qG5FxnHSnTZhAIRkZXS4uTZ5WDcQm/7BAkEA+TlZ1IT9CeU/
|
||||
FpHpqc8RgR8377Ehjy8o4Z4EGFnxQlAUWASnhs6dw4isr3+c7hA1OEmqmcRClPVZ
|
||||
t1JbHAPC4QJBAMV60WSJzPUccCF47T9Ao2CeWFl/9dmgQQe9idpTNuKMXNtPJN44
|
||||
0MQvnb+xS828woJOoRI+/UTVLLBc4xwMtwMCQQDZTadExTw4v5l1nX5GoJUbp9PG
|
||||
/ARN64nSx0u8y9evwVErucs0oL0we+BOGZAEhz9QN/M3pceESDWUwYtNbv4hAkBB
|
||||
Ku2MqvjK7k6GjTxlgjQn/zkSl+qOnZa4MjEarhlPm5hM+wokl0U1aK07BAwK4b6i
|
||||
d8YpmkXEAEEWFiEQMZX3AkEA1SkdiFj1u7HnzO7onLJsnFzowX3pm1UFl0azOMlM
|
||||
2GkjYxWeJ/4VL7Y6QkhHE0Nj3my2+MJQI9xpYgMbw/l11w==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJw97tTsSTtZrO
|
||||
Vjd89NKW548SyaB14Ghuqpfc3KxGa5OMuaXQU9aYnnoUCEU/YlS76t+ShZsQB9XR
|
||||
5UGy7T6ACJodm2tLsMmaNt3OX8JaXUeIY+UB9qPoaG8dLBenHays+dud2O2QUF7Q
|
||||
hvRvVO98NrGpVaYVpes5lEQhvzKjaiK+Nc51gndRdaAmvCPWINIBegUwRbbsAgNQ
|
||||
aW8UEdxYWhVF8MDa//JPR7rTlHSFq6Or8SIvR1D/xAtvEXyAqPlkedIjh6Or2YCE
|
||||
IR2Lx+9Ky0Yti5vmh3PqZG6g7lMDOhIg/fkO2NfT9F+z6q7QL5+yOHzJgBepC7V5
|
||||
nQSQMQr9AgMBAAECggEAHiOf8p98TltHrXBkGAqjRY8ACIytZ0ZXG0bo6gFdy6Qo
|
||||
tZIK0qCfcwtjTYhBvdrksPCAJq1GEUI2XsUKCB4X4rzGNst/Xt5g8yQkhH459FEw
|
||||
TQ+tBxrOd7pX9MngG6LbZzhopb7gl9jlnO036MSNhKbP6a1lYqD3DxIWjlr3B8Fg
|
||||
JCK9T6tlkuij/METOKV0JCeQM1dwGzWTjYOXdtyci8+0/aUsLB08I72E61SP/6IK
|
||||
zeFSaP2JkaUxSSA2vU369OODD/LE0EMdf21U2Dh7OGRZ55NbEoUztop0hdwI6smx
|
||||
44nrmbKG23Y1LnLNFAAxopU6RAw44RG6PIwR9GrYIQKBgQDR/GuaNjfqz9livMi/
|
||||
jO8EyGcLavqHQQykAT0ps1E+w+HL6Vdb+2auFTEcLMeDJIxb0iYvZyAsskvO3lgp
|
||||
rH9sOcrsQrCluE+TA/tLvYmJyfAhFb42EansphO2oTDD/2aOexSGztGtuYAMBpFQ
|
||||
3AkjppZN3qe4iB6gfys6fzCtXQKBgQD1+kh4Q+a1hv+Dh+Cn8wNnhgcTI3LvbU44
|
||||
86G8iYmeTsEhy12PwGkz/RBLOX0DkxSPlpWgvDeF6kAgoQdA47c3ucaVnM1taf4h
|
||||
cjAuS856FigM3E4C/rsEgvI7N4yV/rsKOnyqgHUwNalciAOgrhuGNDViazH9JpkK
|
||||
pFaX7lhaIQKBgQCQKUyyPaDcC0BCIjtsDlSWIwNjellu/ACyo8sa0unnPlHJTXRV
|
||||
SN5wi0oA/tkmNrRJ4ZenmZQkO3ACIPHEApIkefTPiOV/kG7JDsW6dPB08XdzL1s8
|
||||
AZp4RnhoPHefR7tf/C3WcyzOqPi4yNWA/t8jY53y7faVoJXuOAA0k9eWXQKBgEvk
|
||||
GI/igz/mktcGCS5IL/i0xWIGQePVGZlAsdn1pVwOeE/w6sD7YHoFzg0ng4aizmku
|
||||
0KNy7r6Gc5qdlBtVJqYuzzJB2q+zUBEJpgvin6XTSDAPmJIb/Z96tznF/b4ZhaO2
|
||||
P9hrIbzqEYLXBCeDEELrwLzIzfeI/RbndUAS7XeBAoGAa8WGdE7ndAtjCK4jCFiB
|
||||
vIt4M6EZyZdmfTJ91SSEA303mp10cV6YGLOErROnBvnUlFSbRN8VNVA/LYq3M4Vm
|
||||
da5jaFCjJhvH6qFhwsAkuGKsE1OPTs56iqZlSw9/8IJ6NW7/Igy6xR/Z5xRajAzl
|
||||
FsyRRSHduI5HNgDo7riv46M=
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
18
packages/engine.io/test/fixtures/server.key.orig
vendored
18
packages/engine.io/test/fixtures/server.key.orig
vendored
@@ -1,18 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-EDE3-CBC,8A14F3F31BF3CDF7
|
||||
|
||||
84UpLjX5GbfCoP/+ZRLwHZHqmgKyc2cjQOQKhp2YTk+4E0JSO7KsKwJax55r5VqE
|
||||
ECGvbd8yvVZ3GRpU6bDCLelDN+Ob5ZSGhynI0f9is6Qle1/SbvaK+qL6MLf4H38l
|
||||
Y91eZxjE8hLvOMHBlbobLmy9UUzl2osBDAM1nm5d1X7pF7mN4xgY8s2G7UPq3ZZS
|
||||
NWwMsnwtb/9Ahm7WXfsJbebyKspkTSHYp/ozaOGW58fuKxkwRFd1UlyblZWU3ezP
|
||||
JCpvJQ7HS6Dfy6+GUaxcC6pyxqnoJHYccB5usJ2h4QD4Es3sT7Vw7M80JKw3vAWm
|
||||
TH7VkFX3yGfJ1p1jNzifN5687QqrjeI3/ecTs1rFhIC4TUPN9EDvw86Y6l6Mvo04
|
||||
Hl2cVzCnCrZYq0ICD0op3+7f9kuKl7bz54S/iRG2qQdICohPs7ra2yaUy+NFVDs1
|
||||
XdXyF5/xew+Rr2z7ygEd+OrvXxPV0zTFbicg9GXGeB/pIYAclmoSNXD5T3voN7y9
|
||||
5MjSGL375N3z+kqPzMNawYCnZLwQ5jYTDUkDTATYpjcIDVDQkzbl8mFp4i+Z9GhL
|
||||
H8FRAgmBbkgy3dxhFjxr/WzuaTkUCAbGhrtPd8WCPhBXJBzWdrBVR9SE8zT9n8lq
|
||||
ZwaPwkPLBrLe/XZFEQJ5cdvMVNy4QQwsyxHnCgO3VUc68UYXb39MvyM6t8+S1rSm
|
||||
SyvVAT+jB8T4VlE7tedQGuvyKMmeNIJe9znd3u4S+Aq2+vw6bOKeNYBMaupR5Gyl
|
||||
bJrscuTG1aLUe+XH9BuFBUoIwdXuBv4Ko/pDL0MYPghDAEGkp4Acmg==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -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);
|
||||
|
||||
@@ -99,6 +99,26 @@ function setup(opts, cb) {
|
||||
});
|
||||
}
|
||||
|
||||
function createHttpServer(port) {
|
||||
const httpServer = createServer();
|
||||
|
||||
let retryCount = 0;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
httpServer.listen(port, () => resolve(httpServer));
|
||||
|
||||
httpServer.on("error", (e) => {
|
||||
if (e.code === "EADDRINUSE" && ++retryCount <= 3) {
|
||||
console.warn("port already in use, retrying...");
|
||||
setTimeout(() => {
|
||||
httpServer.listen(port, () => resolve(httpServer));
|
||||
}, 100);
|
||||
}
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe("WebTransport", () => {
|
||||
it("should allow to connect with WebTransport directly", (done) => {
|
||||
setupServer({}, async ({ engine, h3Server, certificate }) => {
|
||||
@@ -154,9 +174,8 @@ describe("WebTransport", () => {
|
||||
transports: ["polling", "websocket", "webtransport"],
|
||||
},
|
||||
async ({ engine, h3Server, certificate }) => {
|
||||
const httpServer = createServer();
|
||||
const httpServer = await createHttpServer(h3Server.port);
|
||||
engine.attach(httpServer);
|
||||
httpServer.listen(h3Server.port);
|
||||
|
||||
const partialDone = createPartialDone(() => {
|
||||
httpServer.close();
|
||||
@@ -283,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(
|
||||
{
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
# History
|
||||
# Changelog
|
||||
|
||||
- [2.5.5](#255-2024-06-18) (Jun 2024)
|
||||
- [2.5.4](#254-2024-02-22) (Feb 2024)
|
||||
- [2.5.3](#253-2024-02-21) (Feb 2024)
|
||||
- [2.5.2](#252-2023-01-12) (Jan 2023)
|
||||
- [2.5.1](#251-2023-01-06) (Jan 2023)
|
||||
- [2.5.0](#250-2023-01-06) (Jan 2023)
|
||||
- [2.4.0](#240-2022-03-30) (Mar 2022)
|
||||
- [2.3.3](#233-2021-11-16) (Nov 2021)
|
||||
- [2.3.2](#232-2021-08-28) (Aug 2021)
|
||||
- [2.3.1](#231-2021-05-19) (May 2021)
|
||||
- [2.3.0](#230-2021-05-10) (May 2021)
|
||||
- [2.2.0](#220-2021-02-27) (Feb 2021)
|
||||
- [2.1.0](#210-2021-01-15) (Jan 2021)
|
||||
- [2.0.3](#203-2020-11-05) (Nov 2020)
|
||||
- [2.0.2](#202-2020-09-28) (Sep 2020)
|
||||
- [2.0.1](#201-2020-09-28) (Sep 2020)
|
||||
- [**2.0.0**](#200-2020-09-25) (Sep 2020)
|
||||
| Version | Release date |
|
||||
|------------------------------|----------------|
|
||||
| [2.5.6](#256-2025-12-23) | December 2025 |
|
||||
| [2.5.5](#255-2024-06-18) | June 2024 |
|
||||
| [2.5.4](#254-2024-02-22) | February 2024 |
|
||||
| [2.5.3](#253-2024-02-21) | February 2024 |
|
||||
| [2.5.2](#252-2023-01-12) | January 2023 |
|
||||
| [2.5.1](#251-2023-01-06) | January 2023 |
|
||||
| [2.5.0](#250-2023-01-06) | January 2023 |
|
||||
| [2.4.0](#240-2022-03-30) | March 2022 |
|
||||
| [2.3.3](#233-2021-11-16) | November 2021 |
|
||||
| [2.3.2](#232-2021-08-28) | August 2021 |
|
||||
| [2.3.1](#231-2021-05-19) | May 2021 |
|
||||
| [2.3.0](#230-2021-05-10) | May 2021 |
|
||||
| [2.2.0](#220-2021-02-27) | February 2021 |
|
||||
| [2.1.0](#210-2021-01-15) | January 2021 |
|
||||
| [2.0.3](#203-2020-11-05) | November 2020 |
|
||||
| [2.0.2](#202-2020-09-28) | September 2020 |
|
||||
| [2.0.1](#201-2020-09-28) | September 2020 |
|
||||
| [**2.0.0**](#200-2020-09-25) | September 2020 |
|
||||
|
||||
|
||||
## [2.5.6](https://github.com/socketio/socket.io/compare/socket.io-adapter@2.5.5...socket.io-adapter@2.5.6) (2025-12-23)
|
||||
|
||||
This release contains a bump of:
|
||||
|
||||
- `ws` from `~8.17.1` to `~8.18.3`
|
||||
- `debug` from `~4.3.1` to `~4.4.1`
|
||||
|
||||
|
||||
# Release notes
|
||||
|
||||
## [2.5.5](https://github.com/socketio/socket.io-adapter/compare/2.5.4...2.5.5) (2024-06-18)
|
||||
|
||||
|
||||
@@ -199,6 +199,14 @@ export abstract class ClusterAdapter extends Adapter {
|
||||
return debug("[%s] ignore message from self", this.uid);
|
||||
}
|
||||
|
||||
if (message.nsp !== this.nsp.name) {
|
||||
return debug(
|
||||
"[%s] ignore message from another namespace (%s)",
|
||||
this.uid,
|
||||
message.nsp,
|
||||
);
|
||||
}
|
||||
|
||||
debug(
|
||||
"[%s] new event of type %d from %s",
|
||||
this.uid,
|
||||
@@ -430,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -671,6 +675,8 @@ export abstract class ClusterAdapter extends Adapter {
|
||||
protected publish(
|
||||
message: DistributiveOmit<ClusterMessage, "nsp" | "uid">,
|
||||
): void {
|
||||
debug("[%s] sending message %s", this.uid, message.type);
|
||||
|
||||
this.publishAndReturnOffset(message).catch((err) => {
|
||||
debug("[%s] error while publishing message: %s", this.uid, err);
|
||||
});
|
||||
@@ -699,6 +705,14 @@ export abstract class ClusterAdapter extends Adapter {
|
||||
) {
|
||||
(response as ClusterResponse).uid = this.uid;
|
||||
(response as ClusterResponse).nsp = this.nsp.name;
|
||||
|
||||
debug(
|
||||
"[%s] sending response %s to %s",
|
||||
this.uid,
|
||||
response.type,
|
||||
requesterUid,
|
||||
);
|
||||
|
||||
this.doPublishResponse(requesterUid, response as ClusterResponse).catch(
|
||||
(err) => {
|
||||
debug("[%s] error while publishing response: %s", this.uid, err);
|
||||
@@ -790,17 +804,10 @@ export abstract class ClusterAdapterWithHeartbeat extends ClusterAdapter {
|
||||
}
|
||||
|
||||
if (message.uid && message.uid !== EMITTER_UID) {
|
||||
// we track the UID of each sender, in order to know how many servers there are in the cluster
|
||||
// we track the UID of each sender to know how many servers there are in the cluster
|
||||
this.nodesMap.set(message.uid, Date.now());
|
||||
}
|
||||
|
||||
debug(
|
||||
"[%s] new event of type %d from %s",
|
||||
this.uid,
|
||||
message.type,
|
||||
message.uid,
|
||||
);
|
||||
|
||||
switch (message.type) {
|
||||
case MessageType.INITIAL_HEARTBEAT:
|
||||
this.publish({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io-adapter",
|
||||
"version": "2.5.5",
|
||||
"version": "2.5.6",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/socketio/socket.io/tree/main/packages/socket.io-adapter#readme",
|
||||
"repository": {
|
||||
@@ -17,12 +17,12 @@
|
||||
"types": "./dist/index.d.ts",
|
||||
"description": "default socket.io in-memory adapter",
|
||||
"dependencies": {
|
||||
"debug": "~4.3.4",
|
||||
"ws": "~8.17.1"
|
||||
"debug": "~4.4.1",
|
||||
"ws": "~8.18.3"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "rimraf ./dist && tsc",
|
||||
"test": "npm run format:check && npm run compile && nyc mocha --require ts-node/register test/*.ts",
|
||||
"test": "npm run format:check && npm run compile && nyc mocha --import=tsx test/*.ts",
|
||||
"format:check": "prettier --parser typescript --check 'lib/**/*.ts' 'test/**/*.ts'",
|
||||
"format:fix": "prettier --parser typescript --write 'lib/**/*.ts' 'test/**/*.ts'",
|
||||
"prepack": "npm run compile"
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -67,11 +71,6 @@ describe("cluster adapter", () => {
|
||||
serverSockets.push(socket);
|
||||
servers.push(io);
|
||||
if (servers.length === NODES_COUNT) {
|
||||
// ensure all nodes know each other
|
||||
servers[0].emit("ping");
|
||||
servers[1].emit("ping");
|
||||
servers[2].emit("ping");
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -157,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));
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# History
|
||||
# Changelog
|
||||
|
||||
| Version | Release date | Bundle size (UMD min+gzip) |
|
||||
|-------------------------------------------------------------------------------------------------------------|----------------|----------------------------|
|
||||
| [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` |
|
||||
@@ -50,7 +52,33 @@
|
||||
| [2.1.0](#210-2018-03-29) | March 2018 | `18.7 KB` |
|
||||
|
||||
|
||||
# Release notes
|
||||
## [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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bundle** do not mangle the "_placeholder" attribute (bis) ([cdae019](https://github.com/socketio/socket.io/commit/cdae01983a8ae840fc9812875a8b88166b377c11))
|
||||
* drain queue before emitting "connect" ([#5259](https://github.com/socketio/socket.io/issues/5259)) ([d19928e](https://github.com/socketio/socket.io/commit/d19928e8d8b325310274031ed7de2ddc93ebb589)
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [`engine.io-client@~6.6.1`](https://github.com/socketio/engine.io-client/releases/tag/6.5.2) (no change)
|
||||
- [`ws@~8.17.1`](https://github.com/websockets/ws/releases/tag/8.17.1) (no change)
|
||||
|
||||
|
||||
|
||||
## [4.8.1](https://github.com/socketio/socket.io/compare/socket.io-client@4.8.0...socket.io-client@4.8.1) (2024-10-25)
|
||||
|
||||
|
||||
11
packages/socket.io-client/RELEASING.md
Normal file
11
packages/socket.io-client/RELEASING.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Releasing
|
||||
|
||||
1. Update the version in `package.json`
|
||||
2. Update the version in `support/package.esm.json`
|
||||
3. Update the changelog `CHANGELOG.md` with `conventional-changelog -p angular`
|
||||
4. Compile the TypeScript sources with `npm run compile`
|
||||
5. Generate the bundles with `npm run build`
|
||||
6. Commit the changes in `package.json`, `support/package.esm.json`, `CHANGELOG.md` and `dist/`
|
||||
7. Create the tag `socket.io-client@x.y.z` and push it to the GitHub repository. The workflow `.github/workflows/publish.yml` will safely publish the package to npm using trusted publishing.
|
||||
8. Create a new release at https://github.com/socketio/socket.io/releases
|
||||
9. Copy the bundles to the repository https://github.com/socketio/socket.io-cdn so that they are available at https://cdn.socket.io/
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
131
packages/socket.io-client/dist/socket.io.js
vendored
131
packages/socket.io-client/dist/socket.io.js
vendored
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Socket.IO v4.8.1
|
||||
* (c) 2014-2024 Guillermo Rauch
|
||||
* Socket.IO v4.8.3
|
||||
* (c) 2014-2025 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
(function (global, factory) {
|
||||
@@ -899,7 +899,7 @@
|
||||
return hostname.indexOf(":") === -1 ? hostname : "[" + hostname + "]";
|
||||
};
|
||||
_proto._port = function _port() {
|
||||
if (this.opts.port && (this.opts.secure && Number(this.opts.port !== 443) || !this.opts.secure && Number(this.opts.port) !== 80)) {
|
||||
if (this.opts.port && (this.opts.secure && Number(this.opts.port) !== 443 || !this.opts.secure && Number(this.opts.port) !== 80)) {
|
||||
return ":" + this.opts.port;
|
||||
} else {
|
||||
return "";
|
||||
@@ -2669,21 +2669,65 @@
|
||||
createDebug.namespaces = namespaces;
|
||||
createDebug.names = [];
|
||||
createDebug.skips = [];
|
||||
var i;
|
||||
var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
|
||||
var len = split.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!split[i]) {
|
||||
// ignore empty strings
|
||||
continue;
|
||||
var split = (typeof namespaces === 'string' ? namespaces : '').trim().replace(/\s+/g, ',').split(',').filter(Boolean);
|
||||
var _iterator = _createForOfIteratorHelper(split),
|
||||
_step;
|
||||
try {
|
||||
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
||||
var ns = _step.value;
|
||||
if (ns[0] === '-') {
|
||||
createDebug.skips.push(ns.slice(1));
|
||||
} else {
|
||||
createDebug.names.push(ns);
|
||||
}
|
||||
}
|
||||
namespaces = split[i].replace(/\*/g, '.*?');
|
||||
if (namespaces[0] === '-') {
|
||||
createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$'));
|
||||
} catch (err) {
|
||||
_iterator.e(err);
|
||||
} finally {
|
||||
_iterator.f();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given string matches a namespace template, honoring
|
||||
* asterisks as wildcards.
|
||||
*
|
||||
* @param {String} search
|
||||
* @param {String} template
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function matchesTemplate(search, template) {
|
||||
var searchIndex = 0;
|
||||
var templateIndex = 0;
|
||||
var starIndex = -1;
|
||||
var matchIndex = 0;
|
||||
while (searchIndex < search.length) {
|
||||
if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) {
|
||||
// Match character or proceed with wildcard
|
||||
if (template[templateIndex] === '*') {
|
||||
starIndex = templateIndex;
|
||||
matchIndex = searchIndex;
|
||||
templateIndex++; // Skip the '*'
|
||||
} else {
|
||||
searchIndex++;
|
||||
templateIndex++;
|
||||
}
|
||||
} else if (starIndex !== -1) {
|
||||
// eslint-disable-line no-negated-condition
|
||||
// Backtrack to the last '*' and try to match more characters
|
||||
templateIndex = starIndex + 1;
|
||||
matchIndex++;
|
||||
searchIndex = matchIndex;
|
||||
} else {
|
||||
createDebug.names.push(new RegExp('^' + namespaces + '$'));
|
||||
return false; // No match
|
||||
}
|
||||
}
|
||||
|
||||
// Handle trailing '*' in template
|
||||
while (templateIndex < template.length && template[templateIndex] === '*') {
|
||||
templateIndex++;
|
||||
}
|
||||
return templateIndex === template.length;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2693,7 +2737,7 @@
|
||||
* @api public
|
||||
*/
|
||||
function disable() {
|
||||
var namespaces = [].concat(_toConsumableArray(createDebug.names.map(toNamespace)), _toConsumableArray(createDebug.skips.map(toNamespace).map(function (namespace) {
|
||||
var namespaces = [].concat(_toConsumableArray(createDebug.names), _toConsumableArray(createDebug.skips.map(function (namespace) {
|
||||
return '-' + namespace;
|
||||
}))).join(',');
|
||||
createDebug.enable('');
|
||||
@@ -2708,35 +2752,37 @@
|
||||
* @api public
|
||||
*/
|
||||
function enabled(name) {
|
||||
if (name[name.length - 1] === '*') {
|
||||
return true;
|
||||
}
|
||||
var i;
|
||||
var len;
|
||||
for (i = 0, len = createDebug.skips.length; i < len; i++) {
|
||||
if (createDebug.skips[i].test(name)) {
|
||||
return false;
|
||||
var _iterator2 = _createForOfIteratorHelper(createDebug.skips),
|
||||
_step2;
|
||||
try {
|
||||
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
||||
var skip = _step2.value;
|
||||
if (matchesTemplate(name, skip)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_iterator2.e(err);
|
||||
} finally {
|
||||
_iterator2.f();
|
||||
}
|
||||
for (i = 0, len = createDebug.names.length; i < len; i++) {
|
||||
if (createDebug.names[i].test(name)) {
|
||||
return true;
|
||||
var _iterator3 = _createForOfIteratorHelper(createDebug.names),
|
||||
_step3;
|
||||
try {
|
||||
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
||||
var ns = _step3.value;
|
||||
if (matchesTemplate(name, ns)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
_iterator3.e(err);
|
||||
} finally {
|
||||
_iterator3.f();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert regexp to namespace
|
||||
*
|
||||
* @param {RegExp} regxep
|
||||
* @return {String} namespace
|
||||
* @api private
|
||||
*/
|
||||
function toNamespace(regexp) {
|
||||
return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerce `val`.
|
||||
*
|
||||
@@ -2812,15 +2858,17 @@
|
||||
if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
|
||||
return false;
|
||||
}
|
||||
var m;
|
||||
|
||||
// Is webkit? http://stackoverflow.com/a/16459606/376773
|
||||
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
|
||||
// eslint-disable-next-line no-return-assign
|
||||
return typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance ||
|
||||
// Is firebug? http://stackoverflow.com/a/398120/376773
|
||||
typeof window !== 'undefined' && window.console && (window.console.firebug || window.console.exception && window.console.table) ||
|
||||
// Is firefox >= v31?
|
||||
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
|
||||
typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 ||
|
||||
typeof navigator !== 'undefined' && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 ||
|
||||
// Double check webkit in userAgent just in case we are in a worker
|
||||
typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
|
||||
}
|
||||
@@ -2896,7 +2944,7 @@
|
||||
function load() {
|
||||
var r;
|
||||
try {
|
||||
r = exports.storage.getItem('debug');
|
||||
r = exports.storage.getItem('debug') || exports.storage.getItem('DEBUG');
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
@@ -3828,8 +3876,7 @@
|
||||
};
|
||||
args.push(function (err) {
|
||||
if (packet !== _this4._queue[0]) {
|
||||
// the packet has already been acknowledged
|
||||
return;
|
||||
return debug$2("packet [%d] already acknowledged", packet.id);
|
||||
}
|
||||
var hasError = err !== null;
|
||||
if (hasError) {
|
||||
@@ -4100,8 +4147,8 @@
|
||||
this._pid = pid; // defined only if connection state recovery is enabled
|
||||
this.connected = true;
|
||||
this.emitBuffered();
|
||||
this.emitReserved("connect");
|
||||
this._drainQueue(true);
|
||||
this.emitReserved("connect");
|
||||
}
|
||||
/**
|
||||
* Emit buffered events (received and emitted).
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
import { url } from "./url.js";
|
||||
import { Manager, ManagerOptions } from "./manager.js";
|
||||
import { Socket, SocketOptions } from "./socket.js";
|
||||
import { DisconnectDescription, Socket, SocketOptions } from "./socket.js";
|
||||
import debugModule from "debug"; // debug()
|
||||
|
||||
const debug = debugModule("socket.io-client"); // debug()
|
||||
@@ -91,6 +91,7 @@ export { protocol } from "socket.io-parser";
|
||||
*/
|
||||
|
||||
export {
|
||||
DisconnectDescription,
|
||||
Manager,
|
||||
ManagerOptions,
|
||||
Socket,
|
||||
|
||||
@@ -542,8 +542,7 @@ export class Socket<
|
||||
|
||||
args.push((err, ...responseArgs) => {
|
||||
if (packet !== this._queue[0]) {
|
||||
// the packet has already been acknowledged
|
||||
return;
|
||||
return debug("packet [%d] already acknowledged", packet.id);
|
||||
}
|
||||
const hasError = err !== null;
|
||||
if (hasError) {
|
||||
@@ -834,8 +833,8 @@ export class Socket<
|
||||
this._pid = pid; // defined only if connection state recovery is enabled
|
||||
this.connected = true;
|
||||
this.emitBuffered();
|
||||
this.emitReserved("connect");
|
||||
this._drainQueue(true);
|
||||
this.emitReserved("connect");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io-client",
|
||||
"version": "4.8.1",
|
||||
"version": "4.8.3",
|
||||
"description": "Realtime application framework client",
|
||||
"keywords": [
|
||||
"realtime",
|
||||
@@ -46,14 +46,14 @@
|
||||
"types": "./build/esm/index.d.ts",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"debug": "~4.4.1",
|
||||
"engine.io-client": "~6.6.1",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "rimraf ./build && tsc && tsc -p tsconfig.esm.json && ./postcompile.sh",
|
||||
"test": "npm run format:check && npm run compile && if test \"$BROWSERS\" = \"1\" ; then npm run test:browser; else npm run test:node; fi",
|
||||
"test:node": "mocha --require ts-node/register --require test/support/hooks.ts --exit test/index.ts",
|
||||
"test:node": "mocha --import=tsx --require test/support/hooks.ts --exit test/index.ts",
|
||||
"test:browser": "ts-node test/browser-runner.ts",
|
||||
"test:types": "tsd",
|
||||
"build": "rollup -c support/rollup.config.umd.js && rollup -c support/rollup.config.esm.js && rollup -c support/rollup.config.umd.msgpack.js",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "socket.io-client",
|
||||
"version": "4.7.5",
|
||||
"version": "4.8.3",
|
||||
"type": "module"
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ module.exports = {
|
||||
mangle: {
|
||||
properties: {
|
||||
regex: /^_/,
|
||||
reserved: ["_placeholder"],
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -110,4 +110,40 @@ describe("retry", () => {
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
it("should not emit a packet twice in the 'connect' handler", () => {
|
||||
return wrap((done) => {
|
||||
const socket = io(BASE_URL, {
|
||||
forceNew: true,
|
||||
retries: 3,
|
||||
});
|
||||
|
||||
const received: string[] = [];
|
||||
const sent: string[] = [];
|
||||
|
||||
socket.io.engine.on("packetCreate", ({ data }) => {
|
||||
sent.push(data);
|
||||
});
|
||||
|
||||
socket.io.engine.on("packet", ({ data }) => {
|
||||
received.push(data);
|
||||
});
|
||||
|
||||
socket.on("connect", () => {
|
||||
socket.emit("echo", null);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
expect(sent).to.eql(["0", '20["echo",null]']);
|
||||
|
||||
expect(received.length).to.eql(3);
|
||||
// 1: engine.io OPEN packet
|
||||
// 2: socket.io CONNECT packet
|
||||
// 3: ack packet
|
||||
expect(received[2]).to.eql("30[null]");
|
||||
|
||||
success(done, socket);
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,7 +60,7 @@ if (process.env.CI === "true") {
|
||||
{
|
||||
browserName: "internet explorer",
|
||||
browserVersion: "10",
|
||||
platformName: "Windows 7",
|
||||
platformName: "Windows 8",
|
||||
"sauce:options": BASE_SAUCE_OPTIONS,
|
||||
},
|
||||
{
|
||||
|
||||
60
packages/socket.io-cluster-adapter/CHANGELOG.md
Normal file
60
packages/socket.io-cluster-adapter/CHANGELOG.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Changelog
|
||||
|
||||
| Version | Release date |
|
||||
|--------------------------|--------------|
|
||||
| [0.3.0](#030-2025-10-16) | October 2025 |
|
||||
| [0.2.2](#022-2023-03-24) | March 2023 |
|
||||
| [0.2.1](#021-2022-10-13) | October 2022 |
|
||||
| [0.2.0](#020-2022-04-28) | April 2022 |
|
||||
| [0.1.0](#010-2021-06-22) | June 2021 |
|
||||
|
||||
|
||||
|
||||
## [0.3.0](https://github.com/socketio/socket.io-cluster-adapter/compare/0.2.2...0.3.0) (2025-10-16)
|
||||
|
||||
This release contains an important refactor of the adapter ([this commit](https://github.com/socketio/socket.io-cluster-adapter/commit/0c431243e28913fdd2a4a3de3e67a9f38d67a3aa)), as most of the logic has been moved in the `ClusterAdapter` class of the `socket.io-adapter` package.
|
||||
|
||||
Besides, the `@socket.io/cluster-adapter` package is now part of the `socket.io` monorepo.
|
||||
|
||||
|
||||
|
||||
## [0.2.2](https://github.com/socketio/socket.io-cluster-adapter/compare/0.2.1...0.2.2) (2023-03-24)
|
||||
|
||||
The `socket.io-adapter` package was added to the list of `peerDependencies`, in order to fix sync issues with the version imported by the socket.io package (see [15fd56e](https://github.com/socketio/socket.io-cluster-adapter/commit/15fd56e78d52aa65c5fbf412dec57ab4bdaee7cc)).
|
||||
|
||||
Support for connection state recovery (see [here](https://github.com/socketio/socket.io/releases/4.6.0)) will be added in the next release.
|
||||
|
||||
|
||||
|
||||
## [0.2.1](https://github.com/socketio/socket.io-cluster-adapter/compare/0.2.0...0.2.1) (2022-10-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* properly handle ERR_IPC_CHANNEL_CLOSED errors ([#6](https://github.com/socketio/socket.io-cluster-adapter/issues/6)) ([be0a0e3](https://github.com/socketio/socket.io-cluster-adapter/commit/be0a0e3217bd7100d569e5624194612bcc8b96ff))
|
||||
|
||||
|
||||
|
||||
## [0.2.0](https://github.com/socketio/socket.io-cluster-adapter/compare/0.1.0...0.2.0) (2022-04-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* broadcast and expect multiple acks ([055b784](https://github.com/socketio/socket.io-cluster-adapter/commit/055b7840d8cf88173d8299041ef3fafa9791c97a))
|
||||
|
||||
This feature was added in `socket.io@4.5.0`:
|
||||
|
||||
```js
|
||||
io.timeout(1000).emit("some-event", (err, responses) => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Thanks to this change, it will now work within a Node.js cluster.
|
||||
|
||||
|
||||
|
||||
## 0.1.0 (2021-06-22)
|
||||
|
||||
Initial commit
|
||||
|
||||
7
packages/socket.io-cluster-adapter/LICENSE
Normal file
7
packages/socket.io-cluster-adapter/LICENSE
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2021 Damien Arrachequesne (@darrachequesne)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
100
packages/socket.io-cluster-adapter/README.md
Normal file
100
packages/socket.io-cluster-adapter/README.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Socket.IO cluster adapter
|
||||
|
||||
The `@socket.io/cluster-adapter` package allows broadcasting packets between multiple Socket.IO servers.
|
||||
|
||||

|
||||
|
||||
It can be used in conjunction with [`@socket.io/sticky`](https://github.com/socketio/socket.io-sticky) to broadcast packets between the workers of the same Node.js [cluster](https://nodejs.org/api/cluster.html).
|
||||
|
||||
Supported features:
|
||||
|
||||
- [broadcasting](https://socket.io/docs/v4/broadcasting-events/)
|
||||
- [utility methods](https://socket.io/docs/v4/server-instance/#Utility-methods)
|
||||
- [`socketsJoin`](https://socket.io/docs/v4/server-instance/#socketsJoin)
|
||||
- [`socketsLeave`](https://socket.io/docs/v4/server-instance/#socketsLeave)
|
||||
- [`disconnectSockets`](https://socket.io/docs/v4/server-instance/#disconnectSockets)
|
||||
- [`fetchSockets`](https://socket.io/docs/v4/server-instance/#fetchSockets)
|
||||
- [`serverSideEmit`](https://socket.io/docs/v4/server-instance/#serverSideEmit)
|
||||
|
||||
Related packages:
|
||||
|
||||
- Postgres adapter: https://github.com/socketio/socket.io-postgres-adapter/
|
||||
- Redis adapter: https://github.com/socketio/socket.io-redis-adapter/
|
||||
- MongoDB adapter: https://github.com/socketio/socket.io-mongo-adapter/
|
||||
|
||||
**Table of contents**
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [License](#license)
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
npm install @socket.io/cluster-adapter
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
const cluster = require("cluster");
|
||||
const http = require("http");
|
||||
const { Server } = require("socket.io");
|
||||
const numCPUs = require("os").cpus().length;
|
||||
const { setupMaster, setupWorker } = require("@socket.io/sticky");
|
||||
const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter");
|
||||
|
||||
if (cluster.isMaster) {
|
||||
console.log(`Master ${process.pid} is running`);
|
||||
|
||||
const httpServer = http.createServer();
|
||||
|
||||
// setup sticky sessions
|
||||
setupMaster(httpServer, {
|
||||
loadBalancingMethod: "least-connection",
|
||||
});
|
||||
|
||||
// setup connections between the workers
|
||||
setupPrimary();
|
||||
|
||||
// needed for packets containing buffers (you can ignore it if you only send plaintext objects)
|
||||
// Node.js < 16.0.0
|
||||
cluster.setupMaster({
|
||||
serialization: "advanced",
|
||||
});
|
||||
// Node.js > 16.0.0
|
||||
// cluster.setupPrimary({
|
||||
// serialization: "advanced",
|
||||
// });
|
||||
|
||||
httpServer.listen(3000);
|
||||
|
||||
for (let i = 0; i < numCPUs; i++) {
|
||||
cluster.fork();
|
||||
}
|
||||
|
||||
cluster.on("exit", (worker) => {
|
||||
console.log(`Worker ${worker.process.pid} died`);
|
||||
cluster.fork();
|
||||
});
|
||||
} else {
|
||||
console.log(`Worker ${process.pid} started`);
|
||||
|
||||
const httpServer = http.createServer();
|
||||
const io = new Server(httpServer);
|
||||
|
||||
// use the cluster adapter
|
||||
io.adapter(createAdapter());
|
||||
|
||||
// setup connection with the primary process
|
||||
setupWorker(io);
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
/* ... */
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
822
packages/socket.io-cluster-adapter/assets/adapter.excalidraw
Normal file
822
packages/socket.io-cluster-adapter/assets/adapter.excalidraw
Normal file
@@ -0,0 +1,822 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
"elements": [
|
||||
{
|
||||
"type": "text",
|
||||
"version": 345,
|
||||
"versionNonce": 1782313961,
|
||||
"isDeleted": false,
|
||||
"id": "5hUB5ALUlsn26W0PzU4fM",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 777,
|
||||
"y": -89.5,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 78,
|
||||
"height": 26,
|
||||
"seed": 28708370,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [
|
||||
"_wBO22vaQplcoKyBXbWRC"
|
||||
],
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"text": "worker 1",
|
||||
"baseline": 18,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle"
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 230,
|
||||
"versionNonce": 1587305255,
|
||||
"isDeleted": false,
|
||||
"id": "lmQ4o4New7xuXQLwavuSn",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 725,
|
||||
"y": -169,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 345.00000000000006,
|
||||
"height": 311,
|
||||
"seed": 1594950354,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [
|
||||
"_wBO22vaQplcoKyBXbWRC",
|
||||
"BZVwnsrGk9G-X87ZHkh-6",
|
||||
"eU1gfEXnHSjxc-pEgv43A",
|
||||
"XZpY0rnxgeDlxu5b8fgRQ",
|
||||
"4mjxZzapHnLuRx7KU2JeH",
|
||||
"mV8ZNfAcYrxGLJ7b9a_kn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 152,
|
||||
"versionNonce": 742887113,
|
||||
"isDeleted": false,
|
||||
"id": "ZQsZmj4NaTubBHMkVG2dl",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 745,
|
||||
"y": -159,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 43,
|
||||
"height": 26,
|
||||
"seed": 126533902,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [],
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"text": "Host",
|
||||
"baseline": 18,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top"
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 334,
|
||||
"versionNonce": 1221877319,
|
||||
"isDeleted": false,
|
||||
"id": "RRrk3Vsl-pM8Z1r8Fj3Vu",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 749.5,
|
||||
"y": -105,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 129,
|
||||
"height": 56,
|
||||
"seed": 1013161166,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [
|
||||
"use4Bp2hbb77Fq5njtwBi",
|
||||
"U7UCkn3nVHlWGYetjII_Z"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 386,
|
||||
"versionNonce": 601673129,
|
||||
"isDeleted": false,
|
||||
"id": "qfQdcJHnwYnCMtLCV51X8",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 773,
|
||||
"y": -18.5,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 90,
|
||||
"height": 26,
|
||||
"seed": 1535426147,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [
|
||||
"_wBO22vaQplcoKyBXbWRC",
|
||||
"2DIFacJXJtC5QIuMuo3pK"
|
||||
],
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"text": "worker 2",
|
||||
"baseline": 18,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle"
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 330,
|
||||
"versionNonce": 794754407,
|
||||
"isDeleted": false,
|
||||
"id": "IRd1nPQbv0PQdJQn_yLOs",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 749.5,
|
||||
"y": -36,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 129,
|
||||
"height": 56,
|
||||
"seed": 452398413,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [
|
||||
"2DIFacJXJtC5QIuMuo3pK",
|
||||
"NhqDM6wVMhgbRvXrQJQge",
|
||||
"C1IueNJdiTSkqUxSTEvKe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 374,
|
||||
"versionNonce": 942092425,
|
||||
"isDeleted": false,
|
||||
"id": "ENOSqQ4visNbCN7ZMZwxP",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 770.5,
|
||||
"y": 48.5,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 89,
|
||||
"height": 26,
|
||||
"seed": 1916984429,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [
|
||||
"_wBO22vaQplcoKyBXbWRC"
|
||||
],
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"text": "worker 3",
|
||||
"baseline": 18,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle"
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 314,
|
||||
"versionNonce": 629717127,
|
||||
"isDeleted": false,
|
||||
"id": "IqdB8EO7s50UY1EU9TVVP",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 750.5,
|
||||
"y": 30,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 129,
|
||||
"height": 56,
|
||||
"seed": 1832463587,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [
|
||||
"NhqDM6wVMhgbRvXrQJQge"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 115,
|
||||
"versionNonce": 997860361,
|
||||
"isDeleted": false,
|
||||
"id": "9grXh8d6z3-WENQLWSBP6",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 338,
|
||||
"y": -95,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 140,
|
||||
"height": 49,
|
||||
"seed": 1667334019,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 95,
|
||||
"versionNonce": 1806744839,
|
||||
"isDeleted": false,
|
||||
"id": "uw4DcwoucyYZxQuvW-XdC",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 383,
|
||||
"y": -83.5,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 50,
|
||||
"height": 26,
|
||||
"seed": 1216901411,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [],
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"text": "client",
|
||||
"baseline": 18,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle"
|
||||
},
|
||||
{
|
||||
"id": "ZR4mzBF0WDviz0UhZ4jZM",
|
||||
"type": "diamond",
|
||||
"x": 891.5,
|
||||
"y": -87,
|
||||
"width": 35,
|
||||
"height": 18,
|
||||
"angle": 0,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"seed": 1951590473,
|
||||
"version": 87,
|
||||
"versionNonce": 232917705,
|
||||
"isDeleted": false,
|
||||
"boundElementIds": [
|
||||
"LgoBfF5uxSyzlQnWpzp21",
|
||||
"VzzoutsEZMxQbMloZdWpR",
|
||||
"OEzsLqAW3F-2LE0bnPrtx"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "tpect-oo26kMVbG_xdqoO",
|
||||
"type": "text",
|
||||
"x": 895.5,
|
||||
"y": -130,
|
||||
"width": 157,
|
||||
"height": 26,
|
||||
"angle": 0,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"seed": 294816809,
|
||||
"version": 104,
|
||||
"versionNonce": 2120602793,
|
||||
"isDeleted": false,
|
||||
"boundElementIds": null,
|
||||
"text": "cluster adapter",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top",
|
||||
"baseline": 18
|
||||
},
|
||||
{
|
||||
"type": "diamond",
|
||||
"version": 113,
|
||||
"versionNonce": 1690814889,
|
||||
"isDeleted": false,
|
||||
"id": "ScoFMjrxukGD1efHfKiFH",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 894.5,
|
||||
"y": -20,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 35,
|
||||
"height": 18,
|
||||
"seed": 696687431,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [
|
||||
"VzzoutsEZMxQbMloZdWpR"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "diamond",
|
||||
"version": 158,
|
||||
"versionNonce": 1317846759,
|
||||
"isDeleted": false,
|
||||
"id": "wj2HZ2scg4U6UtpfK8x3e",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 893.5,
|
||||
"y": 46,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 35,
|
||||
"height": 18,
|
||||
"seed": 1469547015,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [
|
||||
"OEzsLqAW3F-2LE0bnPrtx"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "U7UCkn3nVHlWGYetjII_Z",
|
||||
"type": "arrow",
|
||||
"x": 734.5,
|
||||
"y": -74,
|
||||
"width": 238,
|
||||
"height": 4,
|
||||
"angle": 0,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"seed": 1537057031,
|
||||
"version": 61,
|
||||
"versionNonce": 1166788937,
|
||||
"isDeleted": false,
|
||||
"boundElementIds": null,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-238,
|
||||
4
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "RRrk3Vsl-pM8Z1r8Fj3Vu",
|
||||
"focus": -0.05720889916209189,
|
||||
"gap": 15
|
||||
},
|
||||
"endBinding": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 163,
|
||||
"versionNonce": 15141831,
|
||||
"isDeleted": false,
|
||||
"id": "mv6FEWy7Oux1XBpOeYKlJ",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 337.25,
|
||||
"y": -26.5,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 140,
|
||||
"height": 49,
|
||||
"seed": 1927924585,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 143,
|
||||
"versionNonce": 766883881,
|
||||
"isDeleted": false,
|
||||
"id": "sBk2P5AAiZa36HbnmeRLi",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 382.25,
|
||||
"y": -15,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 50,
|
||||
"height": 26,
|
||||
"seed": 1643756455,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [],
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"text": "client",
|
||||
"baseline": 18,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"version": 111,
|
||||
"versionNonce": 2076179175,
|
||||
"isDeleted": false,
|
||||
"id": "C1IueNJdiTSkqUxSTEvKe",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 733.75,
|
||||
"y": -5.5,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 238,
|
||||
"height": 4,
|
||||
"seed": 542013001,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"boundElementIds": [],
|
||||
"startBinding": {
|
||||
"elementId": "IRd1nPQbv0PQdJQn_yLOs",
|
||||
"focus": -0.03958393527882115,
|
||||
"gap": 15.75
|
||||
},
|
||||
"endBinding": null,
|
||||
"lastCommittedPoint": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-238,
|
||||
4
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 138,
|
||||
"versionNonce": 478071561,
|
||||
"isDeleted": false,
|
||||
"id": "6r_-m_ehoDx3FlKI9YqbJ",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 337.25,
|
||||
"y": 40.5,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 140,
|
||||
"height": 49,
|
||||
"seed": 704870919,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": []
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 118,
|
||||
"versionNonce": 133039623,
|
||||
"isDeleted": false,
|
||||
"id": "EEhePeT66oko7kRekPQGo",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 382.25,
|
||||
"y": 52,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 50,
|
||||
"height": 26,
|
||||
"seed": 93212137,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [],
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"text": "client",
|
||||
"baseline": 18,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"version": 86,
|
||||
"versionNonce": 979534313,
|
||||
"isDeleted": false,
|
||||
"id": "etOb8BtJX8fUgxXsPOX2F",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 733.75,
|
||||
"y": 61.5,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 238,
|
||||
"height": 4,
|
||||
"seed": 464822567,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"boundElementIds": [],
|
||||
"startBinding": null,
|
||||
"endBinding": null,
|
||||
"lastCommittedPoint": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-238,
|
||||
4
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 184,
|
||||
"versionNonce": 1953344807,
|
||||
"isDeleted": false,
|
||||
"id": "G6-PJf8TRngnFyuC3A0QC",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 337.25,
|
||||
"y": 105.5,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 140,
|
||||
"height": 49,
|
||||
"seed": 1627945223,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [
|
||||
"JFhaNprFAz7gl6FY48Aps"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 163,
|
||||
"versionNonce": 342645961,
|
||||
"isDeleted": false,
|
||||
"id": "UEv3oUzBr56Mj6Mz7hG-B",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 382.25,
|
||||
"y": 117,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 50,
|
||||
"height": 26,
|
||||
"seed": 1822211817,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [],
|
||||
"fontSize": 20,
|
||||
"fontFamily": 1,
|
||||
"text": "client",
|
||||
"baseline": 18,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"version": 135,
|
||||
"versionNonce": 2091610183,
|
||||
"isDeleted": false,
|
||||
"id": "JFhaNprFAz7gl6FY48Aps",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 731.1770639400929,
|
||||
"y": 74.303452225402,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 240.42706394009292,
|
||||
"height": 54.196547774598,
|
||||
"seed": 1203879975,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"boundElementIds": [],
|
||||
"startBinding": null,
|
||||
"endBinding": {
|
||||
"elementId": "G6-PJf8TRngnFyuC3A0QC",
|
||||
"focus": 0.4300574064368146,
|
||||
"gap": 13.5
|
||||
},
|
||||
"lastCommittedPoint": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-240.42706394009292,
|
||||
54.196547774598
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "VzzoutsEZMxQbMloZdWpR",
|
||||
"type": "arrow",
|
||||
"x": 929.5,
|
||||
"y": -70,
|
||||
"width": 17,
|
||||
"height": 53,
|
||||
"angle": 0,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"seed": 1957283401,
|
||||
"version": 330,
|
||||
"versionNonce": 1093640775,
|
||||
"isDeleted": false,
|
||||
"boundElementIds": null,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
16,
|
||||
23
|
||||
],
|
||||
[
|
||||
-1,
|
||||
53
|
||||
]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startBinding": {
|
||||
"elementId": "ZR4mzBF0WDviz0UhZ4jZM",
|
||||
"focus": -0.853416149068323,
|
||||
"gap": 8.48634645640746
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "ScoFMjrxukGD1efHfKiFH",
|
||||
"focus": 0.7485714285714284,
|
||||
"gap": 4.878378801288122
|
||||
},
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"version": 440,
|
||||
"versionNonce": 2038871081,
|
||||
"isDeleted": false,
|
||||
"id": "OEzsLqAW3F-2LE0bnPrtx",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 930.5,
|
||||
"y": -71,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 42,
|
||||
"height": 116,
|
||||
"seed": 1990607913,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"boundElementIds": [],
|
||||
"startBinding": {
|
||||
"elementId": "ZR4mzBF0WDviz0UhZ4jZM",
|
||||
"focus": -0.8894409937888198,
|
||||
"gap": 8.054406666710076
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "wj2HZ2scg4U6UtpfK8x3e",
|
||||
"focus": 0.6,
|
||||
"gap": 8.435530010560711
|
||||
},
|
||||
"lastCommittedPoint": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow",
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
39,
|
||||
46
|
||||
],
|
||||
[
|
||||
-3,
|
||||
116
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": null,
|
||||
"viewBackgroundColor": "#ffffff"
|
||||
}
|
||||
}
|
||||
BIN
packages/socket.io-cluster-adapter/assets/adapter.png
Normal file
BIN
packages/socket.io-cluster-adapter/assets/adapter.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
116
packages/socket.io-cluster-adapter/lib/index.ts
Normal file
116
packages/socket.io-cluster-adapter/lib/index.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import cluster from "node:cluster";
|
||||
import {
|
||||
ClusterAdapterWithHeartbeat,
|
||||
ClusterAdapterOptions,
|
||||
ClusterMessage,
|
||||
ServerId,
|
||||
ClusterResponse,
|
||||
MessageType,
|
||||
} from "socket.io-adapter";
|
||||
import debugModule from "debug";
|
||||
|
||||
const debug = debugModule("socket.io-cluster-adapter");
|
||||
const MESSAGE_SOURCE = "_sio_adapter";
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
function ignoreError() {}
|
||||
|
||||
/**
|
||||
* Returns a function that will create a NodeClusterAdapter instance.
|
||||
*
|
||||
* @param opts - additional options
|
||||
*
|
||||
* @public
|
||||
* @see https://nodejs.org/api/cluster.html
|
||||
*/
|
||||
export function createAdapter(opts: Partial<ClusterAdapterOptions> = {}) {
|
||||
return function (nsp: any) {
|
||||
return new NodeClusterAdapter(nsp, opts);
|
||||
};
|
||||
}
|
||||
|
||||
export class NodeClusterAdapter extends ClusterAdapterWithHeartbeat {
|
||||
constructor(nsp: any, opts: ClusterAdapterOptions = {}) {
|
||||
super(nsp, opts);
|
||||
process.on("message", (message: any) => {
|
||||
const isValidSource = message?.source === MESSAGE_SOURCE;
|
||||
if (!isValidSource) {
|
||||
debug("[%s] ignore unknown source", this.uid);
|
||||
return;
|
||||
}
|
||||
|
||||
// note: this check should be done in the onMessage() handler
|
||||
if (message.nsp !== this.nsp.name) {
|
||||
debug("[%s] ignore other namespace", this.uid);
|
||||
return;
|
||||
}
|
||||
|
||||
this.onMessage(message);
|
||||
});
|
||||
|
||||
// until https://github.com/socketio/socket.io/commit/f3e1f5ebdf59158d0c8d1e20f8230275617fb355 is released
|
||||
this.init();
|
||||
}
|
||||
|
||||
protected override doPublish(message: ClusterMessage & { source: string }) {
|
||||
message.source = MESSAGE_SOURCE;
|
||||
|
||||
process.send(message, null, {}, ignoreError);
|
||||
|
||||
return Promise.resolve(""); // connection state recovery is not supported
|
||||
}
|
||||
|
||||
protected override doPublishResponse(
|
||||
requesterUid: ServerId,
|
||||
response: ClusterResponse & { source: string; requesterUid: string },
|
||||
) {
|
||||
response.source = MESSAGE_SOURCE;
|
||||
response.requesterUid = requesterUid;
|
||||
|
||||
process.send(response, null, {}, ignoreError);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
const UIDS = Symbol("uids");
|
||||
|
||||
export function setupPrimary() {
|
||||
cluster.on("message", (worker, message) => {
|
||||
const isValidSource = message?.source === MESSAGE_SOURCE;
|
||||
if (!isValidSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
// store the requester's uids (one per namespace) so that the response can be sent specifically to them
|
||||
worker[UIDS] = worker[UIDS] || new Set();
|
||||
worker[UIDS].add(message.uid);
|
||||
|
||||
switch (message.type) {
|
||||
case MessageType.FETCH_SOCKETS_RESPONSE:
|
||||
case MessageType.SERVER_SIDE_EMIT_RESPONSE:
|
||||
const requesterUid = message.requesterUid;
|
||||
for (const workerId in cluster.workers) {
|
||||
if (
|
||||
hasOwnProperty.call(cluster.workers, workerId) &&
|
||||
cluster.workers[workerId][UIDS]?.has(requesterUid)
|
||||
) {
|
||||
cluster.workers[workerId].send(message, null, ignoreError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
const emitterIdAsString = String(worker.id);
|
||||
// emit to all workers but the requester
|
||||
for (const workerId in cluster.workers) {
|
||||
if (
|
||||
hasOwnProperty.call(cluster.workers, workerId) &&
|
||||
workerId !== emitterIdAsString
|
||||
) {
|
||||
cluster.workers[workerId].send(message, null, ignoreError);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
3345
packages/socket.io-cluster-adapter/package-lock.json
generated
Normal file
3345
packages/socket.io-cluster-adapter/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
40
packages/socket.io-cluster-adapter/package.json
Normal file
40
packages/socket.io-cluster-adapter/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@socket.io/cluster-adapter",
|
||||
"version": "0.3.0",
|
||||
"description": "The Socket.IO cluster adapter, allowing to broadcast events between several Socket.IO servers",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/socketio/socket.io/tree/main/packages/socket.io-cluster-adapter#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/socketio/socket.io.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/socketio/socket.io/issues"
|
||||
},
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"compile": "rimraf ./dist && tsc",
|
||||
"test": "npm run format:check && npm run compile && nyc mocha --import=tsx test/index.ts",
|
||||
"format:check": "prettier --check \"lib/**/*.ts\" \"test/**/*.ts\"",
|
||||
"format:fix": "prettier --write \"lib/**/*.ts\" \"test/**/*.ts\"",
|
||||
"prepack": "npm run compile"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "~4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"socket.io-adapter": "~2.5.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"socket.io",
|
||||
"cluster",
|
||||
"adapter"
|
||||
]
|
||||
}
|
||||
372
packages/socket.io-cluster-adapter/test/index.ts
Normal file
372
packages/socket.io-cluster-adapter/test/index.ts
Normal file
@@ -0,0 +1,372 @@
|
||||
import { io as ioc, Socket as ClientSocket } from "socket.io-client";
|
||||
import expect = require("expect.js");
|
||||
import { setupPrimary } from "..";
|
||||
import { times, sleep } from "./util";
|
||||
import cluster, { Worker } from "node:cluster";
|
||||
|
||||
const NODES_COUNT = 3;
|
||||
|
||||
cluster.setupMaster({
|
||||
exec: "./test/worker.js",
|
||||
// @ts-ignore
|
||||
serialization: "advanced", // needed for packets containing buffers
|
||||
});
|
||||
|
||||
setupPrimary();
|
||||
|
||||
const getRooms = (worker): Promise<Set<string>> => {
|
||||
worker.send("get rooms");
|
||||
return new Promise((resolve) => {
|
||||
worker.once("message", (content) => {
|
||||
resolve(content);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
describe("@socket.io/cluster-adapter", () => {
|
||||
let clientSockets: ClientSocket[], workers: Worker[];
|
||||
|
||||
beforeEach((done) => {
|
||||
clientSockets = [];
|
||||
workers = [];
|
||||
|
||||
for (let i = 1; i <= NODES_COUNT; i++) {
|
||||
const PORT = 40000 + i;
|
||||
const worker = cluster.fork({
|
||||
PORT,
|
||||
});
|
||||
|
||||
worker.on("listening", () => {
|
||||
const clientSocket = ioc(`http://localhost:${PORT}`);
|
||||
|
||||
clientSocket.on("connect", async () => {
|
||||
workers.push(worker);
|
||||
clientSockets.push(clientSocket);
|
||||
if (clientSockets.length === NODES_COUNT) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
for (const id in cluster.workers) {
|
||||
cluster.workers[id].kill();
|
||||
}
|
||||
clientSockets.forEach((socket) => {
|
||||
socket.disconnect();
|
||||
});
|
||||
});
|
||||
|
||||
describe("broadcast", function () {
|
||||
it("broadcasts to all clients", (done) => {
|
||||
const partialDone = times(3, done);
|
||||
|
||||
clientSockets.forEach((clientSocket) => {
|
||||
clientSocket.on("test", (arg1, arg2, arg3) => {
|
||||
expect(arg1).to.eql(1);
|
||||
expect(arg2).to.eql("2");
|
||||
expect(Buffer.isBuffer(arg3)).to.be(true);
|
||||
partialDone();
|
||||
});
|
||||
});
|
||||
|
||||
workers[0].send("broadcasts to all clients");
|
||||
});
|
||||
|
||||
it("broadcasts to all clients in a namespace", (done) => {
|
||||
const partialDone = times(3, done);
|
||||
|
||||
const onConnect = times(3, async () => {
|
||||
workers[0].send("broadcasts to all clients in a namespace");
|
||||
});
|
||||
|
||||
clientSockets.forEach((clientSocket) => {
|
||||
const socket = clientSocket.io.socket("/custom");
|
||||
socket.on("connect", onConnect);
|
||||
socket.on("test", () => {
|
||||
socket.disconnect();
|
||||
partialDone();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("broadcasts to all clients in a room", (done) => {
|
||||
workers[1].send("join room1");
|
||||
|
||||
clientSockets[0].on("test", () => {
|
||||
done(new Error("should not happen"));
|
||||
});
|
||||
|
||||
clientSockets[1].on("test", () => {
|
||||
done();
|
||||
});
|
||||
|
||||
clientSockets[2].on("test", () => {
|
||||
done(new Error("should not happen"));
|
||||
});
|
||||
|
||||
workers[0].send("broadcasts to all clients in a room");
|
||||
});
|
||||
|
||||
it("broadcasts to all clients except in room", (done) => {
|
||||
const partialDone = times(2, done);
|
||||
workers[1].send("join room1");
|
||||
|
||||
clientSockets[0].on("test", () => {
|
||||
partialDone();
|
||||
});
|
||||
|
||||
clientSockets[1].on("test", () => {
|
||||
done(new Error("should not happen"));
|
||||
});
|
||||
|
||||
clientSockets[2].on("test", () => {
|
||||
partialDone();
|
||||
});
|
||||
|
||||
workers[0].send("broadcasts to all clients except in room");
|
||||
});
|
||||
|
||||
it("broadcasts to local clients only", (done) => {
|
||||
clientSockets[0].on("test", () => {
|
||||
done();
|
||||
});
|
||||
|
||||
clientSockets[1].on("test", () => {
|
||||
done(new Error("should not happen"));
|
||||
});
|
||||
|
||||
clientSockets[2].on("test", () => {
|
||||
done(new Error("should not happen"));
|
||||
});
|
||||
|
||||
workers[0].send("broadcasts to local clients only");
|
||||
});
|
||||
|
||||
it("broadcasts with multiple acknowledgements", (done) => {
|
||||
clientSockets[0].on("test", (cb) => {
|
||||
cb(1);
|
||||
});
|
||||
|
||||
clientSockets[1].on("test", (cb) => {
|
||||
cb(2);
|
||||
});
|
||||
|
||||
clientSockets[2].on("test", (cb) => {
|
||||
cb(3);
|
||||
});
|
||||
|
||||
workers[0].send("broadcasts with multiple acknowledgements");
|
||||
|
||||
workers[0].on("message", (result) => {
|
||||
if (result === "ok") {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("broadcasts with multiple acknowledgements (binary content)", (done) => {
|
||||
clientSockets[0].on("test", (cb) => {
|
||||
cb(Buffer.from([1]));
|
||||
});
|
||||
|
||||
clientSockets[1].on("test", (cb) => {
|
||||
cb(Buffer.from([2]));
|
||||
});
|
||||
|
||||
clientSockets[2].on("test", (cb) => {
|
||||
cb(Buffer.from([3]));
|
||||
});
|
||||
|
||||
workers[0].send(
|
||||
"broadcasts with multiple acknowledgements (binary content)",
|
||||
);
|
||||
|
||||
workers[0].on("message", (result) => {
|
||||
if (result === "ok") {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("broadcasts with multiple acknowledgements (no client)", (done) => {
|
||||
workers[0].send("broadcasts with multiple acknowledgements (no client)");
|
||||
|
||||
workers[0].on("message", (result) => {
|
||||
if (result === "ok") {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("broadcasts with multiple acknowledgements (timeout)", (done) => {
|
||||
clientSockets[0].on("test", (cb) => {
|
||||
cb(1);
|
||||
});
|
||||
|
||||
clientSockets[1].on("test", (cb) => {
|
||||
cb(2);
|
||||
});
|
||||
|
||||
clientSockets[2].on("test", (cb) => {
|
||||
// do nothing
|
||||
});
|
||||
|
||||
workers[0].send("broadcasts with multiple acknowledgements (timeout)");
|
||||
|
||||
workers[0].on("message", (result) => {
|
||||
if (result === "ok") {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("socketsJoin", () => {
|
||||
it("makes all socket instances join the specified room", async () => {
|
||||
workers[0].send("makes all socket instances join the specified room");
|
||||
|
||||
await sleep(100);
|
||||
|
||||
expect((await getRooms(workers[0])).has("room1")).to.be(true);
|
||||
expect((await getRooms(workers[1])).has("room1")).to.be(true);
|
||||
expect((await getRooms(workers[2])).has("room1")).to.be(true);
|
||||
});
|
||||
|
||||
it("makes the matching socket instances join the specified room", async () => {
|
||||
workers[0].send("join room1");
|
||||
workers[2].send("join room1");
|
||||
|
||||
workers[0].send(
|
||||
"makes the matching socket instances join the specified room",
|
||||
);
|
||||
|
||||
await sleep(100);
|
||||
|
||||
expect((await getRooms(workers[0])).has("room2")).to.be(true);
|
||||
expect((await getRooms(workers[1])).has("room2")).to.be(false);
|
||||
expect((await getRooms(workers[2])).has("room2")).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("socketsLeave", () => {
|
||||
it("makes all socket instances leave the specified room", async () => {
|
||||
workers[0].send("join room1");
|
||||
workers[2].send("join room1");
|
||||
|
||||
workers[0].send("makes all socket instances leave the specified room");
|
||||
|
||||
await sleep(100);
|
||||
|
||||
expect((await getRooms(workers[0])).has("room1")).to.be(false);
|
||||
expect((await getRooms(workers[1])).has("room1")).to.be(false);
|
||||
expect((await getRooms(workers[2])).has("room1")).to.be(false);
|
||||
});
|
||||
|
||||
it("makes the matching socket instances leave the specified room", async () => {
|
||||
workers[0].send("join room1 & room2");
|
||||
workers[2].send("join room2");
|
||||
|
||||
workers[0].send(
|
||||
"makes the matching socket instances leave the specified room",
|
||||
);
|
||||
|
||||
await sleep(100);
|
||||
|
||||
expect((await getRooms(workers[0])).has("room2")).to.be(false);
|
||||
expect((await getRooms(workers[1])).has("room2")).to.be(false);
|
||||
expect((await getRooms(workers[2])).has("room2")).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("disconnectSockets", () => {
|
||||
it("makes all socket instances disconnect", (done) => {
|
||||
const partialDone = times(3, done);
|
||||
|
||||
clientSockets.forEach((clientSocket) => {
|
||||
clientSocket.on("disconnect", (reason) => {
|
||||
expect(reason).to.eql("io server disconnect");
|
||||
partialDone();
|
||||
});
|
||||
});
|
||||
|
||||
workers[0].send("makes all socket instances disconnect");
|
||||
});
|
||||
});
|
||||
|
||||
describe("fetchSockets", () => {
|
||||
it("returns all socket instances", (done) => {
|
||||
workers[0].send("returns all socket instances");
|
||||
|
||||
workers[0].on("message", (result) => {
|
||||
if (result === "ok") {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("serverSideEmit", () => {
|
||||
it("sends an event to other server instances", (done) => {
|
||||
const partialDone = times(2, done);
|
||||
|
||||
workers[0].send("sends an event to other server instances");
|
||||
|
||||
workers[0].on("message", (result) => {
|
||||
if (result === "ok") {
|
||||
done(new Error("should not happen"));
|
||||
}
|
||||
});
|
||||
|
||||
workers[1].on("message", (result) => {
|
||||
expect(result).to.eql("ok");
|
||||
partialDone();
|
||||
});
|
||||
|
||||
workers[2].on("message", (result) => {
|
||||
expect(result).to.eql("ok");
|
||||
partialDone();
|
||||
});
|
||||
});
|
||||
|
||||
it("sends an event and receives a response from the other server instances", (done) => {
|
||||
workers[0].send(
|
||||
"sends an event and receives a response from the other server instances (1)",
|
||||
);
|
||||
workers[1].send(
|
||||
"sends an event and receives a response from the other server instances (2)",
|
||||
);
|
||||
workers[2].send(
|
||||
"sends an event and receives a response from the other server instances (3)",
|
||||
);
|
||||
|
||||
workers[0].on("message", (result) => {
|
||||
if (result === "ok") {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("sends an event but timeout if one server does not respond", function (done) {
|
||||
this.timeout(6000); // currently not possible to configure the timeout delay
|
||||
|
||||
workers[0].send(
|
||||
"sends an event but timeout if one server does not respond (1)",
|
||||
);
|
||||
workers[1].send(
|
||||
"sends an event but timeout if one server does not respond (2)",
|
||||
);
|
||||
workers[2].send(
|
||||
"sends an event but timeout if one server does not respond (3)",
|
||||
);
|
||||
|
||||
workers[0].on("message", (result) => {
|
||||
if (result === "ok") {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
13
packages/socket.io-cluster-adapter/test/util.ts
Normal file
13
packages/socket.io-cluster-adapter/test/util.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export function times(count: number, fn: () => void) {
|
||||
let i = 0;
|
||||
return () => {
|
||||
i++;
|
||||
if (i === count) {
|
||||
fn();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function sleep(duration: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, duration));
|
||||
}
|
||||
182
packages/socket.io-cluster-adapter/test/worker.js
Normal file
182
packages/socket.io-cluster-adapter/test/worker.js
Normal file
@@ -0,0 +1,182 @@
|
||||
const { createServer } = require("http");
|
||||
const { Server } = require("socket.io");
|
||||
const { createAdapter } = require("..");
|
||||
|
||||
const httpServer = createServer();
|
||||
const io = new Server(httpServer);
|
||||
const expect = require("expect.js");
|
||||
|
||||
io.adapter(createAdapter());
|
||||
|
||||
let serverSocket;
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
serverSocket = socket;
|
||||
});
|
||||
|
||||
const customNamespace = io.of("/custom");
|
||||
|
||||
process.on("message", async (msg) => {
|
||||
switch (msg) {
|
||||
case "broadcasts to all clients":
|
||||
io.emit("test", 1, "2", Buffer.from([3, 4]));
|
||||
break;
|
||||
case "broadcasts to all clients in a namespace":
|
||||
customNamespace.emit("test");
|
||||
break;
|
||||
case "join room1":
|
||||
serverSocket.join("room1");
|
||||
break;
|
||||
case "join room1 & room2":
|
||||
serverSocket.join(["room1", "room2"]);
|
||||
break;
|
||||
case "join room2":
|
||||
serverSocket.join("room2");
|
||||
break;
|
||||
case "broadcasts to all clients in a room":
|
||||
io.to("room1").emit("test");
|
||||
break;
|
||||
case "broadcasts to all clients except in room":
|
||||
io.of("/").except("room1").emit("test");
|
||||
break;
|
||||
case "broadcasts to local clients only":
|
||||
io.local.emit("test");
|
||||
break;
|
||||
|
||||
case "broadcasts with multiple acknowledgements": {
|
||||
io.timeout(500).emit("test", (err, responses) => {
|
||||
expect(err).to.be(null);
|
||||
expect(responses).to.contain(1);
|
||||
expect(responses).to.contain(2);
|
||||
expect(responses).to.contain(3);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(io.of("/").adapter.ackRequests.size).to.eql(0);
|
||||
|
||||
process.send("ok");
|
||||
}, 500);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "broadcasts with multiple acknowledgements (binary content)": {
|
||||
io.timeout(500).emit("test", (err, responses) => {
|
||||
expect(err).to.be(null);
|
||||
responses.forEach((response) => {
|
||||
expect(Buffer.isBuffer(response)).to.be(true);
|
||||
});
|
||||
|
||||
process.send("ok");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "broadcasts with multiple acknowledgements (no client)": {
|
||||
io
|
||||
.to("abc")
|
||||
.timeout(500)
|
||||
.emit("test", (err, responses) => {
|
||||
expect(err).to.be(null);
|
||||
expect(responses).to.eql([]);
|
||||
|
||||
process.send("ok");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "broadcasts with multiple acknowledgements (timeout)": {
|
||||
io.timeout(500).emit("test", (err, responses) => {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(responses).to.contain(1);
|
||||
expect(responses).to.contain(2);
|
||||
|
||||
process.send("ok");
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "get rooms":
|
||||
process.send(serverSocket.rooms);
|
||||
break;
|
||||
case "makes all socket instances join the specified room":
|
||||
io.socketsJoin("room1");
|
||||
break;
|
||||
|
||||
case "makes the matching socket instances join the specified room":
|
||||
io.in("room1").socketsJoin("room2");
|
||||
break;
|
||||
|
||||
case "makes all socket instances leave the specified room":
|
||||
io.socketsLeave("room1");
|
||||
break;
|
||||
case "makes the matching socket instances leave the specified room":
|
||||
io.in("room1").socketsLeave("room2");
|
||||
break;
|
||||
|
||||
case "makes all socket instances disconnect":
|
||||
io.disconnectSockets();
|
||||
break;
|
||||
|
||||
case "returns all socket instances":
|
||||
const sockets = await io.fetchSockets();
|
||||
|
||||
expect(sockets).to.be.an(Array);
|
||||
expect(sockets).to.have.length(3);
|
||||
expect(io.of("/").adapter.requests.size).to.eql(0); // clean up
|
||||
|
||||
process.send("ok");
|
||||
break;
|
||||
|
||||
case "sends an event to other server instances":
|
||||
io.serverSideEmit("hello", "world", 1, "2");
|
||||
break;
|
||||
case "sends an event and receives a response from the other server instances (1)":
|
||||
io.serverSideEmit("hello with ack", (err, response) => {
|
||||
expect(err).to.be(null);
|
||||
expect(response).to.be.an(Array);
|
||||
expect(response).to.contain(2);
|
||||
expect(response).to.contain("3");
|
||||
process.send("ok");
|
||||
});
|
||||
break;
|
||||
case "sends an event and receives a response from the other server instances (2)":
|
||||
io.on("hello with ack", (cb) => {
|
||||
cb(2);
|
||||
});
|
||||
break;
|
||||
case "sends an event and receives a response from the other server instances (3)":
|
||||
io.on("hello with ack", (cb) => {
|
||||
cb("3");
|
||||
});
|
||||
break;
|
||||
case "sends an event but timeout if one server does not respond (1)":
|
||||
io.serverSideEmit("hello with ack", (err, response) => {
|
||||
expect(err.message).to.be(
|
||||
"timeout reached: missing 1 responses"
|
||||
);
|
||||
expect(response).to.be.an(Array);
|
||||
expect(response).to.contain(2);
|
||||
process.send("ok");
|
||||
});
|
||||
break;
|
||||
case "sends an event but timeout if one server does not respond (2)":
|
||||
io.on("hello with ack", (cb) => {
|
||||
cb(2);
|
||||
});
|
||||
break;
|
||||
case "sends an event but timeout if one server does not respond (3)":
|
||||
io.on("hello with ack", (cb) => {
|
||||
// do nothing
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
io.on("hello", (arg1, arg2, arg3) => {
|
||||
expect(arg1).to.eql("world");
|
||||
expect(arg2).to.eql(1);
|
||||
expect(arg3).to.eql("2");
|
||||
process.send("ok");
|
||||
});
|
||||
|
||||
httpServer.listen(parseInt(process.env.PORT, 10));
|
||||
13
packages/socket.io-cluster-adapter/tsconfig.json
Normal file
13
packages/socket.io-cluster-adapter/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"allowJs": false,
|
||||
"target": "es2017",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": [
|
||||
"./lib/**/*"
|
||||
]
|
||||
}
|
||||
@@ -19,14 +19,14 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@msgpack/msgpack": "~2.8.0",
|
||||
"debug": "~4.3.3",
|
||||
"debug": "~4.4.1",
|
||||
"engine.io": "~6.6.0",
|
||||
"engine.io-parser": "~5.2.3"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "rimraf ./dist && tsc",
|
||||
"test": "npm run format:check && npm run compile && npm run test:unit",
|
||||
"test:unit": "mocha --require ts-node/register test/*.ts",
|
||||
"test:unit": "mocha --import=tsx test/*.ts",
|
||||
"format:check": "prettier --check \"lib/**/*.ts\" \"test/**/*.ts\"",
|
||||
"format:fix": "prettier --write \"lib/**/*.ts\" \"test/**/*.ts\"",
|
||||
"prepack": "npm run compile"
|
||||
|
||||
@@ -1,39 +1,73 @@
|
||||
# History
|
||||
# Changelog
|
||||
|
||||
| Version | Release date |
|
||||
|-------------------------------------------------------------------------------------------------------------|----------------|
|
||||
| [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)
|
||||
|
||||
This release contains a bump of `debug` from `~4.3.1` to `~4.4.1`.
|
||||
|
||||
|
||||
# Release notes
|
||||
|
||||
## [3.3.4](https://github.com/Automattic/socket.io-parser/compare/3.3.3...3.3.4) (2024-07-22)
|
||||
|
||||
|
||||
@@ -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.4",
|
||||
"version": "4.2.6",
|
||||
"description": "socket.io protocol parser",
|
||||
"homepage": "https://github.com/socketio/socket.io/tree/main/packages/socket.io-client#readme",
|
||||
"repository": {
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
"debug": "~4.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "rimraf ./build && tsc && tsc -p tsconfig.esm.json && ./postcompile.sh",
|
||||
|
||||
@@ -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$/,
|
||||
);
|
||||
|
||||
@@ -53,7 +53,7 @@ if (process.env.CI === "true") {
|
||||
{
|
||||
browserName: "internet explorer",
|
||||
browserVersion: "10",
|
||||
platformName: "Windows 7",
|
||||
platformName: "Windows 8",
|
||||
"sauce:options": BASE_SAUCE_OPTIONS,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"compile": "rimraf ./dist && tsc",
|
||||
"test": "npm run format:check && npm run compile && nyc mocha --require ts-node/register --timeout 5000 test/index.ts",
|
||||
"test": "npm run format:check && npm run compile && nyc mocha --import=tsx --timeout 5000 test/index.ts",
|
||||
"format:check": "prettier --parser typescript --check 'lib/**/*.ts' 'test/**/*.ts'",
|
||||
"format:fix": "prettier --parser typescript --write 'lib/**/*.ts' 'test/**/*.ts'",
|
||||
"prepack": "npm run compile"
|
||||
},
|
||||
"dependencies": {
|
||||
"@msgpack/msgpack": "^2.7.0",
|
||||
"debug": "~4.3.1"
|
||||
"debug": "~4.4.1"
|
||||
},
|
||||
"keywords": [
|
||||
"socket.io",
|
||||
|
||||
19
packages/socket.io-redis-streams-emitter/CHANGELOG.md
Normal file
19
packages/socket.io-redis-streams-emitter/CHANGELOG.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Changelog
|
||||
|
||||
| Version | Release date |
|
||||
|--------------------------|---------------|
|
||||
| [0.1.1](#011-2025-11-07) | November 2025 |
|
||||
| [0.1.0](#010-2025-11-06) | November 2025 |
|
||||
|
||||
|
||||
## [0.1.1](https://github.com/socketio/socket.io/compare/@socket.io/redis-streams-emitter@0.1.0...@socket.io/redis-streams-emitter@0.1.1) (2025-11-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove dependency on socket.io-adapter ([7617707](https://github.com/socketio/socket.io/commit/7617707ed8f0defd0e19cb7c6963504ce7b87f6b))
|
||||
|
||||
|
||||
## 0.1.0 (2025-11-06)
|
||||
|
||||
Initial release!
|
||||
7
packages/socket.io-redis-streams-emitter/LICENSE
Normal file
7
packages/socket.io-redis-streams-emitter/LICENSE
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2025-present Guillermo Rauch and Socket.IO contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
220
packages/socket.io-redis-streams-emitter/README.md
Normal file
220
packages/socket.io-redis-streams-emitter/README.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# Socket.IO Redis Streams emitter
|
||||
|
||||
The `@socket.io/redis-streams-emitter` package allows you to easily communicate with a group of Socket.IO servers from another Node.js process (server-side).
|
||||
|
||||
It must be used in conjunction with [`@socket.io/redis-streams-adapter`](https://github.com/socketio/socket.io-redis-streams-adapter).
|
||||
|
||||
**Table of contents**
|
||||
|
||||
<!-- TOC -->
|
||||
* [Installation](#installation)
|
||||
* [Usage](#usage)
|
||||
* [With the `redis` package](#with-the-redis-package)
|
||||
* [With the `redis` package and a Redis cluster](#with-the-redis-package-and-a-redis-cluster)
|
||||
* [With the `ioredis` package](#with-the-ioredis-package)
|
||||
* [With the `ioredis` package and a Redis cluster](#with-the-ioredis-package-and-a-redis-cluster)
|
||||
* [Options](#options)
|
||||
* [API](#api)
|
||||
* [`Emitter(redisClient[, nsp][, opts])`](#emitterredisclient-nsp-opts)
|
||||
* [`Emitter#to(room:string):BroadcastOperator`](#emittertoroomstringbroadcastoperator)
|
||||
* [`Emitter#in(room:string):BroadcastOperator`](#emitterinroomstringbroadcastoperator)
|
||||
* [`Emitter#except(room:string):BroadcastOperator`](#emitterexceptroomstringbroadcastoperator)
|
||||
* [`Emitter#of(namespace:string):Emitter`](#emitterofnamespacestringemitter)
|
||||
* [`Emitter#socketsJoin(rooms:string|string[])`](#emittersocketsjoinroomsstringstring)
|
||||
* [`Emitter#socketsLeave(rooms:string|string[])`](#emittersocketsleaveroomsstringstring)
|
||||
* [`Emitter#disconnectSockets(close:boolean)`](#emitterdisconnectsocketscloseboolean)
|
||||
* [`Emitter#serverSideEmit(ev:string[,...args:any[]])`](#emitterserversideemitevstringargsany)
|
||||
* [License](#license)
|
||||
<!-- TOC -->
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
npm install @socket.io/redis-streams-emitter redis
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### With the `redis` package
|
||||
|
||||
```js
|
||||
import { createClient } from "redis";
|
||||
import { Emitter } from "@socket.io/redis-streams-emitter";
|
||||
|
||||
const redisClient = createClient({
|
||||
url: "redis://localhost:6379"
|
||||
});
|
||||
|
||||
await redisClient.connect();
|
||||
|
||||
const io = new Emitter(redisClient);
|
||||
|
||||
setInterval(() => {
|
||||
io.emit("ping", new Date());
|
||||
}, 1000);
|
||||
```
|
||||
|
||||
### With the `redis` package and a Redis cluster
|
||||
|
||||
```js
|
||||
import { createCluster } from "redis";
|
||||
import { Emitter } from "@socket.io/redis-streams-emitter";
|
||||
|
||||
const redisClient = createCluster({
|
||||
rootNodes: [
|
||||
{
|
||||
url: "redis://localhost:7000",
|
||||
},
|
||||
{
|
||||
url: "redis://localhost:7001",
|
||||
},
|
||||
{
|
||||
url: "redis://localhost:7002",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await redisClient.connect();
|
||||
|
||||
const io = new Emitter(redisClient);
|
||||
|
||||
setInterval(() => {
|
||||
io.emit("ping", new Date());
|
||||
}, 1000);
|
||||
```
|
||||
|
||||
### With the `ioredis` package
|
||||
|
||||
```js
|
||||
import { Redis } from "ioredis";
|
||||
import { Emitter } from "@socket.io/redis-streams-emitter";
|
||||
|
||||
const redisClient = new Redis();
|
||||
|
||||
const io = new Emitter(redisClient);
|
||||
|
||||
setInterval(() => {
|
||||
io.emit("ping", new Date());
|
||||
}, 1000);
|
||||
```
|
||||
|
||||
### With the `ioredis` package and a Redis cluster
|
||||
|
||||
```js
|
||||
import { Cluster } from "ioredis";
|
||||
import { Emitter } from "@socket.io/redis-streams-emitter";
|
||||
|
||||
const redisClient = new Cluster([
|
||||
{
|
||||
host: "localhost",
|
||||
port: 7000,
|
||||
},
|
||||
{
|
||||
host: "localhost",
|
||||
port: 7001,
|
||||
},
|
||||
{
|
||||
host: "localhost",
|
||||
port: 7002,
|
||||
},
|
||||
]);
|
||||
|
||||
const io = new Emitter(redisClient);
|
||||
|
||||
setInterval(() => {
|
||||
io.emit("ping", new Date());
|
||||
}, 1000);
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Name | Description | Default value |
|
||||
|--------------|--------------------------------------------------------------------|---------------|
|
||||
| `streamName` | The name of the Redis stream. | `socket.io` |
|
||||
| `maxLen` | The maximum size of the stream. Almost exact trimming (~) is used. | `10_000` |
|
||||
|
||||
## API
|
||||
|
||||
### `Emitter(redisClient[, nsp][, opts])`
|
||||
|
||||
```js
|
||||
const io = new Emitter(redisClient);
|
||||
```
|
||||
|
||||
### `Emitter#to(room:string):BroadcastOperator`
|
||||
### `Emitter#in(room:string):BroadcastOperator`
|
||||
|
||||
Specifies a specific `room` that you want to emit to.
|
||||
|
||||
```js
|
||||
io.to("room1").emit("hello");
|
||||
```
|
||||
|
||||
### `Emitter#except(room:string):BroadcastOperator`
|
||||
|
||||
Specifies a specific `room` that you want to exclude from broadcasting.
|
||||
|
||||
```js
|
||||
io.except("room2").emit("hello");
|
||||
```
|
||||
|
||||
### `Emitter#of(namespace:string):Emitter`
|
||||
|
||||
Specifies a specific namespace that you want to emit to.
|
||||
|
||||
```js
|
||||
const customNamespace = io.of("/custom");
|
||||
|
||||
customNamespace.emit("hello");
|
||||
```
|
||||
|
||||
### `Emitter#socketsJoin(rooms:string|string[])`
|
||||
|
||||
Makes the matching socket instances join the specified rooms:
|
||||
|
||||
```js
|
||||
// make all Socket instances join the "room1" room
|
||||
io.socketsJoin("room1");
|
||||
|
||||
// make all Socket instances of the "admin" namespace in the "room1" room join the "room2" room
|
||||
io.of("/admin").in("room1").socketsJoin("room2");
|
||||
```
|
||||
|
||||
### `Emitter#socketsLeave(rooms:string|string[])`
|
||||
|
||||
Makes the matching socket instances leave the specified rooms:
|
||||
|
||||
```js
|
||||
// make all Socket instances leave the "room1" room
|
||||
io.socketsLeave("room1");
|
||||
|
||||
// make all Socket instances of the "admin" namespace in the "room1" room leave the "room2" room
|
||||
io.of("/admin").in("room1").socketsLeave("room2");
|
||||
```
|
||||
|
||||
### `Emitter#disconnectSockets(close:boolean)`
|
||||
|
||||
Makes the matching socket instances disconnect:
|
||||
|
||||
```js
|
||||
// make all Socket instances disconnect
|
||||
io.disconnectSockets();
|
||||
|
||||
// make all Socket instances of the "admin" namespace in the "room1" room disconnect
|
||||
io.of("/admin").in("room1").disconnectSockets();
|
||||
|
||||
// this also works with a single socket ID
|
||||
io.of("/admin").in(theSocketId).disconnectSockets();
|
||||
```
|
||||
|
||||
### `Emitter#serverSideEmit(ev:string[,...args:any[]])`
|
||||
|
||||
Emits an event that will be received by each Socket.IO server of the cluster.
|
||||
|
||||
```js
|
||||
io.serverSideEmit("ping");
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
15
packages/socket.io-redis-streams-emitter/compose.yaml
Normal file
15
packages/socket.io-redis-streams-emitter/compose.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
services:
|
||||
redis:
|
||||
image: redis:5
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
redis-cluster:
|
||||
image: grokzen/redis-cluster:7.0.10
|
||||
ports:
|
||||
- "7000-7005:7000-7005"
|
||||
|
||||
valkey:
|
||||
image: valkey/valkey:8
|
||||
ports:
|
||||
- "6389:6379"
|
||||
@@ -0,0 +1,75 @@
|
||||
// imported from the 'socket.io-adapter' package
|
||||
export enum MessageType {
|
||||
INITIAL_HEARTBEAT = 1,
|
||||
HEARTBEAT,
|
||||
BROADCAST,
|
||||
SOCKETS_JOIN,
|
||||
SOCKETS_LEAVE,
|
||||
DISCONNECT_SOCKETS,
|
||||
FETCH_SOCKETS,
|
||||
FETCH_SOCKETS_RESPONSE,
|
||||
SERVER_SIDE_EMIT,
|
||||
SERVER_SIDE_EMIT_RESPONSE,
|
||||
BROADCAST_CLIENT_COUNT,
|
||||
BROADCAST_ACK,
|
||||
ADAPTER_CLOSE,
|
||||
}
|
||||
|
||||
export type ServerId = string;
|
||||
|
||||
export type ClusterMessage = {
|
||||
uid: ServerId;
|
||||
nsp: string;
|
||||
} & (
|
||||
| {
|
||||
type:
|
||||
| MessageType.INITIAL_HEARTBEAT
|
||||
| MessageType.HEARTBEAT
|
||||
| MessageType.ADAPTER_CLOSE;
|
||||
}
|
||||
| {
|
||||
type: MessageType.BROADCAST;
|
||||
data: {
|
||||
opts: { rooms: string[]; except: string[]; flags: BroadcastFlags };
|
||||
packet: unknown;
|
||||
requestId?: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: MessageType.SOCKETS_JOIN | MessageType.SOCKETS_LEAVE;
|
||||
data: {
|
||||
opts: { rooms: string[]; except: string[]; flags: BroadcastFlags };
|
||||
rooms: string[];
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: MessageType.DISCONNECT_SOCKETS;
|
||||
data: {
|
||||
opts: { rooms: string[]; except: string[]; flags: BroadcastFlags };
|
||||
close?: boolean;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: MessageType.FETCH_SOCKETS;
|
||||
data: {
|
||||
opts: { rooms: string[]; except: string[]; flags: BroadcastFlags };
|
||||
requestId: string;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: MessageType.SERVER_SIDE_EMIT;
|
||||
data: {
|
||||
requestId?: string;
|
||||
packet: any[];
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
export interface BroadcastFlags {
|
||||
volatile?: boolean;
|
||||
compress?: boolean;
|
||||
local?: boolean;
|
||||
broadcast?: boolean;
|
||||
binary?: boolean;
|
||||
timeout?: number;
|
||||
}
|
||||
496
packages/socket.io-redis-streams-emitter/lib/index.ts
Normal file
496
packages/socket.io-redis-streams-emitter/lib/index.ts
Normal file
@@ -0,0 +1,496 @@
|
||||
import debugModule from "debug";
|
||||
import type {
|
||||
DefaultEventsMap,
|
||||
EventNames,
|
||||
EventParams,
|
||||
EventsMap,
|
||||
TypedEventBroadcaster,
|
||||
} from "./typed-events";
|
||||
import { encode } from "@msgpack/msgpack";
|
||||
import { hasBinary, XADD } from "./util";
|
||||
import { ClusterMessage, MessageType, BroadcastFlags } from "./adapter-types";
|
||||
|
||||
const debug = debugModule("socket.io-redis-streams-emitter");
|
||||
const EMITTER_UID = "emitter";
|
||||
|
||||
type DistributiveOmit<T, K extends keyof any> = T extends any
|
||||
? Omit<T, K>
|
||||
: never;
|
||||
|
||||
// TODO move to the socket.io-adapter package
|
||||
abstract class BaseEmitter<
|
||||
EmitEvents extends EventsMap = DefaultEventsMap,
|
||||
ServerSideEvents extends EventsMap = DefaultEventsMap,
|
||||
> {
|
||||
protected abstract publish(
|
||||
message: DistributiveOmit<ClusterMessage, "uid" | "nsp">,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Emits to all clients.
|
||||
*
|
||||
* @return Always true
|
||||
* @public
|
||||
*/
|
||||
public emit<Ev extends EventNames<EmitEvents>>(
|
||||
ev: Ev,
|
||||
...args: EventParams<EmitEvents, Ev>
|
||||
): true {
|
||||
return this.newBroadcastOperator().emit(ev, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
*
|
||||
* @param room
|
||||
* @return BroadcastOperator
|
||||
* @public
|
||||
*/
|
||||
public to(
|
||||
room: string | string[],
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
return this.newBroadcastOperator().to(room);
|
||||
}
|
||||
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
*
|
||||
* @param room
|
||||
* @return BroadcastOperator
|
||||
* @public
|
||||
*/
|
||||
public in(
|
||||
room: string | string[],
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
return this.newBroadcastOperator().in(room);
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes a room when emitting.
|
||||
*
|
||||
* @param room
|
||||
* @return BroadcastOperator
|
||||
* @public
|
||||
*/
|
||||
public except(
|
||||
room: string | string[],
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
return this.newBroadcastOperator().except(room);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to
|
||||
* receive messages (because of network slowness or other issues, or because they’re connected through long polling
|
||||
* and is in the middle of a request-response cycle).
|
||||
*
|
||||
* @return BroadcastOperator
|
||||
* @public
|
||||
*/
|
||||
public get volatile(): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
return this.newBroadcastOperator().volatile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the compress flag.
|
||||
*
|
||||
* @param compress - if `true`, compresses the sending data
|
||||
* @return BroadcastOperator
|
||||
* @public
|
||||
*/
|
||||
public compress(
|
||||
compress: boolean,
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
return this.newBroadcastOperator().compress(compress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances join the specified rooms
|
||||
*
|
||||
* @param rooms
|
||||
* @public
|
||||
*/
|
||||
public socketsJoin(rooms: string | string[]): void {
|
||||
return this.newBroadcastOperator().socketsJoin(rooms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances leave the specified rooms
|
||||
*
|
||||
* @param rooms
|
||||
* @public
|
||||
*/
|
||||
public socketsLeave(rooms: string | string[]): void {
|
||||
return this.newBroadcastOperator().socketsLeave(rooms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances disconnect
|
||||
*
|
||||
* @param close - whether to close the underlying connection
|
||||
* @public
|
||||
*/
|
||||
public disconnectSockets(close: boolean = false): void {
|
||||
return this.newBroadcastOperator().disconnectSockets(close);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet to the Socket.IO servers in the cluster
|
||||
*
|
||||
* @param ev - the event name
|
||||
* @param args - any number of serializable arguments
|
||||
*/
|
||||
public serverSideEmit<Ev extends EventNames<ServerSideEvents>>(
|
||||
ev: Ev,
|
||||
...args: EventParams<ServerSideEvents, Ev>
|
||||
): void {
|
||||
return this.newBroadcastOperator().serverSideEmit(ev, ...args);
|
||||
}
|
||||
|
||||
private newBroadcastOperator() {
|
||||
return new BroadcastOperator<EmitEvents, ServerSideEvents>((msg) =>
|
||||
this.publish(msg),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const RESERVED_EVENTS: ReadonlySet<string | Symbol> = new Set(<const>[
|
||||
"connect",
|
||||
"connect_error",
|
||||
"disconnect",
|
||||
"disconnecting",
|
||||
"newListener",
|
||||
"removeListener",
|
||||
]);
|
||||
|
||||
export class BroadcastOperator<
|
||||
EmitEvents extends EventsMap,
|
||||
ServerSideEvents extends EventsMap,
|
||||
> implements TypedEventBroadcaster<EmitEvents>
|
||||
{
|
||||
constructor(
|
||||
private readonly publish: (
|
||||
message: DistributiveOmit<ClusterMessage, "uid" | "nsp">,
|
||||
) => void,
|
||||
private readonly rooms: Set<string> = new Set<string>(),
|
||||
private readonly exceptRooms: Set<string> = new Set<string>(),
|
||||
private readonly flags: BroadcastFlags = {},
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
*
|
||||
* @param room
|
||||
* @return a new BroadcastOperator instance
|
||||
* @public
|
||||
*/
|
||||
public to(
|
||||
room: string | string[],
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
const rooms = new Set(this.rooms);
|
||||
if (Array.isArray(room)) {
|
||||
room.forEach((r) => rooms.add(r));
|
||||
} else {
|
||||
rooms.add(room);
|
||||
}
|
||||
return new BroadcastOperator(
|
||||
this.publish,
|
||||
rooms,
|
||||
this.exceptRooms,
|
||||
this.flags,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
*
|
||||
* @param room
|
||||
* @return a new BroadcastOperator instance
|
||||
* @public
|
||||
*/
|
||||
public in(
|
||||
room: string | string[],
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
return this.to(room);
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes a room when emitting.
|
||||
*
|
||||
* @param room
|
||||
* @return a new BroadcastOperator instance
|
||||
* @public
|
||||
*/
|
||||
public except(
|
||||
room: string | string[],
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
const exceptRooms = new Set(this.exceptRooms);
|
||||
if (Array.isArray(room)) {
|
||||
room.forEach((r) => exceptRooms.add(r));
|
||||
} else {
|
||||
exceptRooms.add(room);
|
||||
}
|
||||
return new BroadcastOperator(
|
||||
this.publish,
|
||||
this.rooms,
|
||||
exceptRooms,
|
||||
this.flags,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the compress flag.
|
||||
*
|
||||
* @param compress - if `true`, compresses the sending data
|
||||
* @return a new BroadcastOperator instance
|
||||
* @public
|
||||
*/
|
||||
public compress(
|
||||
compress: boolean,
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
const flags = Object.assign({}, this.flags, { compress });
|
||||
return new BroadcastOperator(
|
||||
this.publish,
|
||||
this.rooms,
|
||||
this.exceptRooms,
|
||||
flags,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to
|
||||
* receive messages (because of network slowness or other issues, or because they’re connected through long polling
|
||||
* and is in the middle of a request-response cycle).
|
||||
*
|
||||
* @return a new BroadcastOperator instance
|
||||
* @public
|
||||
*/
|
||||
public get volatile(): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
const flags = Object.assign({}, this.flags, { volatile: true });
|
||||
return new BroadcastOperator(
|
||||
this.publish,
|
||||
this.rooms,
|
||||
this.exceptRooms,
|
||||
flags,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits to all clients.
|
||||
*
|
||||
* @return Always true
|
||||
* @public
|
||||
*/
|
||||
public emit<Ev extends EventNames<EmitEvents>>(
|
||||
ev: Ev,
|
||||
...args: EventParams<EmitEvents, Ev>
|
||||
): true {
|
||||
if (RESERVED_EVENTS.has(ev)) {
|
||||
throw new Error(`"${String(ev)}" is a reserved event name`);
|
||||
}
|
||||
|
||||
// set up packet object
|
||||
const data = [ev, ...args];
|
||||
const packet = {
|
||||
type: 2, // EVENT
|
||||
data: data,
|
||||
};
|
||||
|
||||
const opts = {
|
||||
rooms: [...this.rooms],
|
||||
flags: this.flags,
|
||||
except: [...this.exceptRooms],
|
||||
};
|
||||
|
||||
this.publish({
|
||||
type: MessageType.BROADCAST,
|
||||
data: {
|
||||
packet,
|
||||
opts,
|
||||
},
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances join the specified rooms
|
||||
*
|
||||
* @param rooms
|
||||
* @public
|
||||
*/
|
||||
public socketsJoin(rooms: string | string[]): void {
|
||||
this.publish({
|
||||
type: MessageType.SOCKETS_JOIN,
|
||||
data: {
|
||||
opts: {
|
||||
rooms: [...this.rooms],
|
||||
except: [...this.exceptRooms],
|
||||
flags: {},
|
||||
},
|
||||
rooms: Array.isArray(rooms) ? rooms : [rooms],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances leave the specified rooms
|
||||
*
|
||||
* @param rooms
|
||||
* @public
|
||||
*/
|
||||
public socketsLeave(rooms: string | string[]): void {
|
||||
this.publish({
|
||||
type: MessageType.SOCKETS_LEAVE,
|
||||
data: {
|
||||
opts: {
|
||||
rooms: [...this.rooms],
|
||||
except: [...this.exceptRooms],
|
||||
flags: {},
|
||||
},
|
||||
rooms: Array.isArray(rooms) ? rooms : [rooms],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances disconnect
|
||||
*
|
||||
* @param close - whether to close the underlying connection
|
||||
* @public
|
||||
*/
|
||||
public disconnectSockets(close: boolean = false): void {
|
||||
this.publish({
|
||||
type: MessageType.DISCONNECT_SOCKETS,
|
||||
data: {
|
||||
opts: {
|
||||
rooms: [...this.rooms],
|
||||
except: [...this.exceptRooms],
|
||||
flags: {},
|
||||
},
|
||||
close,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet to the Socket.IO servers in the cluster
|
||||
*
|
||||
* @param ev - the event name
|
||||
* @param args - any number of serializable arguments
|
||||
*/
|
||||
public serverSideEmit<Ev extends EventNames<ServerSideEvents>>(
|
||||
ev: Ev,
|
||||
...args: EventParams<ServerSideEvents, Ev>
|
||||
): void {
|
||||
const withAck = args.length && typeof args[args.length - 1] === "function";
|
||||
|
||||
if (withAck) {
|
||||
throw new Error("Acknowledgements are not supported");
|
||||
}
|
||||
|
||||
this.publish({
|
||||
type: MessageType.SERVER_SIDE_EMIT,
|
||||
data: {
|
||||
packet: [ev, ...args],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function flattenPayload(message: ClusterMessage) {
|
||||
const rawMessage = {
|
||||
uid: message.uid,
|
||||
nsp: message.nsp,
|
||||
type: message.type.toString(),
|
||||
data: undefined as string | undefined,
|
||||
};
|
||||
|
||||
// @ts-expect-error
|
||||
const data = message.data;
|
||||
|
||||
if (data) {
|
||||
const mayContainBinary = [
|
||||
MessageType.BROADCAST,
|
||||
MessageType.FETCH_SOCKETS_RESPONSE,
|
||||
MessageType.SERVER_SIDE_EMIT,
|
||||
MessageType.SERVER_SIDE_EMIT_RESPONSE,
|
||||
MessageType.BROADCAST_ACK,
|
||||
].includes(message.type);
|
||||
|
||||
if (mayContainBinary && hasBinary(data)) {
|
||||
rawMessage.data = Buffer.from(encode(data)).toString("base64");
|
||||
} else {
|
||||
rawMessage.data = JSON.stringify(data);
|
||||
}
|
||||
}
|
||||
|
||||
return rawMessage;
|
||||
}
|
||||
|
||||
export interface RedisStreamsEmitterOptions {
|
||||
/**
|
||||
* The name of the Redis stream.
|
||||
* @default "socket.io"
|
||||
*/
|
||||
streamName?: string;
|
||||
/**
|
||||
* The maximum size of the stream. Almost exact trimming (~) is used.
|
||||
* @default 10_000
|
||||
*/
|
||||
maxLen?: number;
|
||||
}
|
||||
|
||||
export class Emitter<
|
||||
EmitEvents extends EventsMap = DefaultEventsMap,
|
||||
ServerSideEvents extends EventsMap = DefaultEventsMap,
|
||||
> extends BaseEmitter<EmitEvents, ServerSideEvents> {
|
||||
readonly #redisClient: any;
|
||||
readonly #opts: Required<RedisStreamsEmitterOptions>;
|
||||
readonly #nsp: string;
|
||||
|
||||
constructor(
|
||||
redisClient: any,
|
||||
opts: RedisStreamsEmitterOptions = {},
|
||||
nsp = "/",
|
||||
) {
|
||||
super();
|
||||
this.#redisClient = redisClient;
|
||||
this.#opts = Object.assign(
|
||||
{
|
||||
streamName: "socket.io",
|
||||
maxLen: 10_000,
|
||||
},
|
||||
opts,
|
||||
);
|
||||
this.#nsp = nsp;
|
||||
}
|
||||
|
||||
public of(nsp: string) {
|
||||
return new Emitter(this.#redisClient, this.#opts, nsp);
|
||||
}
|
||||
|
||||
protected override publish(
|
||||
message: DistributiveOmit<ClusterMessage, "uid" | "nsp">,
|
||||
) {
|
||||
(message as ClusterMessage).uid = EMITTER_UID;
|
||||
(message as ClusterMessage).nsp = this.#nsp;
|
||||
|
||||
debug(
|
||||
"publishing message %s to stream %s",
|
||||
message.type,
|
||||
this.#opts.streamName,
|
||||
);
|
||||
|
||||
if (message.type === MessageType.BROADCAST) {
|
||||
// @ts-expect-error FIXME untyped packet object
|
||||
message.data.packet.nsp = this.#nsp;
|
||||
}
|
||||
|
||||
return XADD(
|
||||
this.#redisClient,
|
||||
this.#opts.streamName,
|
||||
flattenPayload(message as ClusterMessage),
|
||||
this.#opts.maxLen,
|
||||
);
|
||||
}
|
||||
}
|
||||
37
packages/socket.io-redis-streams-emitter/lib/typed-events.ts
Normal file
37
packages/socket.io-redis-streams-emitter/lib/typed-events.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* An events map is an interface that maps event names to their value, which
|
||||
* represents the type of the `on` listener.
|
||||
*/
|
||||
export interface EventsMap {
|
||||
[event: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default events map, used if no EventsMap is given. Using this EventsMap
|
||||
* is equivalent to accepting all event names, and any data.
|
||||
*/
|
||||
export interface DefaultEventsMap {
|
||||
[event: string]: (...args: any[]) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a union type containing all the keys of an event map.
|
||||
*/
|
||||
export type EventNames<Map extends EventsMap> = keyof Map & (string | symbol);
|
||||
|
||||
/** The tuple type representing the parameters of an event listener */
|
||||
export type EventParams<
|
||||
Map extends EventsMap,
|
||||
Ev extends EventNames<Map>,
|
||||
> = Parameters<Map[Ev]>;
|
||||
|
||||
/**
|
||||
* Interface for classes that aren't `EventEmitter`s, but still expose a
|
||||
* strictly typed `emit` method.
|
||||
*/
|
||||
export interface TypedEventBroadcaster<EmitEvents extends EventsMap> {
|
||||
emit<Ev extends EventNames<EmitEvents>>(
|
||||
ev: Ev,
|
||||
...args: EventParams<EmitEvents, Ev>
|
||||
): boolean;
|
||||
}
|
||||
68
packages/socket.io-redis-streams-emitter/lib/util.ts
Normal file
68
packages/socket.io-redis-streams-emitter/lib/util.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
export function hasBinary(obj: any, toJSON?: boolean): boolean {
|
||||
if (!obj || typeof obj !== "object") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj instanceof ArrayBuffer || ArrayBuffer.isView(obj)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
for (let i = 0, l = obj.length; i < l; i++) {
|
||||
if (hasBinary(obj[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key) && hasBinary(obj[key])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.toJSON && typeof obj.toJSON === "function" && !toJSON) {
|
||||
return hasBinary(obj.toJSON(), true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the client comes from the `redis` package
|
||||
*
|
||||
* @param redisClient
|
||||
*
|
||||
* @see https://github.com/redis/node-redis
|
||||
*/
|
||||
function isRedisV4Client(redisClient: any) {
|
||||
return typeof redisClient.sSubscribe === "function";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://redis.io/commands/xadd/
|
||||
*/
|
||||
export function XADD(
|
||||
redisClient: any,
|
||||
streamName: string,
|
||||
payload: any,
|
||||
maxLenThreshold: number,
|
||||
) {
|
||||
if (isRedisV4Client(redisClient)) {
|
||||
return redisClient.xAdd(streamName, "*", payload, {
|
||||
TRIM: {
|
||||
strategy: "MAXLEN",
|
||||
strategyModifier: "~",
|
||||
threshold: maxLenThreshold,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const args = [streamName, "MAXLEN", "~", maxLenThreshold, "*"];
|
||||
Object.keys(payload).forEach((k) => {
|
||||
args.push(k, payload[k]);
|
||||
});
|
||||
|
||||
return redisClient.xadd.call(redisClient, args);
|
||||
}
|
||||
}
|
||||
38
packages/socket.io-redis-streams-emitter/package.json
Normal file
38
packages/socket.io-redis-streams-emitter/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@socket.io/redis-streams-emitter",
|
||||
"version": "0.1.1",
|
||||
"description": "The Socket.IO Redis streams emitter, allowing to communicate with a group of Socket.IO servers from another Node.js process",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/socketio/socket.io/tree/main/packages/socket.io-redis-streams-emitter#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/socketio/socket.io.git"
|
||||
},
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"compile": "rimraf ./dist && tsc",
|
||||
"format:check": "prettier --parser typescript --check 'lib/**/*.ts' 'test/**/*.ts'",
|
||||
"format:fix": "prettier --parser typescript --write 'lib/**/*.ts' 'test/**/*.ts'",
|
||||
"prepack": "npm run compile",
|
||||
"test": "npm run format:check && npm run compile && npm run test:redis-standalone && npm run test:ioredis-standalone",
|
||||
"test:redis-standalone": "tsx --test",
|
||||
"test:redis-cluster": "REDIS_CLUSTER=1 npm run test:redis-standalone",
|
||||
"test:ioredis-standalone": "REDIS_LIB=ioredis npm run test:redis-standalone",
|
||||
"test:ioredis-cluster": "REDIS_LIB=ioredis REDIS_CLUSTER=1 npm run test:redis-standalone",
|
||||
"test:valkey-standalone": "VALKEY=1 npm run test:redis-standalone"
|
||||
},
|
||||
"dependencies": {
|
||||
"@msgpack/msgpack": "~2.8.0",
|
||||
"debug": "~4.4.1"
|
||||
},
|
||||
"keywords": [
|
||||
"socket.io",
|
||||
"redis",
|
||||
"redis-streams",
|
||||
"emitter"
|
||||
]
|
||||
}
|
||||
238
packages/socket.io-redis-streams-emitter/test/index.ts
Normal file
238
packages/socket.io-redis-streams-emitter/test/index.ts
Normal file
@@ -0,0 +1,238 @@
|
||||
import { describe, it, beforeEach, afterEach } from "node:test";
|
||||
import * as assert from "node:assert";
|
||||
import { type Server, type Socket as ServerSocket } from "socket.io";
|
||||
import { type Socket as ClientSocket } from "socket.io-client";
|
||||
import { times, sleep, setup, initRedisClient } from "./util";
|
||||
import { Emitter } from "../lib";
|
||||
|
||||
const PROPAGATION_DELAY_IN_MS = 100;
|
||||
|
||||
describe("@socket.io/redis-streams-emitter", () => {
|
||||
let servers: [Server, Server, Server],
|
||||
serverSockets: [ServerSocket, ServerSocket, ServerSocket],
|
||||
clientSockets: [ClientSocket, ClientSocket, ClientSocket],
|
||||
cleanup: () => void,
|
||||
emitter: Emitter;
|
||||
|
||||
beforeEach(async () => {
|
||||
const testContext = await setup();
|
||||
servers = testContext.servers;
|
||||
serverSockets = testContext.serverSockets;
|
||||
clientSockets = testContext.clientSockets;
|
||||
const redisClient = await initRedisClient();
|
||||
emitter = new Emitter(redisClient);
|
||||
|
||||
cleanup = () => {
|
||||
testContext.cleanup();
|
||||
redisClient.quit();
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => cleanup());
|
||||
|
||||
describe("broadcast", function () {
|
||||
it("broadcasts to all clients", () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
const partialResolve = times(3, resolve);
|
||||
|
||||
clientSockets.forEach((clientSocket) => {
|
||||
clientSocket.on("test", (arg1, arg2, arg3) => {
|
||||
assert.equal(arg1, 1);
|
||||
assert.equal(arg2, "2");
|
||||
assert.ok(Buffer.isBuffer(arg3));
|
||||
partialResolve();
|
||||
});
|
||||
});
|
||||
|
||||
emitter.emit("test", 1, "2", Buffer.from([3, 4]));
|
||||
});
|
||||
});
|
||||
|
||||
it("broadcasts to all clients in a namespace", () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
const partialResolve = times(3, () => {
|
||||
servers.forEach((server) => server.of("/custom").adapter.close());
|
||||
resolve();
|
||||
});
|
||||
|
||||
servers.forEach((server) => server.of("/custom"));
|
||||
|
||||
const onConnect = times(3, async () => {
|
||||
await sleep(PROPAGATION_DELAY_IN_MS);
|
||||
|
||||
emitter.of("/custom").emit("test");
|
||||
});
|
||||
|
||||
clientSockets.forEach((clientSocket) => {
|
||||
const socket = clientSocket.io.socket("/custom");
|
||||
socket.on("connect", onConnect);
|
||||
socket.on("test", () => {
|
||||
socket.disconnect();
|
||||
partialResolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("broadcasts to all clients in a room", () => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
serverSockets[1].join("room1");
|
||||
|
||||
clientSockets[0].on("test", () => {
|
||||
reject("should not happen");
|
||||
});
|
||||
|
||||
clientSockets[1].on("test", () => {
|
||||
resolve();
|
||||
});
|
||||
|
||||
clientSockets[2].on("test", () => {
|
||||
reject("should not happen");
|
||||
});
|
||||
|
||||
emitter.to("room1").emit("test");
|
||||
});
|
||||
});
|
||||
|
||||
it("broadcasts to all clients except in room", () => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const partialResolve = times(2, resolve);
|
||||
serverSockets[1].join("room1");
|
||||
|
||||
clientSockets[0].on("test", () => {
|
||||
partialResolve();
|
||||
});
|
||||
|
||||
clientSockets[1].on("test", () => {
|
||||
reject("should not happen");
|
||||
});
|
||||
|
||||
clientSockets[2].on("test", () => {
|
||||
partialResolve();
|
||||
});
|
||||
|
||||
emitter.of("/").except("room1").emit("test");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("socketsJoin", () => {
|
||||
it("makes all socket instances join the specified room", async () => {
|
||||
emitter.socketsJoin("room1");
|
||||
|
||||
await sleep(PROPAGATION_DELAY_IN_MS);
|
||||
|
||||
assert.ok(serverSockets[0].rooms.has("room1"));
|
||||
assert.ok(serverSockets[1].rooms.has("room1"));
|
||||
assert.ok(serverSockets[2].rooms.has("room1"));
|
||||
});
|
||||
|
||||
it("makes the matching socket instances join the specified room", async () => {
|
||||
serverSockets[0].join("room1");
|
||||
serverSockets[2].join("room1");
|
||||
|
||||
emitter.in("room1").socketsJoin("room2");
|
||||
|
||||
await sleep(PROPAGATION_DELAY_IN_MS);
|
||||
|
||||
assert.ok(serverSockets[0].rooms.has("room2"));
|
||||
assert.ok(serverSockets[1].rooms.has("room2") === false);
|
||||
assert.ok(serverSockets[2].rooms.has("room2"));
|
||||
});
|
||||
|
||||
it("makes the given socket instance join the specified room", async () => {
|
||||
emitter.in(serverSockets[1].id).socketsJoin("room3");
|
||||
|
||||
await sleep(PROPAGATION_DELAY_IN_MS);
|
||||
|
||||
assert.ok(serverSockets[0].rooms.has("room3") === false);
|
||||
assert.ok(serverSockets[1].rooms.has("room3"));
|
||||
assert.ok(serverSockets[2].rooms.has("room3") === false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("socketsLeave", () => {
|
||||
it("makes all socket instances leave the specified room", async () => {
|
||||
serverSockets[0].join("room1");
|
||||
serverSockets[2].join("room1");
|
||||
|
||||
emitter.socketsLeave("room1");
|
||||
|
||||
await sleep(PROPAGATION_DELAY_IN_MS);
|
||||
|
||||
assert.ok(serverSockets[0].rooms.has("room1") === false);
|
||||
assert.ok(serverSockets[1].rooms.has("room1") === false);
|
||||
assert.ok(serverSockets[2].rooms.has("room1") === false);
|
||||
});
|
||||
|
||||
it("makes the matching socket instances leave the specified room", async () => {
|
||||
serverSockets[0].join(["room1", "room2"]);
|
||||
serverSockets[1].join(["room1", "room2"]);
|
||||
serverSockets[2].join(["room2"]);
|
||||
|
||||
emitter.in("room1").socketsLeave("room2");
|
||||
|
||||
await sleep(PROPAGATION_DELAY_IN_MS);
|
||||
|
||||
assert.ok(serverSockets[0].rooms.has("room2") === false);
|
||||
assert.ok(serverSockets[1].rooms.has("room2") === false);
|
||||
assert.ok(serverSockets[2].rooms.has("room2"));
|
||||
});
|
||||
|
||||
it("makes the given socket instance leave the specified room", async () => {
|
||||
serverSockets[0].join("room3");
|
||||
serverSockets[1].join("room3");
|
||||
serverSockets[2].join("room3");
|
||||
|
||||
emitter.in(serverSockets[1].id).socketsLeave("room3");
|
||||
|
||||
await sleep(PROPAGATION_DELAY_IN_MS);
|
||||
|
||||
assert.ok(serverSockets[0].rooms.has("room3"));
|
||||
assert.ok(serverSockets[1].rooms.has("room1") === false);
|
||||
assert.ok(serverSockets[2].rooms.has("room3"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("disconnectSockets", () => {
|
||||
it("makes all socket instances disconnect", () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
const partialResolve = times(3, resolve);
|
||||
|
||||
clientSockets.forEach((clientSocket) => {
|
||||
clientSocket.on("disconnect", (reason) => {
|
||||
assert.equal(reason, "io server disconnect");
|
||||
partialResolve();
|
||||
});
|
||||
});
|
||||
|
||||
emitter.disconnectSockets();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("serverSideEmit", () => {
|
||||
it("sends an event to other server instances", () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
const partialResolve = times(3, resolve);
|
||||
|
||||
emitter.serverSideEmit("hello", "world", 1, "2");
|
||||
|
||||
servers[0].on("hello", (arg1, arg2, arg3) => {
|
||||
assert.equal(arg1, "world");
|
||||
assert.equal(arg2, 1);
|
||||
assert.equal(arg3, "2");
|
||||
partialResolve();
|
||||
});
|
||||
|
||||
servers[1].on("hello", (arg1, arg2, arg3) => {
|
||||
partialResolve();
|
||||
});
|
||||
|
||||
servers[2].of("/").on("hello", () => {
|
||||
partialResolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
164
packages/socket.io-redis-streams-emitter/test/util.ts
Normal file
164
packages/socket.io-redis-streams-emitter/test/util.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import { createServer } from "node:http";
|
||||
import { type AddressInfo } from "node:net";
|
||||
import { createClient, createCluster } from "redis";
|
||||
import { Redis, Cluster } from "ioredis";
|
||||
import { Server, type Socket as ServerSocket } from "socket.io";
|
||||
import { io as ioc, type Socket as ClientSocket } from "socket.io-client";
|
||||
import { createAdapter } from "@socket.io/redis-streams-adapter";
|
||||
|
||||
export function times(count: number, fn: () => void) {
|
||||
let i = 0;
|
||||
return () => {
|
||||
i++;
|
||||
if (i === count) {
|
||||
fn();
|
||||
} else if (i > count) {
|
||||
throw new Error(`too many calls: ${i} instead of ${count}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function sleep(duration: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, duration));
|
||||
}
|
||||
|
||||
const mode = process.env.REDIS_CLUSTER === "1" ? "cluster" : "standalone";
|
||||
const lib = process.env.REDIS_LIB || "redis";
|
||||
|
||||
console.log(`[INFO] testing in ${mode} mode with ${lib}`);
|
||||
|
||||
export async function initRedisClient() {
|
||||
if (mode === "cluster") {
|
||||
if (lib === "ioredis") {
|
||||
return new Cluster([
|
||||
{
|
||||
host: "localhost",
|
||||
port: 7000,
|
||||
},
|
||||
{
|
||||
host: "localhost",
|
||||
port: 7001,
|
||||
},
|
||||
{
|
||||
host: "localhost",
|
||||
port: 7002,
|
||||
},
|
||||
{
|
||||
host: "localhost",
|
||||
port: 7003,
|
||||
},
|
||||
{
|
||||
host: "localhost",
|
||||
port: 7004,
|
||||
},
|
||||
{
|
||||
host: "localhost",
|
||||
port: 7005,
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
const redisClient = createCluster({
|
||||
rootNodes: [
|
||||
{
|
||||
url: "redis://localhost:7000",
|
||||
},
|
||||
{
|
||||
url: "redis://localhost:7001",
|
||||
},
|
||||
{
|
||||
url: "redis://localhost:7002",
|
||||
},
|
||||
{
|
||||
url: "redis://localhost:7003",
|
||||
},
|
||||
{
|
||||
url: "redis://localhost:7004",
|
||||
},
|
||||
{
|
||||
url: "redis://localhost:7005",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await redisClient.connect();
|
||||
|
||||
return redisClient;
|
||||
}
|
||||
} else {
|
||||
if (lib === "ioredis") {
|
||||
return new Redis();
|
||||
} else {
|
||||
const port = process.env.VALKEY === "1" ? 6389 : 6379;
|
||||
const redisClient = createClient({
|
||||
url: `redis://localhost:${port}`,
|
||||
});
|
||||
await redisClient.connect();
|
||||
|
||||
return redisClient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const redisClient = await initRedisClient();
|
||||
|
||||
const httpServer = createServer();
|
||||
const io = new Server(httpServer, {
|
||||
adapter: createAdapter(redisClient, {
|
||||
readCount: 1, // return as soon as possible
|
||||
}),
|
||||
});
|
||||
|
||||
return new Promise<{
|
||||
io: Server;
|
||||
socket: ServerSocket;
|
||||
clientSocket: ClientSocket;
|
||||
cleanup: () => void;
|
||||
}>((resolve) => {
|
||||
httpServer.listen(() => {
|
||||
const port = (httpServer.address() as AddressInfo).port;
|
||||
const clientSocket = ioc(`http://localhost:${port}`);
|
||||
|
||||
io.on("connection", async (socket) => {
|
||||
resolve({
|
||||
io,
|
||||
socket,
|
||||
clientSocket,
|
||||
cleanup: () => {
|
||||
io.close();
|
||||
clientSocket.disconnect();
|
||||
redisClient.quit();
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function setup() {
|
||||
const results = await Promise.all([init(), init(), init()]);
|
||||
|
||||
const servers = results.map(({ io }) => io) as [Server, Server, Server];
|
||||
const serverSockets = results.map(({ socket }) => socket) as [
|
||||
ServerSocket,
|
||||
ServerSocket,
|
||||
ServerSocket,
|
||||
];
|
||||
const clientSockets = results.map(({ clientSocket }) => clientSocket) as [
|
||||
ClientSocket,
|
||||
ClientSocket,
|
||||
ClientSocket,
|
||||
];
|
||||
const cleanupMethods = results.map(({ cleanup }) => cleanup);
|
||||
|
||||
return {
|
||||
servers,
|
||||
serverSockets,
|
||||
clientSockets,
|
||||
cleanup: () => {
|
||||
for (const cleanup of cleanupMethods) {
|
||||
cleanup();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
13
packages/socket.io-redis-streams-emitter/tsconfig.json
Normal file
13
packages/socket.io-redis-streams-emitter/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"allowJs": false,
|
||||
"target": "es2022",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": [
|
||||
"./lib/**/*"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
# History
|
||||
# Changelog
|
||||
|
||||
| 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 |
|
||||
| [4.7.5](#475-2024-03-14) | March 2024 |
|
||||
@@ -49,7 +51,37 @@
|
||||
| [2.1.0](#210-2018-03-29) | March 2018 |
|
||||
|
||||
|
||||
# Release notes
|
||||
## [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)).
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* call adapter.init() when creating each namespace ([f3e1f5e](https://github.com/socketio/socket.io/commit/f3e1f5ebdf59158d0c8d1e20f8230275617fb355))
|
||||
* improve `io.close()` function ([#5344](https://github.com/socketio/socket.io/issues/5344)) ([bb0b480](https://github.com/socketio/socket.io/commit/bb0b480d2ab3108a8ae255b539015da451fdb249))
|
||||
|
||||
|
||||
### 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) ([diff](https://github.com/websockets/ws/compare/8.17.1...8.18.3))
|
||||
|
||||
|
||||
|
||||
## [4.8.1](https://github.com/socketio/socket.io/compare/socket.io@4.8.0...socket.io@4.8.1) (2024-10-25)
|
||||
|
||||
|
||||
8
packages/socket.io/RELEASING.md
Normal file
8
packages/socket.io/RELEASING.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Releasing
|
||||
|
||||
1. Update the version in `package.json`
|
||||
2. Update the changelog `CHANGELOG.md` with `conventional-changelog -p angular`
|
||||
3. Copy the bundles from the client project to `client-dist/`
|
||||
4. Commit the changes in `package.json`, `CHANGELOG.md` and `client-dist/`
|
||||
5. Create the tag `socket.io@x.y.z` and push it to the GitHub repository. The workflow `.github/workflows/publish.yml` will safely publish the package to npm using trusted publishing.
|
||||
6. Create a new release at https://github.com/socketio/socket.io/releases
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user