From 2d5221e48012fb7781c0406d543a922d313075ea Mon Sep 17 00:00:00 2001 From: Javad Date: Sun, 5 Jun 2022 18:31:48 +0430 Subject: [PATCH] fix: interrupted WebSocket connection not closed by LiveQuery server (#8012) --- spec/ParseWebSocketServer.spec.js | 25 +++++++++++++++++++++++++ src/LiveQuery/ParseWebSocketServer.js | 8 +++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/spec/ParseWebSocketServer.spec.js b/spec/ParseWebSocketServer.spec.js index 6050ea0f2f..4eb73695d1 100644 --- a/spec/ParseWebSocketServer.spec.js +++ b/spec/ParseWebSocketServer.spec.js @@ -23,6 +23,7 @@ describe('ParseWebSocketServer', function () { ws.readyState = 0; ws.OPEN = 0; ws.ping = jasmine.createSpy('ping'); + ws.terminate = () => {}; parseWebSocketServer.onConnection(ws); @@ -75,6 +76,30 @@ describe('ParseWebSocketServer', function () { expect(wssError).toBe('Invalid Packet'); }); + it('closes interrupted connection', async () => { + const onConnectCallback = jasmine.createSpy('onConnectCallback'); + const http = require('http'); + const server = http.createServer(); + const parseWebSocketServer = new ParseWebSocketServer(server, onConnectCallback, { + websocketTimeout: 5, + }).server; + const ws = new EventEmitter(); + ws.readyState = 0; + ws.OPEN = 0; + ws.ping = jasmine.createSpy('ping'); + ws.terminate = jasmine.createSpy('terminate'); + + parseWebSocketServer.onConnection(ws); + + // Make sure callback is called + expect(onConnectCallback).toHaveBeenCalled(); + await new Promise(resolve => setTimeout(resolve, 10)); + expect(ws.ping).toHaveBeenCalled(); + await new Promise(resolve => setTimeout(resolve, 10)); + expect(ws.terminate).toHaveBeenCalled(); + server.close(); + }); + afterEach(function () { jasmine.restoreLibrary('ws', 'Server'); }); diff --git a/src/LiveQuery/ParseWebSocketServer.js b/src/LiveQuery/ParseWebSocketServer.js index a0d1e9891f..b8c66673ab 100644 --- a/src/LiveQuery/ParseWebSocketServer.js +++ b/src/LiveQuery/ParseWebSocketServer.js @@ -14,6 +14,10 @@ export class ParseWebSocketServer { logger.info('Parse LiveQuery Server started running'); }; wss.onConnection = ws => { + ws.waitingForPong = false; + ws.on('pong', () => { + this.waitingForPong = false; + }); ws.on('error', error => { logger.error(error.message); logger.error(inspect(ws, false)); @@ -21,10 +25,12 @@ export class ParseWebSocketServer { onConnect(new ParseWebSocket(ws)); // Send ping to client periodically const pingIntervalId = setInterval(() => { - if (ws.readyState == ws.OPEN) { + if (!ws.waitingForPong) { ws.ping(); + ws.waitingForPong = true; } else { clearInterval(pingIntervalId); + ws.terminate(); } }, config.websocketTimeout || 10 * 1000); };