diff --git a/.github/workflows/ci-browser.yml b/.github/workflows/ci-browser.yml index 7cbbbebc75..f7e495b84a 100644 --- a/.github/workflows/ci-browser.yml +++ b/.github/workflows/ci-browser.yml @@ -14,7 +14,7 @@ permissions: jobs: test-browser: runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 20 steps: - name: Checkout repository diff --git a/packages/socket.io-client/lib/manager.ts b/packages/socket.io-client/lib/manager.ts index 8330af13ff..35bae86ae6 100644 --- a/packages/socket.io-client/lib/manager.ts +++ b/packages/socket.io-client/lib/manager.ts @@ -531,7 +531,6 @@ export class Manager< this.skipReconnect = true; this._reconnecting = false; this.onclose("forced close"); - if (this.engine) this.engine.close(); } /** @@ -544,7 +543,11 @@ export class Manager< } /** - * Called upon engine close. + * Called when: + * + * - the low-level engine is closed + * - the parser encountered a badly formatted packet + * - all sockets are disconnected * * @private */ @@ -552,6 +555,7 @@ export class Manager< debug("closed due to %s", reason); this.cleanup(); + this.engine?.close(); this.backoff.reset(); this._readyState = "closed"; this.emitReserved("close", reason, description); diff --git a/packages/socket.io-client/test/connection.ts b/packages/socket.io-client/test/connection.ts index b8c90b794d..7a439089d1 100644 --- a/packages/socket.io-client/test/connection.ts +++ b/packages/socket.io-client/test/connection.ts @@ -4,6 +4,7 @@ import hasCORS from "has-cors"; import { install } from "@sinonjs/fake-timers"; import textBlobBuilder from "text-blob-builder"; import { BASE_URL, wrap } from "./support/util"; +import { nextTick } from "engine.io-client"; describe("connection", () => { it("should connect to localhost", () => { @@ -894,4 +895,30 @@ describe("connection", () => { }); }); }); + + it("should close the engine upon decoding exception", () => { + return wrap((done) => { + const manager = new Manager(BASE_URL, { + autoConnect: true, + reconnectionDelay: 50, + }); + + let engine = manager.engine; + + manager.on("open", () => { + nextTick(() => { + // @ts-expect-error emit() is private + manager.engine.emit("data", "bad"); + }); + }); + + manager.on("reconnect", () => { + expect(manager.engine === engine).to.be(false); + expect(engine.readyState).to.eql("closed"); + + manager._close(); + done(); + }); + }); + }); });