mirror of
https://github.com/socketio/socket.io.git
synced 2026-04-30 03:00:39 -04:00
Compare commits
4 Commits
engine.io@
...
socket.io-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
522edcdbb8 | ||
|
|
3fff7cafa9 | ||
|
|
37aad11417 | ||
|
|
ba9cd6900d |
@@ -508,7 +508,7 @@ export abstract class ClusterAdapter extends Adapter {
|
||||
}, opts.flags!.timeout);
|
||||
}
|
||||
|
||||
return super.broadcastWithAck(packet, opts, clientCountCallback, ack);
|
||||
super.broadcastWithAck(packet, opts, clientCountCallback, ack);
|
||||
}
|
||||
|
||||
override async addSockets(opts: BroadcastOptions, rooms: Room[]) {
|
||||
|
||||
@@ -229,14 +229,6 @@ export class Adapter extends EventEmitter {
|
||||
});
|
||||
|
||||
clientCountCallback(clientCount);
|
||||
|
||||
return {
|
||||
cleanup: () => {
|
||||
this.apply(opts, (socket) => {
|
||||
socket.acks.delete(packet.id);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private _encode(packet: unknown, packetOpts: Record<string, unknown>) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
| 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 |
|
||||
@@ -34,6 +35,15 @@
|
||||
| [3.3.0](#330-2018-11-07) | November 2018 |
|
||||
|
||||
|
||||
## [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`.
|
||||
|
||||
@@ -135,6 +135,20 @@ interface DecoderReservedEvents {
|
||||
decoded: (packet: Packet) => void;
|
||||
}
|
||||
|
||||
type JSONReviver = (this: any, key: string, value: any) => any;
|
||||
|
||||
export interface DecoderOptions {
|
||||
/**
|
||||
* Custom reviver to pass down to JSON.parse()
|
||||
*/
|
||||
reviver?: JSONReviver;
|
||||
/**
|
||||
* Maximum number of binary attachments per packet
|
||||
* @default 10
|
||||
*/
|
||||
maxAttachments?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A socket.io Decoder instance
|
||||
*
|
||||
@@ -142,14 +156,20 @@ interface DecoderReservedEvents {
|
||||
*/
|
||||
export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
|
||||
private reconstructor: BinaryReconstructor;
|
||||
private opts: Required<DecoderOptions>;
|
||||
|
||||
/**
|
||||
* Decoder constructor
|
||||
*
|
||||
* @param {function} reviver - custom reviver to pass down to JSON.stringify
|
||||
*/
|
||||
constructor(private reviver?: (this: any, key: string, value: any) => any) {
|
||||
constructor(opts?: DecoderOptions | JSONReviver) {
|
||||
super();
|
||||
this.opts = Object.assign(
|
||||
{
|
||||
reviver: undefined,
|
||||
maxAttachments: 10,
|
||||
},
|
||||
typeof opts === "function" ? { reviver: opts } : opts,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +244,13 @@ export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
|
||||
if (buf != Number(buf) || str.charAt(i) !== "-") {
|
||||
throw new Error("Illegal attachments");
|
||||
}
|
||||
p.attachments = Number(buf);
|
||||
const n = Number(buf);
|
||||
if (!isInteger(n) || n < 0) {
|
||||
throw new Error("Illegal attachments");
|
||||
} else if (n > this.opts.maxAttachments) {
|
||||
throw new Error("too many attachments");
|
||||
}
|
||||
p.attachments = n;
|
||||
}
|
||||
|
||||
// look up namespace (if any)
|
||||
@@ -271,7 +297,7 @@ export class Decoder extends Emitter<{}, {}, DecoderReservedEvents> {
|
||||
|
||||
private tryParse(str) {
|
||||
try {
|
||||
return JSON.parse(str, this.reviver);
|
||||
return JSON.parse(str, this.opts.reviver);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io-parser",
|
||||
"version": "4.2.5",
|
||||
"version": "4.2.6",
|
||||
"description": "socket.io protocol parser",
|
||||
"homepage": "https://github.com/socketio/socket.io/tree/main/packages/socket.io-client#readme",
|
||||
"repository": {
|
||||
|
||||
@@ -107,6 +107,56 @@ describe("socket.io-parser", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("throws an error when receiving too many attachments", () => {
|
||||
const decoder = new Decoder({ maxAttachments: 2 });
|
||||
|
||||
expect(() => {
|
||||
decoder.add(
|
||||
'53-["hello",{"_placeholder":true,"num":0},{"_placeholder":true,"num":1},{"_placeholder":true,"num":2}]',
|
||||
);
|
||||
}).to.throwException(/^too many attachments$/);
|
||||
});
|
||||
|
||||
it("decodes with a custom reviver", () => {
|
||||
const decoder = new Decoder((key, value) => {
|
||||
if (key === "a") {
|
||||
return value.toUpperCase();
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
decoder.on("decoded", (packet) => {
|
||||
expect(packet.data).to.eql(["b", { a: "VAL" }]);
|
||||
resolve();
|
||||
});
|
||||
|
||||
decoder.add('2["b",{"a":"val"}]');
|
||||
});
|
||||
});
|
||||
|
||||
it("decodes with a custom reviver (options object)", () => {
|
||||
const decoder = new Decoder({
|
||||
reviver: (key, value) => {
|
||||
if (key === "a") {
|
||||
return value.toUpperCase();
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
decoder.on("decoded", (packet) => {
|
||||
expect(packet.data).to.eql(["b", { a: "VAL" }]);
|
||||
resolve();
|
||||
});
|
||||
|
||||
decoder.add('2["b",{"a":"val"}]');
|
||||
});
|
||||
});
|
||||
|
||||
it("throw an error upon parsing error", () => {
|
||||
const isInvalidPayload = (str) =>
|
||||
expect(() => new Decoder().add(str)).to.throwException(
|
||||
@@ -125,6 +175,16 @@ describe("socket.io-parser", () => {
|
||||
isInvalidPayload('2["connect"]');
|
||||
isInvalidPayload('2["disconnect","123"]');
|
||||
|
||||
const isInvalidAttachmentCount = (str) =>
|
||||
expect(() => new Decoder().add(str)).to.throwException(
|
||||
/^Illegal attachments$/,
|
||||
);
|
||||
|
||||
isInvalidAttachmentCount("5");
|
||||
isInvalidAttachmentCount("51");
|
||||
isInvalidAttachmentCount("5a-");
|
||||
isInvalidAttachmentCount("51.23-");
|
||||
|
||||
expect(() => new Decoder().add("999")).to.throwException(
|
||||
/^unknown packet type 9$/,
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
@@ -232,11 +235,20 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
|
||||
const ack = data.pop() as (...args: any[]) => void;
|
||||
let timedOut = false;
|
||||
let responses: any[] = [];
|
||||
let cleanupPendingAcks: (() => void) | undefined;
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
timedOut = true;
|
||||
cleanupPendingAcks?.();
|
||||
|
||||
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,
|
||||
@@ -248,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 &&
|
||||
@@ -261,7 +280,7 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
|
||||
}
|
||||
};
|
||||
|
||||
const result = this.adapter.broadcastWithAck(
|
||||
this.adapter.broadcastWithAck(
|
||||
packet,
|
||||
{
|
||||
rooms: this.rooms,
|
||||
@@ -281,10 +300,6 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
|
||||
},
|
||||
);
|
||||
|
||||
if (result && typeof result.cleanup === "function") {
|
||||
cleanupPendingAcks = result.cleanup;
|
||||
}
|
||||
|
||||
this.adapter.serverCount().then((serverCount) => {
|
||||
expectedServerCount = serverCount;
|
||||
checkCompleteness();
|
||||
|
||||
Reference in New Issue
Block a user