From c75bb6ffa5d2591863a3c9241e98477b72c8923d Mon Sep 17 00:00:00 2001 From: tayou89 Date: Thu, 26 Mar 2026 13:41:26 +0900 Subject: [PATCH] Fix: reject midInProcess callback on close When close() is called, midQueue items are properly rejected with an error callback, but midInProcess is silently set to null. This causes the pending callback/promise to hang forever when a connection drops while a MID is being processed. Reject midInProcess with the close error (or default "service unavailable") before nulling, using the same process.nextTick pattern as midQueue items. Add tests for both error-forwarding and default-error cases. Handle unhandled promise rejections in existing sendMid tests. --- src/sessionControlClient.js | 10 +++- test/sessionControlClient.spec.js | 82 ++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/sessionControlClient.js b/src/sessionControlClient.js index a80fd9c..6b2d18d 100644 --- a/src/sessionControlClient.js +++ b/src/sessionControlClient.js @@ -429,7 +429,15 @@ class SessionControlClient extends EventEmitter { }); } - this.midInProcess = null; + if (this.midInProcess) { + let e = err || new Error("service unavailable"); + let mid = this.midInProcess; + this.midInProcess = null; + process.nextTick(() => mid.doCallback(e)); + } else { + this.midInProcess = null; + } + this.midQueue = []; this.autoRevision = {}; diff --git a/test/sessionControlClient.spec.js b/test/sessionControlClient.spec.js index 1eb3ee1..bfb3cd5 100644 --- a/test/sessionControlClient.spec.js +++ b/test/sessionControlClient.spec.js @@ -552,7 +552,9 @@ describe("Session Control Client", () => { done(); }); - sessionControlClient.sendMid(1212); + sessionControlClient.sendMid(1212).catch(() => { + // Expected: close() rejects pending midInProcess + }); }); sessionControlClient.connect(); @@ -934,6 +936,82 @@ describe("Session Control Client", () => { sessionControlClient.connect(); }); + it("Should reject midInProcess callback on close", (done) => { + + let step = 0; + let sessionControlClient; + + let stream = createStreamHelper((data) => { + switch (step) { + case 0: + stream.push(Buffer.from("00570002001000000000010001020103Airbag1 \u0000")); + step += 1; + break; + + case 1: + // Do not reply — simulate connection drop while MID is in process + sessionControlClient.close(new Error("connection lost")); + break; + } + }); + + sessionControlClient = new SessionControlClient({ + stream: stream + }); + + sessionControlClient.on("error", err => { + // expected + }); + + sessionControlClient.on("connect", () => { + sessionControlClient.request("readTimeUpload", null, (err, data) => { + expect(err).to.be.an("error"); + expect(err.message).to.be.equal("connection lost"); + done(); + }); + }); + + sessionControlClient.connect(); + }); + + it("Should reject midInProcess with default error when close is called without error", (done) => { + + let step = 0; + let sessionControlClient; + + let stream = createStreamHelper((data) => { + switch (step) { + case 0: + stream.push(Buffer.from("00570002001000000000010001020103Airbag1 \u0000")); + step += 1; + break; + + case 1: + // Do not reply — close without specific error + sessionControlClient.close(); + break; + } + }); + + sessionControlClient = new SessionControlClient({ + stream: stream + }); + + sessionControlClient.on("error", err => { + // expected + }); + + sessionControlClient.on("connect", () => { + sessionControlClient.request("readTimeUpload", null, (err, data) => { + expect(err).to.be.an("error"); + expect(err.message).to.be.equal("service unavailable"); + done(); + }); + }); + + sessionControlClient.connect(); + }); + it("Should event data using sendMID() [MID 0080] - Test 05/02/2019", (done) => { let step = 0; @@ -976,6 +1054,8 @@ describe("Session Control Client", () => { sessionControlClient.sendMid(80, { revision: 1 + }).catch(() => { + // Expected: close() rejects pending midInProcess }); });