From d291a4c9f6accfc86fcd96683a5d493a87e3644c Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Wed, 31 Mar 2021 10:16:40 +0200 Subject: [PATCH] fix: ignore packets when the transport is silently closed In some cases, a "Transport not open" error could be thrown when the transport was silently closed in the onbeforeunload event (added in [1]). To reproduce: ```js window.addEventListener("unload", () => { socket.write("..."); }); ``` [1]: https://github.com/socketio/engine.io-client/commit/ed48b5dc3407e5ded45072606b3bb0eafa49c01f Related: https://github.com/socketio/socket.io/issues/3838 --- lib/transport.js | 4 +++- test/connection.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/transport.js b/lib/transport.js index eb864dc6d..a8800771b 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -1,5 +1,6 @@ const parser = require("engine.io-parser"); const Emitter = require("component-emitter"); +const debug = require("debug")("engine.io-client:transport"); class Transport extends Emitter { /** @@ -70,7 +71,8 @@ class Transport extends Emitter { if ("open" === this.readyState) { this.write(packets); } else { - throw new Error("Transport not open"); + // this might happen if the transport was silently closed in the beforeunload event handler + debug("transport is not open, discarding packets"); } } diff --git a/test/connection.js b/test/connection.js index 02fd21f06..28ee94127 100644 --- a/test/connection.js +++ b/test/connection.js @@ -188,4 +188,34 @@ describe("connection", function() { }); }); } + + if (env.browser && typeof addEventListener === "function") { + it("should close the socket when receiving a beforeunload event", done => { + const socket = new Socket(); + + const createEvent = name => { + if (typeof Event === "function") { + return new Event(name); + } else { + // polyfill for IE + const event = document.createEvent("Event"); + event.initEvent(name, true, true); + return event; + } + }; + + socket.on("open", () => { + const handler = () => { + expect(socket.transport.readyState).to.eql("closed"); + expect(() => socket.write("ignored")).to.not.throwException(); + + removeEventListener("beforeunload", handler, false); + done(); + }; + + addEventListener("beforeunload", handler, false); + dispatchEvent(createEvent("beforeunload")); + }); + }); + } });