Compare commits

..

18 Commits

Author SHA1 Message Date
Damien Arrachequesne
522edcdbb8 chore(release): socket.io-parser@4.2.6
Diff: https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.5...socket.io-parser@4.2.6
2026-03-17 10:53:22 +01:00
Damien Arrachequesne
3fff7cafa9 fix(parser): add a limit to the number of binary attachments
When a packet contains binary elements, the built-in parser does not modify them and simply sends them in their own WebSocket frame.

Example: `socket.emit("some event", Buffer.of(1,2,3))`

is encoded and transferred as:

- 1st frame: 51-["some event",{"_placeholder":true,"num":0}]
- 2nd frame: <buffer 01 02 03>

where:

- `5` is the type of the packet (binary message)
- `1` is the number of binary attachments
- `-` is the separator
- `["some event",{"_placeholder":true,"num":0}]` is the payload (including the placeholder)

On the receiving end, the parser reads the number of attachments and buffers them until they are all received.

Before this change, the built-in parser accepted any number of binary attachments, which could be exploited to make the server run out of memory.

The number of attachments is now limited to 10, which should be sufficient for most use cases.

The limit can be increased with a custom `parser`:

```js
import { Encoder, Decoder } from "socket.io-parser";

const io = new Server({
  parser: {
    Encoder,
    Decoder: class extends Decoder {
      constructor() {
        super({
          maxAttachments: 20
        });
      }
    }
  }
});
```
2026-03-17 10:27:15 +01:00
Damien Arrachequesne
37aad11417 fix: cleanup pending acks on timeout to prevent memory leak
Related: https://github.com/socketio/socket.io/issues/4984
2026-03-11 18:26:52 +01:00
Damien Arrachequesne
ba9cd6900d revert: fix: cleanup pending acks on timeout to prevent memory leak
This reverts commit da04267ffc.

