From 41bd3ca032fc8af0849a1ceef6114e2078c1393a Mon Sep 17 00:00:00 2001 From: Orin Eman Date: Sun, 27 Nov 2022 14:02:03 -0800 Subject: [PATCH] fix: issue #743 - close channel on Request 'aborted' event Ensure channel is closed if an API request is aborted. Improved afterResponse test to ensure CloseChannel is called. --- src/server/webserver.test.ts | 34 +++++++++++++++++++++++++++++++--- src/server/webserver.ts | 7 +++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/server/webserver.test.ts b/src/server/webserver.test.ts index fe402341c..367fd4c9e 100644 --- a/src/server/webserver.test.ts +++ b/src/server/webserver.test.ts @@ -210,7 +210,8 @@ describe('webserver tests', () => { ciraHandler: { channel: 2 } - } + }, + on: jest.fn() } const res: Express.Response = { on: jest.fn() @@ -231,14 +232,22 @@ describe('webserver tests', () => { CloseChannel: jest.fn() } } - } + }, + on: jest.fn(), + removeListener: jest.fn() } const res: Express.Response = { removeListener: jest.fn() } const afterResponseSpy = jest.spyOn(web, 'afterResponse') + const closeChannelSpy = jest.spyOn((req as any).deviceAction.ciraHandler.channel, 'CloseChannel') + const reqRemoveListenerSpy = jest.spyOn(req as any, 'removeListener') + const resRemoveListenerSpy = jest.spyOn(res as any, 'removeListener') web.afterResponse(req as any, res as any) expect(afterResponseSpy).toHaveBeenCalledTimes(1) + expect(closeChannelSpy).toHaveBeenCalledTimes(1) + expect(reqRemoveListenerSpy).toHaveBeenCalledTimes(1) + expect(resRemoveListenerSpy).toHaveBeenCalledTimes(2) }) it('test afterResponse with undefined channel', () => { const req: Express.Request = { @@ -246,7 +255,9 @@ describe('webserver tests', () => { ciraHandler: { channel: null } - } + }, + on: jest.fn(), + removeListener: jest.fn() } const res: Express.Response = { removeListener: jest.fn() @@ -255,6 +266,23 @@ describe('webserver tests', () => { web.afterResponse(req as any, res as any) expect(afterResponseSpy).toHaveBeenCalledTimes(2) }) + it('test onAborted calls afterResponse', () => { + const req: Express.Request = { + deviceAction: { + ciraHandler: { + channel: null + } + }, + on: jest.fn(), + removeListener: jest.fn() + } + const res: Express.Response = { + removeListener: jest.fn() + } + const afterResponseSpy = jest.spyOn(web, 'afterResponse') + web.onAborted(req as any, res as any) + expect(afterResponseSpy).toHaveBeenCalledTimes(3) + }) }) describe('relayconnection', () => { diff --git a/src/server/webserver.ts b/src/server/webserver.ts index ae797eb40..4456ab876 100644 --- a/src/server/webserver.ts +++ b/src/server/webserver.ts @@ -81,9 +81,15 @@ export class WebServer { appUseCall (req: Request, res: Response, next: NextFunction): void { res.on('finish', this.afterResponse.bind(this, req, res)) res.on('close', this.afterResponse.bind(this, req, res)) + req.on('aborted', this.onAborted.bind(this, req, res)) next() } + onAborted (req: Request, res: Response): void { + logger.debug(`Request aborted: ${req.url ?? 'undefined'}`) + this.afterResponse(req, res) + } + afterResponse (req: Request, res: Response): void { if (req.deviceAction?.ciraHandler?.channel) { logger.debug(messages.EOR_CLOSING_CHANNEL) @@ -91,6 +97,7 @@ export class WebServer { } res.removeListener('finish', this.afterResponse) res.removeListener('close', this.afterResponse) + req.removeListener('aborted', this.onAborted) // actions after response }