mirror of
https://github.com/socketio/socket.io.git
synced 2026-01-11 16:08:24 -05:00
Compare commits
87 Commits
engine.io-
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ac3df9a747 | ||
|
|
21fd54ece6 | ||
|
|
96d907b9b5 | ||
|
|
32257b6cb8 | ||
|
|
c7144920e3 | ||
|
|
42480e9a7f | ||
|
|
0a8f91047c | ||
|
|
a66ed68506 | ||
|
|
3be6481d9d | ||
|
|
be13cca94c | ||
|
|
e95f6abf93 | ||
|
|
72d61dab82 | ||
|
|
5a31aaf917 | ||
|
|
62e4da125e | ||
|
|
bfa6eab195 | ||
|
|
7fcddcb3bb | ||
|
|
7427109658 | ||
|
|
91e1c8b358 | ||
|
|
8d5528aa2a | ||
|
|
71387e5294 | ||
|
|
aead83560d | ||
|
|
56a53bceb9 | ||
|
|
683720a67d | ||
|
|
a529eb08d6 | ||
|
|
cddb78e5fa | ||
|
|
15fd56e78d | ||
|
|
e86ef45f87 | ||
|
|
fe840e2eb3 | ||
|
|
a5a1c29082 | ||
|
|
66b4079953 | ||
|
|
be0a0e3217 | ||
|
|
43f9ee8d23 | ||
|
|
055b7840d8 | ||
|
|
6397c1bdfd | ||
|
|
ff370cfc46 | ||
|
|
1f8a6c4ecb | ||
|
|
eb01ff5803 | ||
|
|
f2e3d162ab |
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@@ -20,9 +20,9 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version:
|
||||
- 18
|
||||
- 20
|
||||
- 22
|
||||
- 24
|
||||
|
||||
services:
|
||||
redis:
|
||||
@@ -35,12 +35,24 @@ jobs:
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
postgres:
|
||||
image: postgres:14
|
||||
env:
|
||||
POSTGRES_PASSWORD: changeit
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
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 }}
|
||||
|
||||
|
||||
10
.github/workflows/publish.yml
vendored
10
.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
|
||||
|
||||
@@ -19,10 +19,10 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js 20
|
||||
- name: Use Node.js 24
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 24
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -32,6 +32,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) |
|
||||
|
||||
@@ -17,16 +17,35 @@ function sleep(delay) {
|
||||
return new Promise((resolve) => setTimeout(resolve, delay));
|
||||
}
|
||||
|
||||
function createWebSocket(url) {
|
||||
const socket = new WebSocket(url);
|
||||
socket._eventBuffer = {};
|
||||
socket._pendingPromises = {};
|
||||
|
||||
for (const eventType of ["open", "close", "message"]) {
|
||||
socket._eventBuffer[eventType] = [];
|
||||
socket._pendingPromises[eventType] = [];
|
||||
|
||||
socket.addEventListener(eventType, (event) => {
|
||||
if (socket._pendingPromises[eventType].length) {
|
||||
socket._pendingPromises[eventType].shift()(event);
|
||||
} else {
|
||||
socket._eventBuffer[eventType].push(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
function waitFor(socket, eventType) {
|
||||
return new Promise((resolve) => {
|
||||
socket.addEventListener(
|
||||
eventType,
|
||||
(event) => {
|
||||
resolve(event);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
if (socket._eventBuffer[eventType].length) {
|
||||
return Promise.resolve(socket._eventBuffer[eventType].shift());
|
||||
} else {
|
||||
return new Promise((resolve) => {
|
||||
socket._pendingPromises[eventType].push(resolve);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function initLongPollingSession() {
|
||||
@@ -110,7 +129,7 @@ describe("Engine.IO protocol", () => {
|
||||
|
||||
describe("WebSocket", () => {
|
||||
it("successfully opens a session", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -137,7 +156,7 @@ describe("Engine.IO protocol", () => {
|
||||
});
|
||||
|
||||
it("fails with an invalid 'EIO' query parameter", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/engine.io/?transport=websocket`
|
||||
);
|
||||
|
||||
@@ -145,9 +164,9 @@ describe("Engine.IO protocol", () => {
|
||||
socket.on("error", () => {});
|
||||
}
|
||||
|
||||
waitFor(socket, "close");
|
||||
await waitFor(socket, "close");
|
||||
|
||||
const socket2 = new WebSocket(
|
||||
const socket2 = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=abc&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -155,19 +174,19 @@ describe("Engine.IO protocol", () => {
|
||||
socket2.on("error", () => {});
|
||||
}
|
||||
|
||||
waitFor(socket2, "close");
|
||||
await waitFor(socket2, "close");
|
||||
});
|
||||
|
||||
it("fails with an invalid 'transport' query parameter", async () => {
|
||||
const socket = new WebSocket(`${WS_URL}/engine.io/?EIO=4`);
|
||||
const socket = createWebSocket(`${WS_URL}/engine.io/?EIO=4`);
|
||||
|
||||
if (isNodejs) {
|
||||
socket.on("error", () => {});
|
||||
}
|
||||
|
||||
waitFor(socket, "close");
|
||||
await waitFor(socket, "close");
|
||||
|
||||
const socket2 = new WebSocket(
|
||||
const socket2 = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=abc`
|
||||
);
|
||||
|
||||
@@ -175,7 +194,7 @@ describe("Engine.IO protocol", () => {
|
||||
socket2.on("error", () => {});
|
||||
}
|
||||
|
||||
waitFor(socket2, "close");
|
||||
await waitFor(socket2, "close");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -313,11 +332,30 @@ describe("Engine.IO protocol", () => {
|
||||
|
||||
expect(pollResponse.status).to.eql(400);
|
||||
});
|
||||
|
||||
it("closes the session upon cancelled polling request", async () => {
|
||||
const sid = await initLongPollingSession();
|
||||
const controller = new AbortController();
|
||||
|
||||
fetch(`${URL}/engine.io/?EIO=4&transport=polling&sid=${sid}`, {
|
||||
signal: controller.signal,
|
||||
}).catch(() => {});
|
||||
|
||||
await sleep(5);
|
||||
|
||||
controller.abort();
|
||||
|
||||
const pollResponse = await fetch(
|
||||
`${URL}/engine.io/?EIO=4&transport=polling&sid=${sid}`,
|
||||
);
|
||||
|
||||
expect(pollResponse.status).to.eql(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe("WebSocket", () => {
|
||||
it("sends and receives a plain text packet", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -335,7 +373,7 @@ describe("Engine.IO protocol", () => {
|
||||
});
|
||||
|
||||
it("sends and receives a binary packet", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
socket.binaryType = "arraybuffer";
|
||||
@@ -352,7 +390,7 @@ describe("Engine.IO protocol", () => {
|
||||
});
|
||||
|
||||
it("closes the session upon invalid packet format", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -412,7 +450,7 @@ describe("Engine.IO protocol", () => {
|
||||
|
||||
describe("WebSocket", () => {
|
||||
it("sends ping/pong packets", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -430,7 +468,7 @@ describe("Engine.IO protocol", () => {
|
||||
});
|
||||
|
||||
it("closes the session upon ping timeout", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -468,7 +506,7 @@ describe("Engine.IO protocol", () => {
|
||||
|
||||
describe("WebSocket", () => {
|
||||
it("forcefully closes the session", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -485,7 +523,7 @@ describe("Engine.IO protocol", () => {
|
||||
it("successfully upgrades from HTTP long-polling to WebSocket", async () => {
|
||||
const sid = await initLongPollingSession();
|
||||
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=websocket&sid=${sid}`
|
||||
);
|
||||
|
||||
@@ -521,12 +559,13 @@ describe("Engine.IO protocol", () => {
|
||||
it("ignores HTTP requests with same sid after upgrade", async () => {
|
||||
const sid = await initLongPollingSession();
|
||||
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=websocket&sid=${sid}`
|
||||
);
|
||||
|
||||
await waitFor(socket, "open");
|
||||
socket.send("2probe");
|
||||
await waitFor(socket, "message"); // "3probe"
|
||||
socket.send("5");
|
||||
|
||||
const pollResponse = await fetch(
|
||||
@@ -545,15 +584,16 @@ describe("Engine.IO protocol", () => {
|
||||
it("ignores WebSocket connection with same sid after upgrade", async () => {
|
||||
const sid = await initLongPollingSession();
|
||||
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=websocket&sid=${sid}`
|
||||
);
|
||||
|
||||
await waitFor(socket, "open");
|
||||
socket.send("2probe");
|
||||
await waitFor(socket, "message"); // "3probe"
|
||||
socket.send("5");
|
||||
|
||||
const socket2 = new WebSocket(
|
||||
const socket2 = createWebSocket(
|
||||
`${WS_URL}/engine.io/?EIO=4&transport=websocket&sid=${sid}`
|
||||
);
|
||||
|
||||
|
||||
@@ -17,16 +17,35 @@ function sleep(delay) {
|
||||
return new Promise((resolve) => setTimeout(resolve, delay));
|
||||
}
|
||||
|
||||
function createWebSocket(url) {
|
||||
const socket = new WebSocket(url);
|
||||
socket._eventBuffer = {};
|
||||
socket._pendingPromises = {};
|
||||
|
||||
for (const eventType of ["open", "close", "message"]) {
|
||||
socket._eventBuffer[eventType] = [];
|
||||
socket._pendingPromises[eventType] = [];
|
||||
|
||||
socket.addEventListener(eventType, (event) => {
|
||||
if (socket._pendingPromises[eventType].length) {
|
||||
socket._pendingPromises[eventType].shift()(event);
|
||||
} else {
|
||||
socket._eventBuffer[eventType].push(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
function waitFor(socket, eventType) {
|
||||
return new Promise((resolve) => {
|
||||
socket.addEventListener(
|
||||
eventType,
|
||||
(event) => {
|
||||
resolve(event);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
if (socket._eventBuffer[eventType].length) {
|
||||
return Promise.resolve(socket._eventBuffer[eventType].shift());
|
||||
} else {
|
||||
return new Promise((resolve) => {
|
||||
socket._pendingPromises[eventType].push(resolve);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function waitForPackets(socket, count) {
|
||||
@@ -55,7 +74,7 @@ async function initLongPollingSession() {
|
||||
}
|
||||
|
||||
async function initSocketIOConnection() {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
socket.binaryType = "arraybuffer";
|
||||
@@ -145,7 +164,7 @@ describe("Engine.IO protocol", () => {
|
||||
|
||||
describe("WebSocket", () => {
|
||||
it("should successfully open a session", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -172,7 +191,7 @@ describe("Engine.IO protocol", () => {
|
||||
});
|
||||
|
||||
it("should fail with an invalid 'EIO' query parameter", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?transport=websocket`
|
||||
);
|
||||
|
||||
@@ -180,9 +199,9 @@ describe("Engine.IO protocol", () => {
|
||||
socket.on("error", () => {});
|
||||
}
|
||||
|
||||
waitFor(socket, "close");
|
||||
await waitFor(socket, "close");
|
||||
|
||||
const socket2 = new WebSocket(
|
||||
const socket2 = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=abc&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -190,19 +209,19 @@ describe("Engine.IO protocol", () => {
|
||||
socket2.on("error", () => {});
|
||||
}
|
||||
|
||||
waitFor(socket2, "close");
|
||||
await waitFor(socket2, "close");
|
||||
});
|
||||
|
||||
it("should fail with an invalid 'transport' query parameter", async () => {
|
||||
const socket = new WebSocket(`${WS_URL}/socket.io/?EIO=4`);
|
||||
const socket = createWebSocket(`${WS_URL}/socket.io/?EIO=4`);
|
||||
|
||||
if (isNodejs) {
|
||||
socket.on("error", () => {});
|
||||
}
|
||||
|
||||
waitFor(socket, "close");
|
||||
await waitFor(socket, "close");
|
||||
|
||||
const socket2 = new WebSocket(
|
||||
const socket2 = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=abc`
|
||||
);
|
||||
|
||||
@@ -210,7 +229,7 @@ describe("Engine.IO protocol", () => {
|
||||
socket2.on("error", () => {});
|
||||
}
|
||||
|
||||
waitFor(socket2, "close");
|
||||
await waitFor(socket2, "close");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -260,7 +279,7 @@ describe("Engine.IO protocol", () => {
|
||||
|
||||
describe("WebSocket", () => {
|
||||
it("should send ping/pong packets", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -278,7 +297,7 @@ describe("Engine.IO protocol", () => {
|
||||
});
|
||||
|
||||
it("should close the session upon ping timeout", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -316,7 +335,7 @@ describe("Engine.IO protocol", () => {
|
||||
|
||||
describe("WebSocket", () => {
|
||||
it("should forcefully close the session", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -333,7 +352,7 @@ describe("Engine.IO protocol", () => {
|
||||
it("should successfully upgrade from HTTP long-polling to WebSocket", async () => {
|
||||
const sid = await initLongPollingSession();
|
||||
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket&sid=${sid}`
|
||||
);
|
||||
|
||||
@@ -353,12 +372,13 @@ describe("Engine.IO protocol", () => {
|
||||
it("should ignore HTTP requests with same sid after upgrade", async () => {
|
||||
const sid = await initLongPollingSession();
|
||||
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket&sid=${sid}`
|
||||
);
|
||||
|
||||
await waitFor(socket, "open");
|
||||
socket.send("2probe");
|
||||
await waitFor(socket, "message"); // "3probe"
|
||||
socket.send("5");
|
||||
|
||||
const pollResponse = await fetch(
|
||||
@@ -371,15 +391,16 @@ describe("Engine.IO protocol", () => {
|
||||
it("should ignore WebSocket connection with same sid after upgrade", async () => {
|
||||
const sid = await initLongPollingSession();
|
||||
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket&sid=${sid}`
|
||||
);
|
||||
|
||||
await waitFor(socket, "open");
|
||||
socket.send("2probe");
|
||||
await waitFor(socket, "message"); // "3probe"
|
||||
socket.send("5");
|
||||
|
||||
const socket2 = new WebSocket(
|
||||
const socket2 = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket&sid=${sid}`
|
||||
);
|
||||
|
||||
@@ -391,7 +412,7 @@ describe("Engine.IO protocol", () => {
|
||||
describe("Socket.IO protocol", () => {
|
||||
describe("connect", () => {
|
||||
it("should allow connection to the main namespace", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -414,7 +435,7 @@ describe("Socket.IO protocol", () => {
|
||||
});
|
||||
|
||||
it("should allow connection to the main namespace with a payload", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -437,7 +458,7 @@ describe("Socket.IO protocol", () => {
|
||||
});
|
||||
|
||||
it("should allow connection to a custom namespace", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -460,7 +481,7 @@ describe("Socket.IO protocol", () => {
|
||||
});
|
||||
|
||||
it("should allow connection to a custom namespace with a payload", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -483,7 +504,7 @@ describe("Socket.IO protocol", () => {
|
||||
});
|
||||
|
||||
it("should disallow connection to an unknown namespace", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -497,7 +518,7 @@ describe("Socket.IO protocol", () => {
|
||||
});
|
||||
|
||||
it("should disallow connection with an invalid handshake", async () => {
|
||||
const socket = new WebSocket(
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
@@ -508,10 +529,9 @@ describe("Socket.IO protocol", () => {
|
||||
await waitFor(socket, "close");
|
||||
});
|
||||
|
||||
|
||||
it("should close the connection if no handshake is received", async () => {
|
||||
const socket = new WebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
const socket = createWebSocket(
|
||||
`${WS_URL}/socket.io/?EIO=4&transport=websocket`
|
||||
);
|
||||
|
||||
await waitFor(socket, "close");
|
||||
|
||||
@@ -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",
|
||||
|
||||
4800
package-lock.json
generated
4800
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -8,14 +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",
|
||||
"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",
|
||||
@@ -29,10 +32,13 @@
|
||||
"@rollup/plugin-commonjs": "^26.0.1",
|
||||
"@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",
|
||||
"@types/node": "18.15.3",
|
||||
"@types/pg": "^8.6.0",
|
||||
"@types/sinonjs__fake-timers": "^8.1.5",
|
||||
"@wdio/cli": "^8.39.1",
|
||||
"@wdio/local-runner": "^8.39.1",
|
||||
@@ -43,6 +49,7 @@
|
||||
"base64-arraybuffer": "^1.0.2",
|
||||
"benchmark": "^2.1.4",
|
||||
"blob": "^0.1.0",
|
||||
"cookie": "~0.7.2",
|
||||
"eiows": "^7.1.0",
|
||||
"engine.io-client-v3": "npm:engine.io-client@^3.5.2",
|
||||
"expect.js": "^0.3.1",
|
||||
@@ -54,6 +61,7 @@
|
||||
"mocha": "^10.6.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"nyc": "^17.0.0",
|
||||
"pg": "^8.6.0",
|
||||
"prettier": "^3.3.2",
|
||||
"redis": "^4.6.15",
|
||||
"rimraf": "^6.0.0",
|
||||
@@ -68,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,9 @@
|
||||
# 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` |
|
||||
| [6.6.0](#660-2024-06-21) | June 2024 | `8.6 KB` |
|
||||
@@ -38,7 +40,38 @@
|
||||
| [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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* correctly consume the `ws` package ([#5220](https://github.com/socketio/socket.io/issues/5220)) ([7fcddcb](https://github.com/socketio/socket.io/commit/7fcddcb3bbd236b46aa8fee6f4ce6c45afb7b03a))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [`ws@~8.17.1`](https://github.com/websockets/ws/releases/tag/8.17.1) (no change)
|
||||
|
||||
|
||||
|
||||
## [6.6.2](https://github.com/socketio/socket.io/compare/engine.io-client@6.6.1...engine.io-client@6.6.2) (2024-10-23)
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
174
packages/engine.io-client/dist/engine.io.js
vendored
174
packages/engine.io-client/dist/engine.io.js
vendored
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Engine.IO v6.6.2
|
||||
* (c) 2014-2024 Guillermo Rauch
|
||||
* Engine.IO v6.6.4
|
||||
* (c) 2014-2025 Guillermo Rauch
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
(function (global, factory) {
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WebSocket } from "ws";
|
||||
import * as ws from "ws";
|
||||
import type { Packet, RawData } from "engine.io-parser";
|
||||
import { BaseWS } from "./websocket.js";
|
||||
|
||||
@@ -27,7 +27,7 @@ export class WS extends BaseWS {
|
||||
opts.headers.cookie.push(`${name}=${cookie.value}`);
|
||||
}
|
||||
}
|
||||
return new WebSocket(uri, protocols, opts);
|
||||
return new ws.WebSocket(uri, protocols, opts);
|
||||
}
|
||||
|
||||
doWrite(packet: Packet, data: RawData) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "engine.io-client",
|
||||
"description": "Client for the realtime Engine",
|
||||
"license": "MIT",
|
||||
"version": "6.6.2",
|
||||
"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"],
|
||||
|
||||
@@ -32,7 +32,11 @@ export type RawData = any;
|
||||
|
||||
export interface Packet {
|
||||
type: PacketType;
|
||||
options?: { compress: boolean };
|
||||
options?: {
|
||||
compress: boolean;
|
||||
wsPreEncoded?: string; // deprecated in favor of `wsPreEncodedFrame`
|
||||
wsPreEncodedFrame?: any; // computed in the socket.io-adapter package (should be typed as Buffer)
|
||||
};
|
||||
data?: RawData;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,7 +1,10 @@
|
||||
# History
|
||||
# Changelog
|
||||
|
||||
| Version | Release date |
|
||||
|------------------------------------------------------------------------------------------------------|----------------|
|
||||
| [6.6.5](#665-2025-12-22) | December 2025 |
|
||||
| [6.6.4](#664-2025-01-28) | January 2025 |
|
||||
| [6.6.3](#663-2025-01-23) | January 2025 |
|
||||
| [6.6.2](#662-2024-10-09) | October 2024 |
|
||||
| [6.6.1](#661-2024-09-21) | September 2024 |
|
||||
| [6.6.0](#660-2024-06-21) | June 2024 |
|
||||
@@ -45,7 +48,40 @@
|
||||
| [3.4.1](#341-2020-04-17) | April 2020 |
|
||||
|
||||
|
||||
# Release notes
|
||||
## [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))
|
||||
|
||||
|
||||
|
||||
## [6.6.4](https://github.com/socketio/socket.io/compare/engine.io@6.6.3...engine.io@6.6.4) (2025-01-28)
|
||||
|
||||
The bump of the `cookie` dependency was reverted, as it drops support for older Node.js versions (< 14).
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [`ws@~8.17.1`](https://github.com/websockets/ws/releases/tag/8.17.1) (no change)
|
||||
|
||||
|
||||
|
||||
## [6.6.3](https://github.com/socketio/socket.io/compare/engine.io@6.6.2...engine.io@6.6.3) (2025-01-23)
|
||||
|
||||
This release contains a bump of the `cookie` dependency.
|
||||
|
||||
Release notes: https://github.com/jshttp/cookie/releases/tag/v1.0.0
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [`ws@~8.17.1`](https://github.com/websockets/ws/releases/tag/8.17.1) (no change)
|
||||
|
||||
|
||||
|
||||
## [6.6.2](https://github.com/socketio/socket.io/compare/engine.io@6.6.1...engine.io@6.6.2) (2024-10-09)
|
||||
|
||||
|
||||
117
packages/engine.io/lib/contrib/types.cookie.ts
Normal file
117
packages/engine.io/lib/contrib/types.cookie.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
// imported from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b83cf9ef8b044e69f05b2a00aa7c6cb767a9acd2/types/cookie/index.d.ts (now deleted)
|
||||
/**
|
||||
* Basic HTTP cookie parser and serializer for HTTP servers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Additional serialization options
|
||||
*/
|
||||
export interface CookieSerializeOptions {
|
||||
/**
|
||||
* Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.3|Domain Set-Cookie attribute}. By default, no
|
||||
* domain is set, and most clients will consider the cookie to apply to only
|
||||
* the current domain.
|
||||
*/
|
||||
domain?: string | undefined;
|
||||
|
||||
/**
|
||||
* Specifies a function that will be used to encode a cookie's value. Since
|
||||
* value of a cookie has a limited character set (and must be a simple
|
||||
* string), this function can be used to encode a value into a string suited
|
||||
* for a cookie's value.
|
||||
*
|
||||
* The default function is the global `encodeURIComponent`, which will
|
||||
* encode a JavaScript string into UTF-8 byte sequences and then URL-encode
|
||||
* any that fall outside of the cookie range.
|
||||
*/
|
||||
encode?(value: string): string;
|
||||
|
||||
/**
|
||||
* Specifies the `Date` object to be the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.1|`Expires` `Set-Cookie` attribute}. By default,
|
||||
* no expiration is set, and most clients will consider this a "non-persistent cookie" and will delete
|
||||
* it on a condition like exiting a web browser application.
|
||||
*
|
||||
* *Note* the {@link https://tools.ietf.org/html/rfc6265#section-5.3|cookie storage model specification}
|
||||
* states that if both `expires` and `maxAge` are set, then `maxAge` takes precedence, but it is
|
||||
* possible not all clients by obey this, so if both are set, they should
|
||||
* point to the same date and time.
|
||||
*/
|
||||
expires?: Date | undefined;
|
||||
/**
|
||||
* Specifies the boolean value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.6|`HttpOnly` `Set-Cookie` attribute}.
|
||||
* When truthy, the `HttpOnly` attribute is set, otherwise it is not. By
|
||||
* default, the `HttpOnly` attribute is not set.
|
||||
*
|
||||
* *Note* be careful when setting this to true, as compliant clients will
|
||||
* not allow client-side JavaScript to see the cookie in `document.cookie`.
|
||||
*/
|
||||
httpOnly?: boolean | undefined;
|
||||
/**
|
||||
* Specifies the number (in seconds) to be the value for the `Max-Age`
|
||||
* `Set-Cookie` attribute. The given number will be converted to an integer
|
||||
* by rounding down. By default, no maximum age is set.
|
||||
*
|
||||
* *Note* the {@link https://tools.ietf.org/html/rfc6265#section-5.3|cookie storage model specification}
|
||||
* states that if both `expires` and `maxAge` are set, then `maxAge` takes precedence, but it is
|
||||
* possible not all clients by obey this, so if both are set, they should
|
||||
* point to the same date and time.
|
||||
*/
|
||||
maxAge?: number | undefined;
|
||||
/**
|
||||
* Specifies the `boolean` value for the [`Partitioned` `Set-Cookie`](rfc-cutler-httpbis-partitioned-cookies)
|
||||
* attribute. When truthy, the `Partitioned` attribute is set, otherwise it is not. By default, the
|
||||
* `Partitioned` attribute is not set.
|
||||
*
|
||||
* **note** This is an attribute that has not yet been fully standardized, and may change in the future.
|
||||
* This also means many clients may ignore this attribute until they understand it.
|
||||
*
|
||||
* More information about can be found in [the proposal](https://github.com/privacycg/CHIPS)
|
||||
*/
|
||||
partitioned?: boolean | undefined;
|
||||
/**
|
||||
* Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4|`Path` `Set-Cookie` attribute}.
|
||||
* By default, the path is considered the "default path".
|
||||
*/
|
||||
path?: string | undefined;
|
||||
/**
|
||||
* Specifies the `string` to be the value for the [`Priority` `Set-Cookie` attribute][rfc-west-cookie-priority-00-4.1].
|
||||
*
|
||||
* - `'low'` will set the `Priority` attribute to `Low`.
|
||||
* - `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.
|
||||
* - `'high'` will set the `Priority` attribute to `High`.
|
||||
*
|
||||
* More information about the different priority levels can be found in
|
||||
* [the specification][rfc-west-cookie-priority-00-4.1].
|
||||
*
|
||||
* **note** This is an attribute that has not yet been fully standardized, and may change in the future.
|
||||
* This also means many clients may ignore this attribute until they understand it.
|
||||
*/
|
||||
priority?: "low" | "medium" | "high" | undefined;
|
||||
/**
|
||||
* Specifies the boolean or string to be the value for the {@link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7|`SameSite` `Set-Cookie` attribute}.
|
||||
*
|
||||
* - `true` will set the `SameSite` attribute to `Strict` for strict same
|
||||
* site enforcement.
|
||||
* - `false` will not set the `SameSite` attribute.
|
||||
* - `'lax'` will set the `SameSite` attribute to Lax for lax same site
|
||||
* enforcement.
|
||||
* - `'strict'` will set the `SameSite` attribute to Strict for strict same
|
||||
* site enforcement.
|
||||
* - `'none'` will set the SameSite attribute to None for an explicit
|
||||
* cross-site cookie.
|
||||
*
|
||||
* More information about the different enforcement levels can be found in {@link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7|the specification}.
|
||||
*
|
||||
* *note* This is an attribute that has not yet been fully standardized, and may change in the future. This also means many clients may ignore this attribute until they understand it.
|
||||
*/
|
||||
sameSite?: true | false | "lax" | "strict" | "none" | undefined;
|
||||
/**
|
||||
* Specifies the boolean value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.5|`Secure` `Set-Cookie` attribute}. When truthy, the
|
||||
* `Secure` attribute is set, otherwise it is not. By default, the `Secure` attribute is not set.
|
||||
*
|
||||
* *Note* be careful when setting this to `true`, as compliant clients will
|
||||
* not send the cookie back to the server in the future if the browser does
|
||||
* not have an HTTPS connection.
|
||||
*/
|
||||
secure?: boolean | undefined;
|
||||
}
|
||||
@@ -1,27 +1,36 @@
|
||||
import { createServer } from "http";
|
||||
import { createServer, Server as HttpServer } from "http";
|
||||
import { Server, AttachOptions, ServerOptions } from "./server";
|
||||
import transports from "./transports/index";
|
||||
import * as parser from "engine.io-parser";
|
||||
|
||||
export { Server, transports, listen, attach, parser };
|
||||
export type { AttachOptions, ServerOptions, BaseServer } from "./server";
|
||||
export type {
|
||||
AttachOptions,
|
||||
ServerOptions,
|
||||
BaseServer,
|
||||
ErrorCallback,
|
||||
} from "./server";
|
||||
export { uServer } from "./userver";
|
||||
export { Socket } from "./socket";
|
||||
export { Transport } from "./transport";
|
||||
export const protocol = parser.protocol;
|
||||
|
||||
/**
|
||||
* Creates an http.Server exclusively used for WS upgrades.
|
||||
* Creates an http.Server exclusively used for WS upgrades, and starts listening.
|
||||
*
|
||||
* @param {Number} port
|
||||
* @param {Function} callback
|
||||
* @param {Object} options
|
||||
* @return {Server} websocket.io server
|
||||
* @param port
|
||||
* @param options
|
||||
* @param listenCallback - callback for http.Server.listen()
|
||||
* @return engine.io server
|
||||
*/
|
||||
|
||||
function listen(port, options: AttachOptions & ServerOptions, fn) {
|
||||
function listen(
|
||||
port: number,
|
||||
options?: AttachOptions & ServerOptions,
|
||||
listenCallback?: () => void,
|
||||
): Server {
|
||||
if ("function" === typeof options) {
|
||||
fn = options;
|
||||
listenCallback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
@@ -34,7 +43,7 @@ function listen(port, options: AttachOptions & ServerOptions, fn) {
|
||||
const engine = attach(server, options);
|
||||
engine.httpServer = server;
|
||||
|
||||
server.listen(port, fn);
|
||||
server.listen(port, listenCallback);
|
||||
|
||||
return engine;
|
||||
}
|
||||
@@ -42,12 +51,15 @@ function listen(port, options: AttachOptions & ServerOptions, fn) {
|
||||
/**
|
||||
* Captures upgrade requests for a http.Server.
|
||||
*
|
||||
* @param {http.Server} server
|
||||
* @param {Object} options
|
||||
* @return {Server} engine server
|
||||
* @param server
|
||||
* @param options
|
||||
* @return engine.io server
|
||||
*/
|
||||
|
||||
function attach(server, options: AttachOptions & ServerOptions) {
|
||||
function attach(
|
||||
server: HttpServer,
|
||||
options: AttachOptions & ServerOptions,
|
||||
): Server {
|
||||
const engine = new Server(options);
|
||||
engine.attach(server, options);
|
||||
return engine;
|
||||
|
||||
@@ -59,8 +59,7 @@ const EMPTY_BUFFER = Buffer.concat([]);
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
export function encodePacket (packet, supportsBinary, utf8encode, callback) {
|
||||
export function encodePacket (packet: any, supportsBinary?: any, utf8encode?: any, callback?: any) {
|
||||
if (typeof supportsBinary === 'function') {
|
||||
callback = supportsBinary;
|
||||
supportsBinary = null;
|
||||
@@ -86,7 +85,7 @@ export function encodePacket (packet, supportsBinary, utf8encode, callback) {
|
||||
}
|
||||
|
||||
return callback('' + encoded);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode Buffer data
|
||||
@@ -120,16 +119,16 @@ export function encodeBase64Packet (packet, callback){
|
||||
/**
|
||||
* Decodes a packet. Data also available as an ArrayBuffer if requested.
|
||||
*
|
||||
* @return {Object} with `type` and `data` (if any)
|
||||
* @return {import('engine.io-parser').Packet} with `type` and `data` (if any)
|
||||
* @api private
|
||||
*/
|
||||
|
||||
export function decodePacket (data, binaryType, utf8decode) {
|
||||
export function decodePacket (data: any, binaryType?: any, utf8decode?: any): any {
|
||||
if (data === undefined) {
|
||||
return err;
|
||||
}
|
||||
|
||||
var type;
|
||||
let type: string | number;
|
||||
|
||||
// String data
|
||||
if (typeof data === 'string') {
|
||||
@@ -147,6 +146,7 @@ export function decodePacket (data, binaryType, utf8decode) {
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
if (Number(type) != type || !packetslist[type]) {
|
||||
return err;
|
||||
}
|
||||
@@ -274,7 +274,7 @@ function map(ary, each, done) {
|
||||
* @api public
|
||||
*/
|
||||
|
||||
export function decodePayload (data, binaryType, callback) {
|
||||
export function decodePayload (data: any, binaryType?: any, callback?: any) {
|
||||
if (typeof data !== 'string') {
|
||||
return decodePayloadAsBinary(data, binaryType, callback);
|
||||
}
|
||||
|
||||
@@ -1,29 +1,37 @@
|
||||
import * as qs from "querystring";
|
||||
import { parse } from "url";
|
||||
import * as base64id from "base64id";
|
||||
import transports from "./transports";
|
||||
import { EventEmitter } from "events";
|
||||
import { Socket } from "./socket";
|
||||
import debugModule from "debug";
|
||||
import { serialize } from "cookie";
|
||||
import { Server as DEFAULT_WS_ENGINE } from "ws";
|
||||
import {
|
||||
Server as DEFAULT_WS_ENGINE,
|
||||
type Server as WsServer,
|
||||
type PerMessageDeflateOptions,
|
||||
type WebSocket as WsWebSocket,
|
||||
} from "ws";
|
||||
import type {
|
||||
IncomingMessage,
|
||||
Server as HttpServer,
|
||||
ServerResponse,
|
||||
} from "http";
|
||||
import type { CookieSerializeOptions } from "cookie";
|
||||
import type { CorsOptions, CorsOptionsDelegate } from "cors";
|
||||
import type { Duplex } from "stream";
|
||||
import { WebTransport } from "./transports/webtransport";
|
||||
import { createPacketDecoderStream } from "engine.io-parser";
|
||||
import type { EngineRequest } from "./transport";
|
||||
import type { EngineRequest, Transport } from "./transport";
|
||||
import type { CookieSerializeOptions } from "./contrib/types.cookie";
|
||||
|
||||
const debug = debugModule("engine");
|
||||
|
||||
const kResponseHeaders = Symbol("responseHeaders");
|
||||
|
||||
type Transport = "polling" | "websocket" | "webtransport";
|
||||
type TransportName = "polling" | "websocket" | "webtransport";
|
||||
|
||||
export type ErrorCallback = (
|
||||
errorCode?: (typeof Server.errors)[keyof typeof Server.errors],
|
||||
errorContext?: Record<string, unknown> & { name?: string; message?: string },
|
||||
) => void;
|
||||
|
||||
export interface AttachOptions {
|
||||
/**
|
||||
@@ -90,7 +98,7 @@ export interface ServerOptions {
|
||||
*
|
||||
* @default ["polling", "websocket"]
|
||||
*/
|
||||
transports?: Transport[];
|
||||
transports?: TransportName[];
|
||||
/**
|
||||
* whether to allow transport upgrades
|
||||
* @default true
|
||||
@@ -100,7 +108,7 @@ export interface ServerOptions {
|
||||
* parameters of the WebSocket permessage-deflate extension (see ws module api docs). Set to false to disable.
|
||||
* @default false
|
||||
*/
|
||||
perMessageDeflate?: boolean | object;
|
||||
perMessageDeflate?: boolean | PerMessageDeflateOptions;
|
||||
/**
|
||||
* parameters of the http compression for the polling transports (see zlib api docs). Set to false to disable.
|
||||
* @default true
|
||||
@@ -149,7 +157,7 @@ type Middleware = (
|
||||
next: (err?: any) => void,
|
||||
) => void;
|
||||
|
||||
function parseSessionId(data: string) {
|
||||
function parseSessionId(data: string): string | undefined {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
if (typeof parsed.sid === "string") {
|
||||
@@ -224,7 +232,7 @@ export abstract class BaseServer extends EventEmitter {
|
||||
this.init();
|
||||
}
|
||||
|
||||
protected abstract init();
|
||||
protected abstract init(): void;
|
||||
|
||||
/**
|
||||
* Compute the pathname of the requests that are handled by the server
|
||||
@@ -244,10 +252,8 @@ export abstract class BaseServer extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Returns a list of available transports for upgrade given a certain transport.
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
public upgrades(transport: string) {
|
||||
public upgrades(transport: TransportName): string[] {
|
||||
if (!this.opts.allowUpgrades) return [];
|
||||
return transports[transport].upgradesTo || [];
|
||||
}
|
||||
@@ -259,17 +265,18 @@ export abstract class BaseServer extends EventEmitter {
|
||||
* @param upgrade - whether it's an upgrade request
|
||||
* @param fn
|
||||
* @protected
|
||||
* @return whether the request is valid
|
||||
*/
|
||||
protected verify(
|
||||
req: any,
|
||||
req: EngineRequest,
|
||||
upgrade: boolean,
|
||||
fn: (errorCode?: number, errorContext?: any) => void,
|
||||
) {
|
||||
fn: ErrorCallback,
|
||||
): void | boolean {
|
||||
// transport check
|
||||
const transport = req._query.transport;
|
||||
// WebTransport does not go through the verify() method, see the onWebTransportSession() method
|
||||
if (
|
||||
!~this.opts.transports.indexOf(transport) ||
|
||||
!~this.opts.transports.indexOf(transport as TransportName) ||
|
||||
transport === "webtransport"
|
||||
) {
|
||||
debug('unknown transport "%s"', transport);
|
||||
@@ -408,7 +415,7 @@ export abstract class BaseServer extends EventEmitter {
|
||||
*
|
||||
* @param {IncomingMessage} req - the request object
|
||||
*/
|
||||
public generateId(req: IncomingMessage) {
|
||||
public generateId(req: IncomingMessage): string | PromiseLike<string> {
|
||||
return base64id.generateId();
|
||||
}
|
||||
|
||||
@@ -422,9 +429,9 @@ export abstract class BaseServer extends EventEmitter {
|
||||
* @protected
|
||||
*/
|
||||
protected async handshake(
|
||||
transportName: string,
|
||||
req: any,
|
||||
closeConnection: (errorCode?: number, errorContext?: any) => void,
|
||||
transportName: TransportName,
|
||||
req: EngineRequest,
|
||||
closeConnection: ErrorCallback,
|
||||
) {
|
||||
const protocol = req._query.EIO === "4" ? 4 : 3; // 3rd revision by default
|
||||
if (protocol === 3 && !this.opts.allowEIO3) {
|
||||
@@ -600,7 +607,10 @@ export abstract class BaseServer extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract createTransport(transportName, req);
|
||||
protected abstract createTransport(
|
||||
transportName: TransportName,
|
||||
req: EngineRequest,
|
||||
);
|
||||
|
||||
/**
|
||||
* Protocol errors mappings.
|
||||
@@ -613,7 +623,7 @@ export abstract class BaseServer extends EventEmitter {
|
||||
BAD_REQUEST: 3,
|
||||
FORBIDDEN: 4,
|
||||
UNSUPPORTED_PROTOCOL_VERSION: 5,
|
||||
};
|
||||
} as const;
|
||||
|
||||
static errorMessages = {
|
||||
0: "Transport unknown",
|
||||
@@ -622,7 +632,7 @@ export abstract class BaseServer extends EventEmitter {
|
||||
3: "Bad request",
|
||||
4: "Forbidden",
|
||||
5: "Unsupported protocol version",
|
||||
};
|
||||
} as const;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -667,7 +677,7 @@ class WebSocketResponse {
|
||||
*/
|
||||
export class Server extends BaseServer {
|
||||
public httpServer?: HttpServer;
|
||||
private ws: any;
|
||||
private ws: WsServer;
|
||||
|
||||
/**
|
||||
* Initialize websocket server
|
||||
@@ -687,7 +697,7 @@ export class Server extends BaseServer {
|
||||
});
|
||||
|
||||
if (typeof this.ws.on === "function") {
|
||||
this.ws.on("headers", (headersArray, req) => {
|
||||
this.ws.on("headers", (headersArray, req: EngineRequest) => {
|
||||
// note: 'ws' uses an array of headers, while Engine.IO uses an object (response.writeHead() accepts both formats)
|
||||
// we could also try to parse the array and then sync the values, but that will be error-prone
|
||||
const additionalHeaders = req[kResponseHeaders] || {};
|
||||
@@ -724,13 +734,16 @@ 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());
|
||||
}
|
||||
}
|
||||
|
||||
protected createTransport(transportName: string, req: IncomingMessage) {
|
||||
protected createTransport(
|
||||
transportName: TransportName,
|
||||
req: IncomingMessage,
|
||||
): Transport {
|
||||
// @ts-expect-error 'polling' is a plain function used as constructor
|
||||
return new transports[transportName](req);
|
||||
}
|
||||
|
||||
@@ -745,7 +758,7 @@ export class Server extends BaseServer {
|
||||
this.prepare(req);
|
||||
req.res = res;
|
||||
|
||||
const callback = (errorCode, errorContext) => {
|
||||
const callback: ErrorCallback = (errorCode, errorContext) => {
|
||||
if (errorCode !== undefined) {
|
||||
this.emit("connection_error", {
|
||||
req,
|
||||
@@ -763,7 +776,11 @@ export class Server extends BaseServer {
|
||||
} else {
|
||||
const closeConnection = (errorCode, errorContext) =>
|
||||
abortRequest(res, errorCode, errorContext);
|
||||
this.handshake(req._query.transport, req, closeConnection);
|
||||
this.handshake(
|
||||
req._query.transport as TransportName,
|
||||
req,
|
||||
closeConnection,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -787,7 +804,7 @@ export class Server extends BaseServer {
|
||||
this.prepare(req);
|
||||
|
||||
const res = new WebSocketResponse(req, socket);
|
||||
const callback = (errorCode, errorContext) => {
|
||||
const callback: ErrorCallback = (errorCode, errorContext) => {
|
||||
if (errorCode !== undefined) {
|
||||
this.emit("connection_error", {
|
||||
req,
|
||||
@@ -823,11 +840,16 @@ export class Server extends BaseServer {
|
||||
|
||||
/**
|
||||
* Called upon a ws.io connection.
|
||||
*
|
||||
* @param {ws.Socket} websocket
|
||||
* @param req
|
||||
* @param socket
|
||||
* @param websocket
|
||||
* @private
|
||||
*/
|
||||
private onWebSocket(req, socket, websocket) {
|
||||
private onWebSocket(
|
||||
req: EngineRequest,
|
||||
socket: Duplex,
|
||||
websocket: WsWebSocket,
|
||||
) {
|
||||
websocket.on("error", onUpgradeError);
|
||||
|
||||
if (
|
||||
@@ -862,14 +884,22 @@ export class Server extends BaseServer {
|
||||
// transport error handling takes over
|
||||
websocket.removeListener("error", onUpgradeError);
|
||||
|
||||
const transport = this.createTransport(req._query.transport, req);
|
||||
const transport = this.createTransport(
|
||||
req._query.transport as TransportName,
|
||||
req,
|
||||
);
|
||||
// @ts-expect-error this option is only for WebSocket impl
|
||||
transport.perMessageDeflate = this.opts.perMessageDeflate;
|
||||
client._maybeUpgrade(transport);
|
||||
}
|
||||
} else {
|
||||
const closeConnection = (errorCode, errorContext) =>
|
||||
abortUpgrade(socket, errorCode, errorContext);
|
||||
this.handshake(req._query.transport, req, closeConnection);
|
||||
this.handshake(
|
||||
req._query.transport as TransportName,
|
||||
req,
|
||||
closeConnection,
|
||||
);
|
||||
}
|
||||
|
||||
function onUpgradeError() {
|
||||
@@ -947,7 +977,11 @@ export class Server extends BaseServer {
|
||||
* @private
|
||||
*/
|
||||
|
||||
function abortRequest(res, errorCode, errorContext) {
|
||||
function abortRequest(
|
||||
res: ServerResponse,
|
||||
errorCode: number,
|
||||
errorContext?: { message?: string },
|
||||
) {
|
||||
const statusCode = errorCode === Server.errors.FORBIDDEN ? 403 : 400;
|
||||
const message =
|
||||
errorContext && errorContext.message
|
||||
@@ -1030,7 +1064,7 @@ const validHdrChars = [
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // ... 255
|
||||
]
|
||||
|
||||
function checkInvalidHeaderChar(val) {
|
||||
function checkInvalidHeaderChar(val?: string) {
|
||||
val += "";
|
||||
if (val.length < 1) return false;
|
||||
if (!validHdrChars[val.charCodeAt(0)]) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { EngineRequest, Transport } from "./transport";
|
||||
import type { BaseServer } from "./server";
|
||||
import { setTimeout, clearTimeout } from "timers";
|
||||
import type { Packet, PacketType, RawData } from "engine.io-parser";
|
||||
import type transports from "./transports";
|
||||
|
||||
const debug = debugModule("engine:socket");
|
||||
|
||||
@@ -537,9 +538,11 @@ export class Socket extends EventEmitter {
|
||||
*/
|
||||
private getAvailableUpgrades() {
|
||||
const availableUpgrades = [];
|
||||
const allUpgrades = this.server.upgrades(this.transport.name);
|
||||
const allUpgrades = this.server.upgrades(
|
||||
this.transport.name as keyof typeof transports,
|
||||
);
|
||||
for (let i = 0; i < allUpgrades.length; ++i) {
|
||||
const upg = allUpgrades[i];
|
||||
const upg = allUpgrades[i] as keyof typeof transports;
|
||||
if (this.server.opts.transports.indexOf(upg) !== -1) {
|
||||
availableUpgrades.push(upg);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as parser_v3 from "./parser-v3/index";
|
||||
import debugModule from "debug";
|
||||
import type { IncomingMessage, ServerResponse } from "http";
|
||||
import { Packet, RawData } from "engine.io-parser";
|
||||
import type { WebSocket } from "ws";
|
||||
|
||||
const debug = debugModule("engine:transport");
|
||||
|
||||
@@ -15,7 +16,11 @@ export type EngineRequest = IncomingMessage & {
|
||||
_query: Record<string, string>;
|
||||
res?: ServerResponse;
|
||||
cleanup?: Function;
|
||||
websocket?: any;
|
||||
websocket?: WebSocket & {
|
||||
_socket?: {
|
||||
remoteAddress: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export abstract class Transport extends EventEmitter {
|
||||
@@ -37,7 +42,7 @@ export abstract class Transport extends EventEmitter {
|
||||
*
|
||||
* @see https://github.com/socketio/engine.io-protocol
|
||||
*/
|
||||
public protocol: number;
|
||||
public protocol: 3 | 4;
|
||||
|
||||
/**
|
||||
* The current state of the transport.
|
||||
@@ -53,7 +58,7 @@ export abstract class Transport extends EventEmitter {
|
||||
* The parser to use (depends on the revision of the {@link Transport#protocol}.
|
||||
* @protected
|
||||
*/
|
||||
protected parser: any;
|
||||
protected parser: typeof parser_v4 | typeof parser_v3;
|
||||
/**
|
||||
* Whether the transport supports binary payloads (else it will be base64-encoded)
|
||||
* @protected
|
||||
@@ -74,6 +79,11 @@ export abstract class Transport extends EventEmitter {
|
||||
this._readyState = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of transports this transport can be upgraded to.
|
||||
*/
|
||||
static upgradesTo: string[] = [];
|
||||
|
||||
/**
|
||||
* Transport constructor.
|
||||
*
|
||||
@@ -148,7 +158,7 @@ export abstract class Transport extends EventEmitter {
|
||||
/**
|
||||
* Called with the encoded packet data.
|
||||
*
|
||||
* @param {String} data
|
||||
* @param data
|
||||
* @protected
|
||||
*/
|
||||
protected onData(data: RawData) {
|
||||
|
||||
@@ -3,6 +3,8 @@ import { createGzip, createDeflate } from "zlib";
|
||||
import * as accepts from "accepts";
|
||||
import debugModule from "debug";
|
||||
import { HttpRequest, HttpResponse } from "uWebSockets.js";
|
||||
import type * as parser_v4 from "engine.io-parser";
|
||||
import type * as parser_v3 from "../parser-v3/index";
|
||||
|
||||
const debug = debugModule("engine:polling");
|
||||
|
||||
@@ -228,9 +230,9 @@ export class Polling extends Transport {
|
||||
};
|
||||
|
||||
if (this.protocol === 3) {
|
||||
this.parser.decodePayload(data, callback);
|
||||
(this.parser as typeof parser_v3).decodePayload(data, callback);
|
||||
} else {
|
||||
this.parser.decodePayload(data).forEach(callback);
|
||||
(this.parser as typeof parser_v4).decodePayload(data).forEach(callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +265,7 @@ export class Polling extends Transport {
|
||||
this.shouldClose = null;
|
||||
}
|
||||
|
||||
const doWrite = (data) => {
|
||||
const doWrite = (data: string) => {
|
||||
const compress = packets.some((packet) => {
|
||||
return packet.options && packet.options.compress;
|
||||
});
|
||||
@@ -271,9 +273,13 @@ export class Polling extends Transport {
|
||||
};
|
||||
|
||||
if (this.protocol === 3) {
|
||||
this.parser.encodePayload(packets, this.supportsBinary, doWrite);
|
||||
(this.parser as typeof parser_v3).encodePayload(
|
||||
packets,
|
||||
this.supportsBinary,
|
||||
doWrite,
|
||||
);
|
||||
} else {
|
||||
this.parser.encodePayload(packets, doWrite);
|
||||
(this.parser as typeof parser_v4).encodePayload(packets, doWrite);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ import { Polling as XHR } from "./polling";
|
||||
import { JSONP } from "./polling-jsonp";
|
||||
import { WebSocket } from "./websocket";
|
||||
import { WebTransport } from "./webtransport";
|
||||
import type { EngineRequest } from "../transport";
|
||||
|
||||
export default {
|
||||
polling: polling,
|
||||
polling,
|
||||
websocket: WebSocket,
|
||||
webtransport: WebTransport,
|
||||
};
|
||||
@@ -12,8 +13,7 @@ export default {
|
||||
/**
|
||||
* Polling polymorphic constructor.
|
||||
*/
|
||||
|
||||
function polling(req) {
|
||||
function polling(req: EngineRequest) {
|
||||
if ("string" === typeof req._query.j) {
|
||||
return new JSONP(req);
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,8 @@ import * as accepts from "accepts";
|
||||
import debugModule from "debug";
|
||||
import type { IncomingMessage, ServerResponse } from "http";
|
||||
import type { Packet, RawData } from "engine.io-parser";
|
||||
import type * as parser_v4 from "engine.io-parser";
|
||||
import type * as parser_v3 from "../parser-v3/index";
|
||||
|
||||
const debug = debugModule("engine:polling");
|
||||
|
||||
@@ -196,9 +198,9 @@ export class Polling extends Transport {
|
||||
};
|
||||
|
||||
if (this.protocol === 3) {
|
||||
this.parser.decodePayload(data, callback);
|
||||
(this.parser as typeof parser_v3).decodePayload(data, callback);
|
||||
} else {
|
||||
this.parser.decodePayload(data).forEach(callback);
|
||||
(this.parser as typeof parser_v4).decodePayload(data).forEach(callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +227,7 @@ export class Polling extends Transport {
|
||||
this.shouldClose = null;
|
||||
}
|
||||
|
||||
const doWrite = (data) => {
|
||||
const doWrite = (data: string) => {
|
||||
const compress = packets.some((packet) => {
|
||||
return packet.options && packet.options.compress;
|
||||
});
|
||||
@@ -233,9 +235,13 @@ export class Polling extends Transport {
|
||||
};
|
||||
|
||||
if (this.protocol === 3) {
|
||||
this.parser.encodePayload(packets, this.supportsBinary, doWrite);
|
||||
(this.parser as typeof parser_v3).encodePayload(
|
||||
packets,
|
||||
this.supportsBinary,
|
||||
doWrite,
|
||||
);
|
||||
} else {
|
||||
this.parser.encodePayload(packets, doWrite);
|
||||
(this.parser as typeof parser_v4).encodePayload(packets, doWrite);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { EngineRequest, Transport } from "../transport";
|
||||
import debugModule from "debug";
|
||||
import type { Packet, RawData } from "engine.io-parser";
|
||||
import type { PerMessageDeflateOptions, WebSocket as WsWebSocket } from "ws";
|
||||
|
||||
const debug = debugModule("engine:ws");
|
||||
|
||||
export class WebSocket extends Transport {
|
||||
protected perMessageDeflate: any;
|
||||
private socket: any;
|
||||
perMessageDeflate?: boolean | PerMessageDeflateOptions;
|
||||
private socket: WsWebSocket;
|
||||
|
||||
/**
|
||||
* WebSocket transport
|
||||
@@ -51,8 +52,8 @@ export class WebSocket extends Transport {
|
||||
if (this._canSendPreEncodedFrame(packet)) {
|
||||
// the WebSocket frame was computed with WebSocket.Sender.frame()
|
||||
// see https://github.com/websockets/ws/issues/617#issuecomment-283002469
|
||||
// @ts-expect-error use of untyped member
|
||||
this.socket._sender.sendFrame(
|
||||
// @ts-ignore
|
||||
packet.options.wsPreEncodedFrame,
|
||||
isLast ? this._onSentLast : this._onSent,
|
||||
);
|
||||
@@ -74,8 +75,8 @@ export class WebSocket extends Transport {
|
||||
private _canSendPreEncodedFrame(packet: Packet) {
|
||||
return (
|
||||
!this.perMessageDeflate &&
|
||||
// @ts-expect-error use of untyped member
|
||||
typeof this.socket?._sender?.sendFrame === "function" &&
|
||||
// @ts-ignore
|
||||
packet.options?.wsPreEncodedFrame !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import debugModule from "debug";
|
||||
import { AttachOptions, BaseServer, Server } from "./server";
|
||||
import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js";
|
||||
import transports from "./transports-uws";
|
||||
import { EngineRequest } from "./transport";
|
||||
|
||||
const debug = debugModule("engine:uws");
|
||||
|
||||
@@ -36,7 +37,7 @@ export class uServer extends BaseServer {
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private prepare(req, res: HttpResponse) {
|
||||
private prepare(req: HttpRequest & EngineRequest, res: HttpResponse) {
|
||||
req.method = req.getMethod().toUpperCase();
|
||||
req.url = req.getUrl();
|
||||
|
||||
@@ -48,6 +49,7 @@ export class uServer extends BaseServer {
|
||||
req.headers[key] = value;
|
||||
});
|
||||
|
||||
// @ts-expect-error
|
||||
req.connection = {
|
||||
remoteAddress: Buffer.from(res.getRemoteAddressAsText()).toString(),
|
||||
};
|
||||
@@ -57,7 +59,7 @@ export class uServer extends BaseServer {
|
||||
});
|
||||
}
|
||||
|
||||
protected createTransport(transportName, req) {
|
||||
protected createTransport(transportName: string, req: EngineRequest) {
|
||||
return new transports[transportName](req);
|
||||
}
|
||||
|
||||
@@ -123,7 +125,7 @@ export class uServer extends BaseServer {
|
||||
req: HttpRequest & { res: any; _query: any },
|
||||
) {
|
||||
debug('handling "%s" http request "%s"', req.getMethod(), req.getUrl());
|
||||
this.prepare(req, res);
|
||||
this.prepare(req as unknown as HttpRequest & EngineRequest, res);
|
||||
|
||||
req.res = res;
|
||||
|
||||
@@ -146,7 +148,11 @@ export class uServer extends BaseServer {
|
||||
} else {
|
||||
const closeConnection = (errorCode, errorContext) =>
|
||||
this.abortRequest(res, errorCode, errorContext);
|
||||
this.handshake(req._query.transport, req, closeConnection);
|
||||
this.handshake(
|
||||
req._query.transport,
|
||||
req as unknown as EngineRequest,
|
||||
closeConnection,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -154,7 +160,11 @@ export class uServer extends BaseServer {
|
||||
if (err) {
|
||||
callback(Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
|
||||
} else {
|
||||
this.verify(req, false, callback);
|
||||
this.verify(
|
||||
req as unknown as HttpRequest & EngineRequest,
|
||||
false,
|
||||
callback,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -166,7 +176,7 @@ export class uServer extends BaseServer {
|
||||
) {
|
||||
debug("on upgrade");
|
||||
|
||||
this.prepare(req, res);
|
||||
this.prepare(req as unknown as HttpRequest & EngineRequest, res);
|
||||
|
||||
req.res = res;
|
||||
|
||||
@@ -198,13 +208,16 @@ export class uServer extends BaseServer {
|
||||
return res.close();
|
||||
} else {
|
||||
debug("upgrading existing transport");
|
||||
transport = this.createTransport(req._query.transport, req);
|
||||
transport = this.createTransport(
|
||||
req._query.transport,
|
||||
req as unknown as EngineRequest,
|
||||
);
|
||||
client._maybeUpgrade(transport);
|
||||
}
|
||||
} else {
|
||||
transport = await this.handshake(
|
||||
req._query.transport,
|
||||
req,
|
||||
req as unknown as EngineRequest,
|
||||
(errorCode, errorContext) =>
|
||||
this.abortRequest(res, errorCode, errorContext),
|
||||
);
|
||||
@@ -231,15 +244,15 @@ export class uServer extends BaseServer {
|
||||
if (err) {
|
||||
callback(Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
|
||||
} else {
|
||||
this.verify(req, true, callback);
|
||||
this.verify(req as unknown as EngineRequest, true, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private abortRequest(
|
||||
res: HttpResponse | ResponseWrapper,
|
||||
errorCode,
|
||||
errorContext,
|
||||
errorCode: number,
|
||||
errorContext?: { message?: string },
|
||||
) {
|
||||
const statusCode =
|
||||
errorCode === Server.errors.FORBIDDEN
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "engine.io",
|
||||
"version": "6.6.2",
|
||||
"version": "6.6.5",
|
||||
"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",
|
||||
@@ -31,16 +31,15 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.4.1",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/node": ">=10.0.0",
|
||||
"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;
|
||||
}
|
||||
|
||||
|
||||
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-----
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
@@ -671,6 +679,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 +709,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 +808,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 @@
|
||||
import { EventEmitter } from "events";
|
||||
import { yeast } from "./contrib/yeast";
|
||||
import WebSocket = require("ws");
|
||||
import * as WebSocket from "ws";
|
||||
|
||||
// @ts-expect-error
|
||||
const canPreComputeFrame = typeof WebSocket?.Sender?.frame === "function";
|
||||
@@ -51,11 +51,11 @@ export class Adapter extends EventEmitter {
|
||||
/**
|
||||
* In-memory adapter constructor.
|
||||
*
|
||||
* @param {Namespace} nsp
|
||||
* @param nsp
|
||||
*/
|
||||
constructor(readonly nsp: any) {
|
||||
super();
|
||||
this.encoder = nsp.server.encoder;
|
||||
this.encoder = nsp.server.encoder; // nsp is a Namespace object
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -67,11 +67,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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
# 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` |
|
||||
| [4.7.4](#474-2024-01-12) | January 2024 | `14.5 KB` |
|
||||
@@ -49,9 +52,50 @@
|
||||
| [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)
|
||||
|
||||
# [4.8.0](https://github.com/socketio/socket.io/compare/socket.io-client@4.7.5...socket.io-client@4.8.0) (2024-09-21)
|
||||
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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bundle:** do not mangle the "_placeholder" attribute ([ca9e994](https://github.com/socketio/socket.io/commit/ca9e994815aa2e31e0342e37ccdc2e9e8c5fd13c))
|
||||
|
||||
|
||||
### 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.0](https://github.com/socketio/socket.io/compare/socket.io-client@4.7.5...socket.io-client@4.8.0) (2024-09-21)
|
||||
|
||||
### Features
|
||||
|
||||
|
||||
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
132
packages/socket.io-client/dist/socket.io.js
vendored
132
packages/socket.io-client/dist/socket.io.js
vendored
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Socket.IO v4.8.0
|
||||
* (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 "";
|
||||
@@ -1432,6 +1432,7 @@
|
||||
};
|
||||
_proto.doClose = function doClose() {
|
||||
if (typeof this.ws !== "undefined") {
|
||||
this.ws.onerror = function () {};
|
||||
this.ws.close();
|
||||
this.ws = null;
|
||||
}
|
||||
@@ -2668,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2692,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('');
|
||||
@@ -2707,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`.
|
||||
*
|
||||
@@ -2811,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+)/);
|
||||
}
|
||||
@@ -2895,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?
|
||||
@@ -3827,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) {
|
||||
@@ -4099,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,
|
||||
@@ -99,3 +100,12 @@ export {
|
||||
lookup as connect,
|
||||
lookup as default,
|
||||
};
|
||||
|
||||
export {
|
||||
Fetch,
|
||||
NodeXHR,
|
||||
XHR,
|
||||
NodeWebSocket,
|
||||
WebSocket,
|
||||
WebTransport,
|
||||
} from "engine.io-client";
|
||||
|
||||
@@ -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.0",
|
||||
"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/**/*"
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,10 @@
|
||||
import { Server, type ServerOptions, Socket, type Transport } from "engine.io";
|
||||
import {
|
||||
Server,
|
||||
type ServerOptions,
|
||||
type ErrorCallback,
|
||||
Socket,
|
||||
type Transport,
|
||||
} from "engine.io";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { setTimeout, clearTimeout } from "node:timers";
|
||||
import { type IncomingMessage } from "node:http";
|
||||
@@ -392,9 +398,9 @@ export abstract class ClusterEngine extends Server {
|
||||
override verify(
|
||||
req: IncomingMessage & { _query: Record<string, string> },
|
||||
upgrade: boolean,
|
||||
fn: (errorCode?: number, context?: any) => void,
|
||||
fn: ErrorCallback,
|
||||
): void {
|
||||
super.verify(req, upgrade, (errorCode: number, errorContext: any) => {
|
||||
super.verify(req, upgrade, (errorCode, errorContext) => {
|
||||
if (errorCode !== Server.errors.UNKNOWN_SID) {
|
||||
return fn(errorCode, errorContext);
|
||||
}
|
||||
@@ -412,7 +418,7 @@ export abstract class ClusterEngine extends Server {
|
||||
req[kSenderId] = senderId;
|
||||
fn();
|
||||
} else {
|
||||
const transport = this.createTransport(transportName, req);
|
||||
const transport = this.createTransport(transportName as any, req);
|
||||
this._hookTransport(sid, transport, lockType, senderId);
|
||||
transport.onRequest(req);
|
||||
}
|
||||
|
||||
@@ -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,7 +1,8 @@
|
||||
# History
|
||||
# Changelog
|
||||
|
||||
| Version | Release date |
|
||||
|-------------------------------------------------------------------------------------------------------------|----------------|
|
||||
| [4.2.5](#425-2025-12-23) | December 2025 |
|
||||
| [3.3.4](#334-2024-07-22) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | July 2024 |
|
||||
| [4.2.4](#424-2023-05-31) | May 2023 |
|
||||
| [3.4.3](#343-2023-05-22) (from the [3.4.x](https://github.com/socketio/socket.io-parser/tree/3.4.x) branch) | May 2023 |
|
||||
@@ -33,7 +34,11 @@
|
||||
| [3.3.0](#330-2018-11-07) | November 2018 |
|
||||
|
||||
|
||||
# Release notes
|
||||
## [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`.
|
||||
|
||||
|
||||
|
||||
## [3.3.4](https://github.com/Automattic/socket.io-parser/compare/3.3.3...3.3.4) (2024-07-22)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io-parser",
|
||||
"version": "4.2.4",
|
||||
"version": "4.2.5",
|
||||
"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",
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
{
|
||||
|
||||
19
packages/socket.io-postgres-emitter/CHANGELOG.md
Normal file
19
packages/socket.io-postgres-emitter/CHANGELOG.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# History
|
||||
|
||||
| Version | Release date |
|
||||
|--------------------------|----------------|
|
||||
| [0.1.1](#011-2025-09-05) | September 2025 |
|
||||
| [0.1.0](#010-2021-06-14) | June 2021 |
|
||||
|
||||
# Release notes
|
||||
|
||||
## 0.1.1 (2025-09-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use parameterized query to send the NOTIFY command ([32257b6](https://github.com/socketio/socket.io/commit/32257b6cb89f9dac15e69e1d6ee76365ff262170))
|
||||
|
||||
|
||||
## 0.1.0 (2021-06-14)
|
||||
|
||||
Initial release!
|
||||
7
packages/socket.io-postgres-emitter/LICENSE
Normal file
7
packages/socket.io-postgres-emitter/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.
|
||||
152
packages/socket.io-postgres-emitter/README.md
Normal file
152
packages/socket.io-postgres-emitter/README.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Socket.IO Postgres emitter
|
||||
|
||||
The `@socket.io/postgres-emitter` package allows you to easily communicate with a group of Socket.IO servers from another Node.js process (server-side).
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./assets/emitter_dark.png">
|
||||
<img alt="Diagram of Socket.IO packets forwarded through PostgreSQL" src="./assets/emitter.png">
|
||||
</picture>
|
||||
|
||||
It must be used in conjunction with [`@socket.io/postgres-adapter`](https://github.com/socketio/socket.io-postgres-adapter/).
|
||||
|
||||
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)
|
||||
- [`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/
|
||||
- Redis emitter: https://github.com/socketio/socket.io-redis-emitter/
|
||||
- MongoDB adapter: https://github.com/socketio/socket.io-mongo-adapter/
|
||||
- MongoDB emitter: https://github.com/socketio/socket.io-mongo-emitter/
|
||||
|
||||
**Table of contents**
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [API](#api)
|
||||
- [Known errors](#known-errors)
|
||||
- [License](#license)
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
npm install @socket.io/postgres-emitter pg
|
||||
```
|
||||
|
||||
For TypeScript users, you might also need `@types/pg`.
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
const { Emitter } = require("@socket.io/postgres-emitter");
|
||||
const { Pool } = require("pg");
|
||||
|
||||
const pool = new Pool({
|
||||
user: "postgres",
|
||||
host: "localhost",
|
||||
database: "postgres",
|
||||
password: "changeit",
|
||||
port: 5432,
|
||||
});
|
||||
|
||||
const io = new Emitter(pool);
|
||||
|
||||
setInterval(() => {
|
||||
io.emit("ping", new Date());
|
||||
}, 1000);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `Emitter(pool[, nsp][, opts])`
|
||||
|
||||
```js
|
||||
const io = new Emitter(pool);
|
||||
```
|
||||
|
||||
The `pool` argument is a [Pool object](https://node-postgres.com/api/pool) from the `pg` package.
|
||||
|
||||
### `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)
|
||||
1380
packages/socket.io-postgres-emitter/assets/emitter.excalidraw
Normal file
1380
packages/socket.io-postgres-emitter/assets/emitter.excalidraw
Normal file
File diff suppressed because it is too large
Load Diff
BIN
packages/socket.io-postgres-emitter/assets/emitter.png
Normal file
BIN
packages/socket.io-postgres-emitter/assets/emitter.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 89 KiB |
BIN
packages/socket.io-postgres-emitter/assets/emitter_dark.png
Normal file
BIN
packages/socket.io-postgres-emitter/assets/emitter_dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
7
packages/socket.io-postgres-emitter/compose.yaml
Normal file
7
packages/socket.io-postgres-emitter/compose.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_PASSWORD: "changeit"
|
||||
536
packages/socket.io-postgres-emitter/lib/index.ts
Normal file
536
packages/socket.io-postgres-emitter/lib/index.ts
Normal file
@@ -0,0 +1,536 @@
|
||||
import debugModule from "debug";
|
||||
import type {
|
||||
DefaultEventsMap,
|
||||
EventNames,
|
||||
EventParams,
|
||||
EventsMap,
|
||||
TypedEventBroadcaster,
|
||||
} from "./typed-events";
|
||||
import { encode } from "@msgpack/msgpack";
|
||||
|
||||
const debug = debugModule("socket.io-postgres-emitter");
|
||||
const EMITTER_UID = "emitter";
|
||||
|
||||
const 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event types, for messages between nodes
|
||||
*/
|
||||
|
||||
enum EventType {
|
||||
INITIAL_HEARTBEAT = 1,
|
||||
HEARTBEAT,
|
||||
BROADCAST,
|
||||
SOCKETS_JOIN,
|
||||
SOCKETS_LEAVE,
|
||||
DISCONNECT_SOCKETS,
|
||||
FETCH_SOCKETS,
|
||||
FETCH_SOCKETS_RESPONSE,
|
||||
SERVER_SIDE_EMIT,
|
||||
SERVER_SIDE_EMIT_RESPONSE,
|
||||
}
|
||||
|
||||
interface BroadcastFlags {
|
||||
volatile?: boolean;
|
||||
compress?: boolean;
|
||||
}
|
||||
|
||||
export interface PostgresEmitterOptions {
|
||||
/**
|
||||
* The prefix of the notification channel
|
||||
* @default "socket.io"
|
||||
*/
|
||||
channelPrefix: string;
|
||||
/**
|
||||
* The name of the table for payloads over the 8000 bytes limit or containing binary data
|
||||
*/
|
||||
tableName: string;
|
||||
/**
|
||||
* The threshold for the payload size in bytes (see https://www.postgresql.org/docs/current/sql-notify.html)
|
||||
* @default 8000
|
||||
*/
|
||||
payloadThreshold: number;
|
||||
}
|
||||
|
||||
export class Emitter<
|
||||
EmitEvents extends EventsMap = DefaultEventsMap,
|
||||
ServerSideEvents extends EventsMap = DefaultEventsMap,
|
||||
> {
|
||||
public readonly channel: string;
|
||||
public readonly tableName: string;
|
||||
public payloadThreshold: number;
|
||||
|
||||
constructor(
|
||||
readonly pool: any,
|
||||
readonly nsp: string = "/",
|
||||
opts: Partial<PostgresEmitterOptions> = {},
|
||||
) {
|
||||
const channelPrefix = opts.channelPrefix || "socket.io";
|
||||
this.channel = `${channelPrefix}#${nsp}`;
|
||||
this.tableName = opts.tableName || "socket_io_attachments";
|
||||
this.payloadThreshold = opts.payloadThreshold || 8000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new emitter for the given namespace.
|
||||
*
|
||||
* @param nsp - namespace
|
||||
* @public
|
||||
*/
|
||||
public of(nsp: string): Emitter {
|
||||
return new Emitter(this.pool, (nsp[0] !== "/" ? "/" : "") + nsp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits to all clients.
|
||||
*
|
||||
* @return Always true
|
||||
* @public
|
||||
*/
|
||||
public emit<Ev extends EventNames<EmitEvents>>(
|
||||
ev: Ev,
|
||||
...args: EventParams<EmitEvents, Ev>
|
||||
): true {
|
||||
return new BroadcastOperator<EmitEvents, ServerSideEvents>(this).emit(
|
||||
ev,
|
||||
...args,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
*
|
||||
* @param room
|
||||
* @return BroadcastOperator
|
||||
* @public
|
||||
*/
|
||||
public to(
|
||||
room: string | string[],
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
return new BroadcastOperator(this).to(room);
|
||||
}
|
||||
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
*
|
||||
* @param room
|
||||
* @return BroadcastOperator
|
||||
* @public
|
||||
*/
|
||||
public in(
|
||||
room: string | string[],
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
return new BroadcastOperator(this).in(room);
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes a room when emitting.
|
||||
*
|
||||
* @param room
|
||||
* @return BroadcastOperator
|
||||
* @public
|
||||
*/
|
||||
public except(
|
||||
room: string | string[],
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
return new BroadcastOperator(this).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 new BroadcastOperator(this).volatile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the compress flag.
|
||||
*
|
||||
* @param compress - if `true`, compresses the sending data
|
||||
* @return BroadcastOperator
|
||||
* @public
|
||||
*/
|
||||
public compress(
|
||||
compress: boolean,
|
||||
): BroadcastOperator<EmitEvents, ServerSideEvents> {
|
||||
return new BroadcastOperator(this).compress(compress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances join the specified rooms
|
||||
*
|
||||
* @param rooms
|
||||
* @public
|
||||
*/
|
||||
public socketsJoin(rooms: string | string[]): void {
|
||||
return new BroadcastOperator(this).socketsJoin(rooms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances leave the specified rooms
|
||||
*
|
||||
* @param rooms
|
||||
* @public
|
||||
*/
|
||||
public socketsLeave(rooms: string | string[]): void {
|
||||
return new BroadcastOperator(this).socketsLeave(rooms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances disconnect
|
||||
*
|
||||
* @param close - whether to close the underlying connection
|
||||
* @public
|
||||
*/
|
||||
public disconnectSockets(close: boolean = false): void {
|
||||
return new BroadcastOperator(this).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 new BroadcastOperator<EmitEvents, ServerSideEvents>(
|
||||
this,
|
||||
).serverSideEmit(ev, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
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 emitter: Emitter,
|
||||
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.emitter,
|
||||
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.emitter,
|
||||
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.emitter,
|
||||
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.emitter,
|
||||
this.rooms,
|
||||
this.exceptRooms,
|
||||
flags,
|
||||
);
|
||||
}
|
||||
|
||||
private async publish(document: any) {
|
||||
document.uid = EMITTER_UID;
|
||||
|
||||
try {
|
||||
if (
|
||||
[
|
||||
EventType.BROADCAST,
|
||||
EventType.SERVER_SIDE_EMIT,
|
||||
EventType.SERVER_SIDE_EMIT_RESPONSE,
|
||||
].includes(document.type) &&
|
||||
hasBinary(document)
|
||||
) {
|
||||
return await this.publishWithAttachment(document);
|
||||
}
|
||||
|
||||
const payload = JSON.stringify(document);
|
||||
if (Buffer.byteLength(payload) > this.emitter.payloadThreshold) {
|
||||
return await this.publishWithAttachment(document);
|
||||
}
|
||||
|
||||
debug(
|
||||
"sending event of type %s to channel %s",
|
||||
document.type,
|
||||
this.emitter.channel,
|
||||
);
|
||||
await this.emitter.pool.query("SELECT pg_notify($1, $2)", [
|
||||
this.emitter.channel,
|
||||
payload,
|
||||
]);
|
||||
} catch (err) {
|
||||
// @ts-ignore
|
||||
this.emit("error", err);
|
||||
}
|
||||
}
|
||||
|
||||
private async publishWithAttachment(document: any) {
|
||||
const payload = encode(document);
|
||||
|
||||
debug(
|
||||
"sending event of type %s with attachment to channel %s",
|
||||
document.type,
|
||||
this.emitter.channel,
|
||||
);
|
||||
const result = await this.emitter.pool.query(
|
||||
`INSERT INTO ${this.emitter.tableName} (payload) VALUES ($1) RETURNING id;`,
|
||||
[payload],
|
||||
);
|
||||
const attachmentId = result.rows[0].id;
|
||||
const headerPayload = JSON.stringify({
|
||||
uid: document.uid,
|
||||
type: document.type,
|
||||
attachmentId,
|
||||
});
|
||||
await this.emitter.pool.query("SELECT pg_notify($1, $2)", [
|
||||
this.emitter.channel,
|
||||
headerPayload,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
nsp: this.emitter.nsp,
|
||||
};
|
||||
|
||||
const opts = {
|
||||
rooms: [...this.rooms],
|
||||
flags: this.flags,
|
||||
except: [...this.exceptRooms],
|
||||
};
|
||||
|
||||
this.publish({
|
||||
type: EventType.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: EventType.SOCKETS_JOIN,
|
||||
data: {
|
||||
opts: {
|
||||
rooms: [...this.rooms],
|
||||
except: [...this.exceptRooms],
|
||||
},
|
||||
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: EventType.SOCKETS_LEAVE,
|
||||
data: {
|
||||
opts: {
|
||||
rooms: [...this.rooms],
|
||||
except: [...this.exceptRooms],
|
||||
},
|
||||
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: EventType.DISCONNECT_SOCKETS,
|
||||
data: {
|
||||
opts: {
|
||||
rooms: [...this.rooms],
|
||||
except: [...this.exceptRooms],
|
||||
},
|
||||
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: EventType.SERVER_SIDE_EMIT,
|
||||
data: {
|
||||
packet: [ev, ...args],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
37
packages/socket.io-postgres-emitter/lib/typed-events.ts
Normal file
37
packages/socket.io-postgres-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;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user