The reverted fix was incorrect because the rooms might have changed between the emit() and the timeout.
2026-03-11 18:26:30 +01:00
Damien Arrachequesne
84c2fb7821 chore(release): engine.io@6.6.6
Diff: https://github.com/socketio/socket.io/compare/engine.io@6.6.5...engine.io@6.6.6
2026-03-10 10:52:10 +01:00
Not-Sarthak
07cbe1510d fix(eio): add @types/ws as dependency (#5458)
Since engine.io@6.6.5, the generated .d.ts files import types from "ws"
(WebSocket, PerMessageDeflateOptions), but @types/ws was not declared as
a dependency. This causes TypeScript compilation errors for consumers
who do not have @types/ws installed.

This follows the existing pattern where @types/cors and @types/node are
already listed as dependencies.

Related: https://github.com/socketio/socket.io/issues/5437
2026-03-04 09:54:58 +01:00
Erdinç Cürebal
44ed73f539 fix(eio): emit initial_headers and headers events in uServer (#5460)
The uServer (uWebSockets.js) implementation did not emit
"initial_headers" and "headers" events during WebSocket upgrades,
unlike the regular Server which does this via the ws "headers" event.

Related: https://github.com/socketio/socket.io/issues/5300
2026-03-04 09:07:02 +01:00
seungeonchoi
da04267ffc fix: cleanup pending acks on timeout to prevent memory leak (#5442)
When using `emitWithAck` with a timeout, if clients didn't respond
and the timeout triggers, the ack callbacks remained in `socket.acks`
Map indefinitely, causing a memory leak.

Related: https://github.com/socketio/socket.io/issues/4984
2026-03-02 16:32:13 +01:00
Damien Arrachequesne
74599a6b9e fix(types): properly import http module
This commit fixes `Module '"http"' has no default export.` errors (ts-node + esm).
2026-01-23 14:55:39 +01:00
Pádraic Slattery
d48718cb67 ci: use actions/checkout@v6 and actions/setup-node@v6 (#5449)
Release notes:

- https://github.com/actions/checkout/blob/main/CHANGELOG.md
- https://github.com/actions/setup-node/releases/tag/v6.0.0
2026-01-23 09:46:45 +01:00
Damien Arrachequesne
9978574e4f chore(release): socket.io@4.8.3
Diff: https://github.com/socketio/socket.io/compare/socket.io@4.8.2...socket.io@4.8.3
2025-12-23 17:39:51 +01:00
Damien Arrachequesne
e9e5bed4f2 chore(release): socket.io-client@4.8.3
Diff: https://github.com/socketio/socket.io/compare/socket.io-client@4.8.2...socket.io-client@4.8.3
2025-12-23 17:36:53 +01:00
Damien Arrachequesne
9581f9bcfd fix(sio): do not throw when calling io.close() on a stopped server
Following [1], calling both `io.close()` and `httpServer.close()` would throw an ERR_SERVER_NOT_RUNNING exception, which was not the case before.

Related: https://github.com/socketio/socket.io/issues/5431

[1]: bb0b480d2a
2025-12-23 17:30:15 +01:00
Damien Arrachequesne
579d43f33f refactor: remove unused files
[skip ci]
2025-12-23 13:34:44 +01:00
Damien Arrachequesne
ee9aac3134 chore(release): socket.io-parser@4.2.5
Diff: https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.4...socket.io-parser@4.2.5
2025-12-23 12:26:38 +01:00
Damien Arrachequesne
968277cef8 chore(release): socket.io-adapter@2.5.6
Diff: https://github.com/socketio/socket.io/compare/socket.io-adapter@2.5.5...socket.io-adapter@2.5.6
2025-12-23 12:18:53 +01:00
Damien Arrachequesne
2bf16bd214 chore(release): engine.io-client@6.6.4
Diff: https://github.com/socketio/socket.io/compare/engine.io-client@6.6.3...engine.io-client@6.6.4
2025-12-23 12:03:43 +01:00
Damien Arrachequesne
ad616070b8 docs(eio): fix link in the release notes
[skip ci]
2025-12-22 17:53:09 +01:00
41 changed files with 499 additions and 227 deletions

View File

@@ -31,10 +31,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Use Node.js 20
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 20

View File

@@ -20,10 +20,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Use Node.js 20
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 20

View File

@@ -17,10 +17,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Use Node.js 24
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 24
registry-url: 'https://registry.npmjs.org'

14
package-lock.json generated
View File

@@ -3499,7 +3499,6 @@
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
@@ -16038,11 +16037,12 @@
}
},
"packages/engine.io": {
"version": "6.6.4",
"version": "6.6.5",
"license": "MIT",
"dependencies": {
"@types/cors": "^2.8.12",
"@types/node": ">=10.0.0",
"@types/ws": "^8.5.12",
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.7.2",
@@ -16056,7 +16056,7 @@
}
},
"packages/engine.io-client": {
"version": "6.6.3",
"version": "6.6.4",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
@@ -16082,7 +16082,7 @@
}
},
"packages/socket.io": {
"version": "4.8.1",
"version": "4.8.3",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.4",
@@ -16098,7 +16098,7 @@
}
},
"packages/socket.io-adapter": {
"version": "2.5.5",
"version": "2.5.6",
"license": "MIT",
"dependencies": {
"debug": "~4.4.1",
@@ -16106,7 +16106,7 @@
}
},
"packages/socket.io-client": {
"version": "4.8.1",
"version": "4.8.3",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
@@ -16164,7 +16164,7 @@
"license": "MIT"
},
"packages/socket.io-parser": {
"version": "4.2.4",
"version": "4.2.5",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",

View File

@@ -1,7 +1,8 @@
# History
# Changelog
| Version | Release date | Bundle size (UMD min+gzip) |
|-------------------------------------------------------------------------------------------------------------|----------------|----------------------------|
| [6.6.4](#664-2025-12-23) | December 2025 | `8.7 KB` |
| [6.6.3](#663-2025-01-23) | January 2025 | `8.7 KB` |
| [6.6.2](#662-2024-10-23) | October 2024 | `8.7 KB` |
| [6.6.1](#661-2024-09-21) | September 2024 | `8.7 KB` |
@@ -39,7 +40,24 @@
| [4.1.1](#411-2021-02-02) | February 2021 | `9.1 KB` |
| [4.1.0](#410-2021-01-14) | January 2021 | `9.1 KB` |
# Release notes
## [6.6.4](https://github.com/socketio/socket.io/compare/engine.io-client@6.6.3...engine.io-client@6.6.4) (2025-12-23)
This release contains a bump of:
- `ws` from `~8.17.1` to `~8.18.3`
- `debug` from `~4.3.1` to `~4.4.1`
### Bug Fixes
* properly handle port option ([#5241](https://github.com/socketio/socket.io/issues/5241)) ([1da9cdd](https://github.com/socketio/socket.io/commit/1da9cddeab0bf5ce41890d156d73af8194cef656))
### Dependencies
- [`ws@~8.18.3`](https://github.com/websockets/ws/releases/tag/8.18.3) ([diff](https://github.com/websockets/ws/compare/8.17.1...8.18.3))
## [6.6.3](https://github.com/socketio/socket.io/compare/engine.io-client@6.6.2...engine.io-client@6.6.3) (2025-01-23)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
/*!
* Engine.IO v6.6.3
* Engine.IO v6.6.4
* (c) 2014-2025 Guillermo Rauch
* Released under the MIT License.
*/
@@ -35,6 +35,54 @@
writable: !1
}), e;
}
function _createForOfIteratorHelper(r, e) {
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (!t) {
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {
t && (r = t);
var n = 0,
F = function () {};
return {
s: F,
n: function () {
return n >= r.length ? {
done: !0
} : {
done: !1,
value: r[n++]
};
},
e: function (r) {
throw r;
},
f: F
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var o,
a = !0,
u = !1;
return {
s: function () {
t = t.call(r);
},
n: function () {
var r = t.next();
return a = r.done, r;
},
e: function (r) {
u = !0, o = r;
},
f: function () {
try {
a || null == t.return || t.return();
} finally {
if (u) throw o;
}
}
};
}
function _extends() {
return _extends = Object.assign ? Object.assign.bind() : function (n) {
for (var e = 1; e < arguments.length; e++) {
@@ -1049,21 +1097,65 @@
createDebug.namespaces = namespaces;
createDebug.names = [];
createDebug.skips = [];
var i;
var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
var len = split.length;
for (i = 0; i < len; i++) {
if (!split[i]) {
// ignore empty strings
continue;
var split = (typeof namespaces === 'string' ? namespaces : '').trim().replace(/\s+/g, ',').split(',').filter(Boolean);
var _iterator = _createForOfIteratorHelper(split),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var ns = _step.value;
if (ns[0] === '-') {
createDebug.skips.push(ns.slice(1));
} else {
createDebug.names.push(ns);
}
}
namespaces = split[i].replace(/\*/g, '.*?');
if (namespaces[0] === '-') {
createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$'));
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
}
/**
* Checks if the given string matches a namespace template, honoring
* asterisks as wildcards.
*
* @param {String} search
* @param {String} template
* @return {Boolean}
*/
function matchesTemplate(search, template) {
var searchIndex = 0;
var templateIndex = 0;
var starIndex = -1;
var matchIndex = 0;
while (searchIndex < search.length) {
if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) {
// Match character or proceed with wildcard
if (template[templateIndex] === '*') {
starIndex = templateIndex;
matchIndex = searchIndex;
templateIndex++; // Skip the '*'
} else {
searchIndex++;
templateIndex++;
}
} else if (starIndex !== -1) {
// eslint-disable-line no-negated-condition
// Backtrack to the last '*' and try to match more characters
templateIndex = starIndex + 1;
matchIndex++;
searchIndex = matchIndex;
} else {
createDebug.names.push(new RegExp('^' + namespaces + '$'));
return false; // No match
}
}
// Handle trailing '*' in template
while (templateIndex < template.length && template[templateIndex] === '*') {
templateIndex++;
}
return templateIndex === template.length;
}
/**
@@ -1073,7 +1165,7 @@
* @api public
*/
function disable() {
var namespaces = [].concat(_toConsumableArray(createDebug.names.map(toNamespace)), _toConsumableArray(createDebug.skips.map(toNamespace).map(function (namespace) {
var namespaces = [].concat(_toConsumableArray(createDebug.names), _toConsumableArray(createDebug.skips.map(function (namespace) {
return '-' + namespace;
}))).join(',');
createDebug.enable('');
@@ -1088,35 +1180,37 @@
* @api public
*/
function enabled(name) {
if (name[name.length - 1] === '*') {
return true;
}
var i;
var len;
for (i = 0, len = createDebug.skips.length; i < len; i++) {
if (createDebug.skips[i].test(name)) {
return false;
var _iterator2 = _createForOfIteratorHelper(createDebug.skips),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var skip = _step2.value;
if (matchesTemplate(name, skip)) {
return false;
}
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
for (i = 0, len = createDebug.names.length; i < len; i++) {
if (createDebug.names[i].test(name)) {
return true;
var _iterator3 = _createForOfIteratorHelper(createDebug.names),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var ns = _step3.value;
if (matchesTemplate(name, ns)) {
return true;
}
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
return false;
}
/**
* Convert regexp to namespace
*
* @param {RegExp} regxep
* @return {String} namespace
* @api private
*/
function toNamespace(regexp) {
return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, '*');
}
/**
* Coerce `val`.
*
@@ -1192,15 +1286,17 @@
if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
return false;
}
var m;
// Is webkit? http://stackoverflow.com/a/16459606/376773
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
// eslint-disable-next-line no-return-assign
return typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance ||
// Is firebug? http://stackoverflow.com/a/398120/376773
typeof window !== 'undefined' && window.console && (window.console.firebug || window.console.exception && window.console.table) ||
// Is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 ||
typeof navigator !== 'undefined' && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 ||
// Double check webkit in userAgent just in case we are in a worker
typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
}
@@ -1276,7 +1372,7 @@
function load() {
var r;
try {
r = exports.storage.getItem('debug');
r = exports.storage.getItem('debug') || exports.storage.getItem('DEBUG');
} catch (error) {
// Swallow
// XXX (@Qix-) should we be logging these?
@@ -1457,7 +1553,7 @@
return hostname.indexOf(":") === -1 ? hostname : "[" + hostname + "]";
};
_proto._port = function _port() {
if (this.opts.port && (this.opts.secure && Number(this.opts.port !== 443) || !this.opts.secure && Number(this.opts.port) !== 80)) {
if (this.opts.port && (this.opts.secure && Number(this.opts.port) !== 443 || !this.opts.secure && Number(this.opts.port) !== 80)) {
return ":" + this.opts.port;
} else {
return "";

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
"name": "engine.io-client",
"description": "Client for the realtime Engine",
"license": "MIT",
"version": "6.6.3",
"version": "6.6.4",
"main": "./build/cjs/index.js",
"module": "./build/esm/index.js",
"exports": {

View File

@@ -1,12 +0,0 @@
const parser = require('.');
parser.encodePayload([
{
type: 'message',
data: '€',
},
{
type: 'message',
data: Buffer.from([1, 2, 3, 4]),
},
], true, console.log);

View File

@@ -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;

View File

@@ -1,56 +1,71 @@
# Changelog
| Version | Release date |
|------------------------------------------------------------------------------------------------------|----------------|
| [6.6.5](#665-2025-12-22) | December 2025 |
| [6.6.4](#664-2025-01-28) | January 2025 |
| [6.6.3](#663-2025-01-23) | January 2025 |
| [6.6.2](#662-2024-10-09) | October 2024 |
| [6.6.1](#661-2024-09-21) | September 2024 |
| [6.6.0](#660-2024-06-21) | June 2024 |
| [6.5.5](#655-2024-06-18) (from the [6.5.x](https://github.com/socketio/engine.io/tree/6.5.x) branch) | June 2024 |
| [3.6.2](#362-2024-06-18) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | June 2024 |
| [6.5.4](#654-2023-11-09) | November 2023 |
| [6.5.3](#653-2023-10-06) | October 2023 |
| [6.5.2](#652-2023-08-01) | August 2023 |
| [6.5.1](#651-2023-06-27) | June 2023 |
| [6.5.0](#650-2023-06-16) | June 2023 |
| [6.4.2](#642-2023-05-02) | May 2023 |
| [6.4.1](#641-2023-02-20) | February 2023 |
| [6.4.0](#640-2023-02-06) | February 2023 |
| [6.3.1](#631-2023-01-12) | January 2023 |
| [6.3.0](#630-2023-01-10) | January 2023 |
| [3.6.1](#361-2022-11-20) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | November 2022 |
| [6.2.1](#621-2022-11-20) | November 2022 |
| [3.6.0](#360-2022-06-06) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | June 2022 |
| [6.2.0](#620-2022-04-17) | April 2022 |
| [6.1.3](#613-2022-02-23) | February 2022 |
| [6.1.2](#612-2022-01-18) | January 2022 |
| [6.1.1](#611-2022-01-11) | January 2021 |
| [6.1.0](#610-2021-11-08) | November 2022 |
| [6.0.1](#601-2021-11-06) | November 2021 |
| [**6.0.0**](#600-2021-10-08) | October 2021 |
| [5.2.0](#520-2021-08-29) | August 2021 |
| [5.1.1](#511-2021-05-16) | May 2021 |
| [5.1.0](#510-2021-05-04) | May 2021 |
| [**5.0.0**](#500-2021-03-10) | March 2021 |
| [4.1.1](#411-2021-02-02) | February 2021 |
| [4.1.0](#410-2021-01-14) | January 2021 |
| [4.0.6](#406-2021-01-04) | January 2021 |
| [3.5.0](#350-2020-12-30) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | December 2020 |
| [4.0.5](#405-2020-12-07) | December 2020 |
| [4.0.4](#404-2020-11-17) | November 2020 |
| [4.0.3](#403-2020-11-17) | November 2020 |
| [4.0.2](#402-2020-11-09) | November 2020 |
| [4.0.1](#401-2020-10-21) | October 2020 |
| [**4.0.0**](#400-2020-09-10) | September 2020 |
| [3.4.2](#342-2020-06-04) | June 2020 |
| [3.4.1](#341-2020-04-17) | April 2020 |
| Version | Release date | `ws` version |
|------------------------------------------------------------------------------------------------------|----------------|--------------|
| [6.6.6](#666-2026-03-10) | March 2026 | `"` |
| [6.6.5](#665-2025-12-22) | December 2025 | `~8.18.3` |
| [6.6.4](#664-2025-01-28) | January 2025 | `"` |
| [6.6.3](#663-2025-01-23) | January 2025 | `"` |
| [6.6.2](#662-2024-10-09) | October 2024 | `"` |
| [6.6.1](#661-2024-09-21) | September 2024 | `"` |
| [6.6.0](#660-2024-06-21) | June 2024 | `"` |
| [6.5.5](#655-2024-06-18) (from the [6.5.x](https://github.com/socketio/engine.io/tree/6.5.x) branch) | June 2024 | `~8.17.1` |
| [3.6.2](#362-2024-06-18) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | June 2024 | `~7.5.10` |
| [6.5.4](#654-2023-11-09) | November 2023 | `"` |
| [6.5.3](#653-2023-10-06) | October 2023 | `"` |
| [6.5.2](#652-2023-08-01) | August 2023 | `"` |
| [6.5.1](#651-2023-06-27) | June 2023 | `"` |
| [6.5.0](#650-2023-06-16) | June 2023 | `"` |
| [6.4.2](#642-2023-05-02) | May 2023 | `"` |
| [6.4.1](#641-2023-02-20) | February 2023 | `"` |
| [6.4.0](#640-2023-02-06) | February 2023 | `"` |
| [6.3.1](#631-2023-01-12) | January 2023 | `"` |
| [6.3.0](#630-2023-01-10) | January 2023 | `~8.11.0` |
| [3.6.1](#361-2022-11-20) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | November 2022 | `"` |
| [6.2.1](#621-2022-11-20) | November 2022 | `"` |
| [3.6.0](#360-2022-06-06) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | June 2022 | `"` |
| [6.2.0](#620-2022-04-17) | April 2022 | `"` |
| [6.1.3](#613-2022-02-23) | February 2022 | `"` |
| [6.1.2](#612-2022-01-18) | January 2022 | `"` |
| [6.1.1](#611-2022-01-11) | January 2021 | `"` |
| [6.1.0](#610-2021-11-08) | November 2022 | `"` |
| [6.0.1](#601-2021-11-06) | November 2021 | `"` |
| [**6.0.0**](#600-2021-10-08) | October 2021 | `~8.2.3` |
| [5.2.0](#520-2021-08-29) | August 2021 | `"` |
| [5.1.1](#511-2021-05-16) | May 2021 | `"` |
| [5.1.0](#510-2021-05-04) | May 2021 | `"` |
| [**5.0.0**](#500-2021-03-10) | March 2021 | `"` |
| [4.1.1](#411-2021-02-02) | February 2021 | `"` |
| [4.1.0](#410-2021-01-14) | January 2021 | `"` |
| [4.0.6](#406-2021-01-04) | January 2021 | `"` |
| [3.5.0](#350-2020-12-30) (from the [3.x](https://github.com/socketio/engine.io/tree/3.x) branch) | December 2020 | `~7.4.2` |
| [4.0.5](#405-2020-12-07) | December 2020 | `"` |
| [4.0.4](#404-2020-11-17) | November 2020 | `"` |
| [4.0.3](#403-2020-11-17) | November 2020 | `"` |
| [4.0.2](#402-2020-11-09) | November 2020 | `"` |
| [4.0.1](#401-2020-10-21) | October 2020 | `"` |
| [**4.0.0**](#400-2020-09-10) | September 2020 | `"` |
| [3.4.2](#342-2020-06-04) | June 2020 | `"` |
| [3.4.1](#341-2020-04-17) | April 2020 | `^7.1.2` |
## [6.6.6](https://github.com/socketio/socket.io/compare/engine.io@6.6.5...engine.io@6.6.6) (2026-03-10)
### Bug Fixes
* add `@types/ws` as dependency ([#5458](https://github.com/socketio/socket/issues/5458)) ([07cbe15](https://github.com/socketio/socket/commit/07cbe1510ded7e5460cb82e026e2533e50e30eaf))
* **uws** emit initial_headers and headers events in uServer ([#5460](https://github.com/socketio/socket/issues/5460)) ([44ed73f](https://github.com/socketio/socket/commit/44ed73f53995d35ef0c8d10df6806d5687238282))
### Dependencies
- [`ws@~8.18.3`](https://github.com/websockets/ws/releases/tag/8.18.3) (no change)
## [6.6.5](https://github.com/socketio/socket.io/compare/engine.io@6.6.4...engine.io@6.6.5) (2025-12-22)
The `url.parse()` function is now deprecated and has been replaced by `new URL()` (see [e08293b](https://github.com/socketio/socket.io/commit/e08293bc3735de5b824b347383e86e0b8ab9fbd5b).
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

View File

@@ -226,9 +226,23 @@ export class uServer extends BaseServer {
}
}
// emit headers events for WebSocket upgrades
const additionalHeaders = {};
const isInitialRequest = !id;
if (isInitialRequest) {
this.emit("initial_headers", additionalHeaders, req);
}
this.emit("headers", additionalHeaders, req);
// calling writeStatus() triggers the flushing of any header added in a middleware
req.res.writeStatus("101 Switching Protocols");
Object.keys(additionalHeaders).forEach((key) => {
req.res.writeHeader(key, additionalHeaders[key]);
});
res.upgrade(
{
transport,

View File

@@ -1,6 +1,6 @@
{
"name": "engine.io",
"version": "6.6.5",
"version": "6.6.6",
"description": "The realtime engine behind Socket.IO. Provides the foundation of a bidirectional connection between client and server",
"type": "commonjs",
"main": "./build/engine.io.js",
@@ -33,6 +33,7 @@
"dependencies": {
"@types/cors": "^2.8.12",
"@types/node": ">=10.0.0",
"@types/ws": "^8.5.12",
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.7.2",

View File

@@ -3598,10 +3598,7 @@ describe("server", () => {
});
it("should emit a 'initial_headers' event (websocket)", function (done) {
if (
process.env.EIO_WS_ENGINE === "eiows" ||
process.env.EIO_WS_ENGINE === "uws"
) {
if (process.env.EIO_WS_ENGINE === "eiows") {
return this.skip();
}
const partialDone = createPartialDone(done, 2);
@@ -3644,10 +3641,7 @@ describe("server", () => {
});
it("should emit several 'headers' events per connection", function (done) {
if (
process.env.EIO_WS_ENGINE === "eiows" ||
process.env.EIO_WS_ENGINE === "uws"
) {
if (process.env.EIO_WS_ENGINE === "eiows") {
return this.skip();
}
const partialDone = createPartialDone(done, 4);

View File

@@ -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)

View File

@@ -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": {

View File

@@ -2,7 +2,8 @@
| Version | Release date | Bundle size (UMD min+gzip) |
|-------------------------------------------------------------------------------------------------------------|----------------|----------------------------|
| [4.8.2](#482-2025-12-22) | December 2024 | `14.4 KB` |
| [4.8.3](#483-2025-12-23) | December 2025 | `14.4 KB` |
| [4.8.2](#482-2025-12-22) | December 2025 | `14.4 KB` |
| [4.8.1](#481-2024-10-25) | October 2024 | `14.4 KB` |
| [4.8.0](#480-2024-09-21) | September 2024 | `14.4 KB` |
| [4.7.5](#475-2024-03-14) | March 2024 | `14.6 KB` |
@@ -51,6 +52,18 @@
| [2.1.0](#210-2018-03-29) | March 2018 | `18.7 KB` |
## [4.8.3](https://github.com/socketio/socket.io/compare/socket.io-client@4.8.2...socket.io-client@4.8.3) (2025-12-23)
There were some minor bug fixes on the server side, which mandate a client bump.
### Dependencies
- [`engine.io-client@~6.6.1`](https://github.com/socketio/engine.io-client/releases/tag/6.5.2) (no change)
- [`ws@~8.18.3`](https://github.com/websockets/ws/releases/tag/8.18.3) ([diff](https://github.com/websockets/ws/compare/8.17.1...8.18.3))
## [4.8.2](https://github.com/socketio/socket.io/compare/socket.io-client@4.8.1...socket.io-client@4.8.2) (2025-12-22)

View File

@@ -1,5 +1,5 @@
/*!
* Socket.IO v4.8.2
* Socket.IO v4.8.3
* (c) 2014-2025 Guillermo Rauch
* Released under the MIT License.
*/

View File

@@ -1,5 +1,5 @@
/*!
* Socket.IO v4.8.2
* Socket.IO v4.8.3
* (c) 2014-2025 Guillermo Rauch
* Released under the MIT License.
*/

View File

@@ -1,5 +1,5 @@
/*!
* Socket.IO v4.8.2
* Socket.IO v4.8.3
* (c) 2014-2025 Guillermo Rauch
* Released under the MIT License.
*/

View File

@@ -1,5 +1,5 @@
/*!
* Socket.IO v4.8.2
* Socket.IO v4.8.3
* (c) 2014-2025 Guillermo Rauch
* Released under the MIT License.
*/

View File

@@ -1,6 +1,6 @@
{
"name": "socket.io-client",
"version": "4.8.2",
"version": "4.8.3",
"description": "Realtime application framework client",
"keywords": [
"realtime",

View File

@@ -1,5 +1,5 @@
{
"name": "socket.io-client",
"version": "4.8.2",
"version": "4.8.3",
"type": "module"
}

View File

@@ -1,7 +1,9 @@
# History
# Changelog
| Version | Release date |
|-------------------------------------------------------------------------------------------------------------|----------------|
| [4.2.6](#426-2026-03-17) | March 2026 |
| [4.2.5](#425-2025-12-23) | December 2025 |
| [3.3.4](#334-2024-07-22) (from the [3.3.x](https://github.com/socketio/socket.io-parser/tree/3.3.x) branch) | July 2024 |
| [4.2.4](#424-2023-05-31) | May 2023 |
| [3.4.3](#343-2023-05-22) (from the [3.4.x](https://github.com/socketio/socket.io-parser/tree/3.4.x) branch) | May 2023 |
@@ -33,7 +35,20 @@
| [3.3.0](#330-2018-11-07) | November 2018 |
# Release notes
## [4.2.6](https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.5...socket.io-parser@4.2.6) (2026-03-17)
### Bug Fixes
* **parser:** add a limit to the number of binary attachments ([3fff7ca](https://github.com/socketio/socket/commit/3fff7cafa98f1ba5840475b6917c651fe841a943))
## [4.2.5](https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.4...socket.io-parser@4.2.5) (2025-12-23)
This release contains a bump of `debug` from `~4.3.1` to `~4.4.1`.
## [3.3.4](https://github.com/Automattic/socket.io-parser/compare/3.3.3...3.3.4) (2024-07-22)

View File

@@ -135,6 +135,20 @@ interface DecoderReservedEvents {
decoded: (packet: Packet) => void;
}
type JSONReviver = (this: any, key: string, value: any) => any;
export interface DecoderOptions {
/**
* Custom reviver to pass down to JSON.parse()
*/
reviver?: JSONReviver;
/**
* Maximum number of binary attachments per packet
* @default 10
*/
maxAttachments?: number;
}
/**
* A socket.io Decoder instance
*
@@ -142,14 +156,20 @@ interface DecoderReservedEvents {
*/
export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
private reconstructor: BinaryReconstructor;
private opts: Required<DecoderOptions>;
/**
* Decoder constructor
*
* @param {function} reviver - custom reviver to pass down to JSON.stringify
*/
constructor(private reviver?: (this: any, key: string, value: any) => any) {
constructor(opts?: DecoderOptions | JSONReviver) {
super();
this.opts = Object.assign(
{
reviver: undefined,
maxAttachments: 10,
},
typeof opts === "function" ? { reviver: opts } : opts,
);
}
/**
@@ -224,7 +244,13 @@ export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
if (buf != Number(buf) || str.charAt(i) !== "-") {
throw new Error("Illegal attachments");
}
p.attachments = Number(buf);
const n = Number(buf);
if (!isInteger(n) || n < 0) {
throw new Error("Illegal attachments");
} else if (n > this.opts.maxAttachments) {
throw new Error("too many attachments");
}
p.attachments = n;
}
// look up namespace (if any)
@@ -271,7 +297,7 @@ export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
private tryParse(str) {
try {
return JSON.parse(str, this.reviver);
return JSON.parse(str, this.opts.reviver);
} catch (e) {
return false;
}

View File

@@ -1,6 +1,6 @@
{
"name": "socket.io-parser",
"version": "4.2.4",
"version": "4.2.6",
"description": "socket.io protocol parser",
"homepage": "https://github.com/socketio/socket.io/tree/main/packages/socket.io-client#readme",
"repository": {

View File

@@ -107,6 +107,56 @@ describe("socket.io-parser", () => {
}
});
it("throws an error when receiving too many attachments", () => {
const decoder = new Decoder({ maxAttachments: 2 });
expect(() => {
decoder.add(
'53-["hello",{"_placeholder":true,"num":0},{"_placeholder":true,"num":1},{"_placeholder":true,"num":2}]',
);
}).to.throwException(/^too many attachments$/);
});
it("decodes with a custom reviver", () => {
const decoder = new Decoder((key, value) => {
if (key === "a") {
return value.toUpperCase();
} else {
return value;
}
});
return new Promise((resolve) => {
decoder.on("decoded", (packet) => {
expect(packet.data).to.eql(["b", { a: "VAL" }]);
resolve();
});
decoder.add('2["b",{"a":"val"}]');
});
});
it("decodes with a custom reviver (options object)", () => {
const decoder = new Decoder({
reviver: (key, value) => {
if (key === "a") {
return value.toUpperCase();
} else {
return value;
}
},
});
return new Promise((resolve) => {
decoder.on("decoded", (packet) => {
expect(packet.data).to.eql(["b", { a: "VAL" }]);
resolve();
});
decoder.add('2["b",{"a":"val"}]');
});
});
it("throw an error upon parsing error", () => {
const isInvalidPayload = (str) =>
expect(() => new Decoder().add(str)).to.throwException(
@@ -125,6 +175,16 @@ describe("socket.io-parser", () => {
isInvalidPayload('2["connect"]');
isInvalidPayload('2["disconnect","123"]');
const isInvalidAttachmentCount = (str) =>
expect(() => new Decoder().add(str)).to.throwException(
/^Illegal attachments$/,
);
isInvalidAttachmentCount("5");
isInvalidAttachmentCount("51");
isInvalidAttachmentCount("5a-");
isInvalidAttachmentCount("51.23-");
expect(() => new Decoder().add("999")).to.throwException(
/^unknown packet type 9$/,
);

View File

@@ -2,6 +2,7 @@
| Version | Release date |
|--------------------------------------------------------------------------------------------------|----------------|
| [4.8.3](#483-2025-12-23) | December 2025 |
| [4.8.2](#482-2025-12-22) | December 2025 |
| [4.8.1](#481-2024-10-25) | October 2024 |
| [4.8.0](#480-2024-09-21) | September 2024 |
@@ -50,6 +51,21 @@
| [2.1.0](#210-2018-03-29) | March 2018 |
## [4.8.3](https://github.com/socketio/socket.io/compare/socket.io@4.8.2...socket.io@4.8.3) (2025-12-23)
### Bug Fixes
* do not throw when calling io.close() on a stopped server ([9581f9b](https://github.com/socketio/socket.io/commit/9581f9bcfd0c0fa8cb16eae1604c6a727af21efa))
### Dependencies
- [`engine.io@~6.6.0`](https://github.com/socketio/engine.io/releases/tag/6.6.0) (no change)
- [`ws@~8.18.3`](https://github.com/websockets/ws/releases/tag/8.18.3) (no change)
## [4.8.2](https://github.com/socketio/socket.io/compare/socket.io@4.8.1...socket.io@4.8.2) (2025-12-22)
The `url.parse()` function is now deprecated and has been replaced by `new URL()` (see [8af7019](https://github.com/socketio/socket.io/commit/8af70195bb8c5bc3efe9685997ab6373fb8b1ca9)).

View File

@@ -1,5 +1,5 @@
/*!
* Socket.IO v4.8.2
* Socket.IO v4.8.3
* (c) 2014-2025 Guillermo Rauch
* Released under the MIT License.
*/

View File

@@ -1,5 +1,5 @@
/*!
* Socket.IO v4.8.2
* Socket.IO v4.8.3
* (c) 2014-2025 Guillermo Rauch
* Released under the MIT License.
*/

View File

@@ -1,5 +1,5 @@
/*!
* Socket.IO v4.8.2
* Socket.IO v4.8.3
* (c) 2014-2025 Guillermo Rauch
* Released under the MIT License.
*/

View File

@@ -1,5 +1,5 @@
/*!
* Socket.IO v4.8.2
* Socket.IO v4.8.3
* (c) 2014-2025 Guillermo Rauch
* Released under the MIT License.
*/

View File

@@ -13,6 +13,9 @@ import type {
FirstNonErrorArg,
EventNamesWithError,
} from "./typed-events";
import debugModule from "debug";
const debug = debugModule("socket.io:broadcast-operator");
export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
implements TypedEventBroadcaster<EmitEvents>
@@ -235,6 +238,17 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
const timer = setTimeout(() => {
timedOut = true;
debug("operation has timed out");
// @ts-expect-error
const packetId = packet.id;
if (packetId !== undefined) {
this.adapter.nsp.sockets.forEach((socket) => {
socket.acks.delete(packetId);
});
}
ack.apply(this, [
new Error("operation has timed out"),
this.flags.expectSingleResponse ? null : responses,
@@ -246,6 +260,13 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
let expectedClientCount = 0;
const checkCompleteness = () => {
debug(
"responses: servers: %d / %d ; clients: %d / %d",
actualServerCount,
expectedServerCount,
responses.length,
expectedClientCount,
);
if (
!timedOut &&
expectedServerCount === actualServerCount &&

View File

@@ -1,4 +1,9 @@
import http from "http";
import { createServer } from "http"; // 'node:' prefix was added in Node.js 16
import type {
Server as HTTPServer,
IncomingMessage,
ServerResponse,
} from "http";
import type { Server as HTTPSServer } from "https";
import type { Http2SecureServer, Http2Server } from "http2";
import { createReadStream } from "fs";
@@ -58,7 +63,7 @@ type ParentNspNameMatchFn = (
type AdapterConstructor = typeof Adapter | ((nsp: Namespace) => Adapter);
type TServerInstance =
| http.Server
| HTTPServer
| HTTPSServer
| Http2SecureServer
| Http2Server;
@@ -276,8 +281,8 @@ export class Server<
*/
_connectTimeout: number;
private _corsMiddleware: (
req: http.IncomingMessage,
res: http.ServerResponse,
req: IncomingMessage,
res: ServerResponse,
next: () => void,
) => void;
@@ -301,7 +306,7 @@ export class Server<
if (
"object" === typeof srv &&
srv instanceof Object &&
!(srv as Partial<http.Server>).listen
!(srv as Partial<HTTPServer>).listen
) {
opts = srv as Partial<ServerOptions>;
srv = undefined;
@@ -493,7 +498,7 @@ export class Server<
if ("number" == typeof srv) {
debug("creating http server and binding to %d", srv);
const port = srv;
srv = http.createServer((req, res) => {
srv = createServer((_req, res) => {
res.writeHead(404);
res.end();
});
@@ -591,7 +596,7 @@ export class Server<
): void {
// initialize engine
debug("creating engine.io instance with opts %j", opts);
this.eio = attach(srv as http.Server, opts);
this.eio = attach(srv as HTTPServer, opts);
// attach static file serving
if (this._serveClient) this.attachServe(srv);
@@ -638,7 +643,7 @@ export class Server<
* @param res
* @private
*/
private serve(req: http.IncomingMessage, res: http.ServerResponse): void {
private serve(req: IncomingMessage, res: ServerResponse): void {
const filename = req.url!.replace(this._path, "").replace(/\?.*$/, "");
const isMap = dotMapRegex.test(filename);
const type = isMap ? "map" : "source";
@@ -678,8 +683,8 @@ export class Server<
*/
private static sendFile(
filename: string,
req: http.IncomingMessage,
res: http.ServerResponse,
req: IncomingMessage,
res: ServerResponse,
): void {
const readStream = createReadStream(
path.join(__dirname, "../client-dist/", filename),
@@ -831,14 +836,13 @@ export class Server<
restoreAdapter();
if (this.httpServer) {
await new Promise<void>((resolve, reject) => {
return new Promise<void>((resolve) => {
this.httpServer.close((err) => {
fn && fn(err);
if (err) {
reject(err);
} else {
resolve();
debug("server was not running");
}
resolve();
});
});
} else {

View File

@@ -1,6 +1,6 @@
{
"name": "socket.io",
"version": "4.8.2",
"version": "4.8.3",
"description": "node.js realtime framework server",
"keywords": [
"realtime",

View File

@@ -70,6 +70,27 @@ describe("close", () => {
});
});
it("should not throw when the underlying HTTP server is not running (callback)", (done) => {
const httpServer = createServer();
const io = new Server(httpServer);
io.close((err) => {
expect((err as Error & { code: string }).code).to.eql(
"ERR_SERVER_NOT_RUNNING",
);
done();
});
});
it("should not throw when the underlying HTTP server is not running (Promise)", (done) => {
const httpServer = createServer();
const io = new Server(httpServer);
io.close()
.then(() => done())
.catch((e) => done(e));
});
describe("graceful close", () => {
function fixture(filename) {
return (

View File

@@ -534,6 +534,11 @@ describe("messaging many", () => {
// @ts-ignore
expect(err.responses).to.contain(1, 2);
for (const [, serverSocket] of io.of("/").sockets) {
// @ts-ignore accessing private acks map to verify cleanup
expect(serverSocket.acks.size).to.be(0);
}
success(done, io, socket1, socket2, socket3);
}
});