From 7ddad2c09dea0391b20378ef03b40040f0230d3e Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 16 Oct 2020 09:17:53 +0200 Subject: [PATCH] feat: add volatile events A volatile packet will be dropped if: - the socket is not connected - the low-level transport is not ready (for example, a HTTP POST request is already pending) Syntax: ```js socket.volatile.emit("volatile event", "might or might not be sent"); ``` --- lib/socket.ts | 32 +++++++++++++++++++++++++++++--- test/socket.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/lib/socket.ts b/lib/socket.ts index c79b86152..9ad254f00 100644 --- a/lib/socket.ts +++ b/lib/socket.ts @@ -28,6 +28,11 @@ const RESERVED_EVENTS = { removeListener: 1, }; +interface Flags { + compress?: boolean; + volatile?: boolean; +} + export class Socket extends Emitter { public readonly io: Manager; @@ -42,7 +47,7 @@ export class Socket extends Emitter { private acks: object = {}; private receiveBuffer: Array = []; private sendBuffer: Array = []; - private flags: any = {}; + private flags: Flags = {}; private subs: Array; /** @@ -136,7 +141,7 @@ export class Socket extends Emitter { }; packet.options = {}; - packet.options.compress = !this.flags || false !== this.flags.compress; + packet.options.compress = this.flags.compress !== false; // event ack callback if ("function" === typeof args[args.length - 1]) { @@ -145,7 +150,16 @@ export class Socket extends Emitter { packet.id = this.ids++; } - if (this.connected) { + const isTransportWritable = + this.io.engine && + this.io.engine.transport && + this.io.engine.transport.writable; + + const discardPacket = + this.flags.volatile && (!isTransportWritable || !this.connected); + if (discardPacket) { + debug("discard packet as the transport is not currently writable"); + } else if (this.connected) { this.packet(packet); } else { this.sendBuffer.push(packet); @@ -406,4 +420,16 @@ export class Socket extends Emitter { this.flags.compress = compress; return this; } + + /** + * Sets a modifier for a subsequent event emission that the event message will be dropped when this socket is not + * ready to send messages. + * + * @returns {Socket} self + * @public + */ + public get volatile(): Socket { + this.flags.volatile = true; + return this; + } } diff --git a/test/socket.ts b/test/socket.ts index 09ce1b006..d03c9d8b1 100644 --- a/test/socket.ts +++ b/test/socket.ts @@ -190,4 +190,48 @@ describe("socket", function () { /"disconnecting" is a reserved event name/ ); }); + + describe("volatile packets", () => { + it("should discard a volatile packet when the socket is not connected", (done) => { + const socket = io({ forceNew: true, autoConnect: false }); + + socket.volatile.emit("getId", () => { + done(new Error("should not happen")); + }); + + socket.emit("getId", () => { + socket.disconnect(); + done(); + }); + + socket.connect(); + }); + + it("should discard a volatile packet when the pipe is not ready", (done) => { + const socket = io({ forceNew: true }); + + socket.on("connect", () => { + socket.emit("getId", () => { + socket.disconnect(); + done(); + }); + + socket.volatile.emit("getId", () => { + done(new Error("should not happen")); + }); + }); + }); + + it("should send a volatile packet when the socket is connected and the pipe is ready", (done) => { + const socket = io({ forceNew: true }); + + const interval = setInterval(() => { + socket.volatile.emit("getId", () => { + clearInterval(interval); + socket.disconnect(); + done(); + }); + }, 200); + }); + }); });