From 0dbca07941b43db33a36c71fd135e993175b4125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 16 Jan 2024 17:31:09 +0000 Subject: [PATCH] Fix leak when reconnecting to back end without reload. Fixes #13215 (#13250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../browser/messaging/ws-connection-source.ts | 62 ++++++++++++------- .../websocket-frontend-connection-service.ts | 15 +++-- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/packages/core/src/browser/messaging/ws-connection-source.ts b/packages/core/src/browser/messaging/ws-connection-source.ts index a1e9b83814035..24d7d7920143f 100644 --- a/packages/core/src/browser/messaging/ws-connection-source.ts +++ b/packages/core/src/browser/messaging/ws-connection-source.ts @@ -87,38 +87,58 @@ export class WebSocketConnectionSource implements ConnectionSource { this._socket.connect(); } - protected handleSocketConnected(): void { - if (this.currentChannel) { - const reconnectListener = (hasConnection: boolean) => { - this._socket.off(ConnectionManagementMessages.RECONNECT, reconnectListener); - if (hasConnection) { - this.writeBuffer.flush(this.socket); + protected negogiateReconnect(): void { + const reconnectListener = (hasConnection: boolean) => { + this._socket.off(ConnectionManagementMessages.RECONNECT, reconnectListener); + if (hasConnection) { + console.info(`reconnect succeeded on ${this.socket.id}`); + this.writeBuffer!.flush(this.socket); + } else { + if (FrontendApplicationConfigProvider.get().reloadOnReconnect) { + window.location.reload(); // this might happen in the preload module, when we have no window service yet } else { - if (FrontendApplicationConfigProvider.get().reloadOnReconnect) { - window.location.reload(); // this might happen in the preload module, when we have no window service yet - } else { - this.connectNewChannel(); - } + console.info(`reconnect failed on ${this.socket.id}`); + this.currentChannel.onCloseEmitter.fire({ reason: 'reconnecting channel' }); + this.currentChannel.close(); + this.writeBuffer.drain(); + this.socket.disconnect(); + this.socket.connect(); + this.negotiateInitialConnect(); } - }; - this._socket.on(ConnectionManagementMessages.RECONNECT, reconnectListener); - this._socket.emit(ConnectionManagementMessages.RECONNECT, this.frontendIdProvider.getId()); + } + }; + this._socket.on(ConnectionManagementMessages.RECONNECT, reconnectListener); + console.info(`sending reconnect on ${this.socket.id}`); + this._socket.emit(ConnectionManagementMessages.RECONNECT, this.frontendIdProvider.getId()); + } + + protected negotiateInitialConnect(): void { + const initialConnectListener = () => { + console.info(`initial connect received on ${this.socket.id}`); + + this._socket.off(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); + this.connectNewChannel(); + }; + this._socket.on(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); + console.info(`sending initial connect on ${this.socket.id}`); + + this._socket.emit(ConnectionManagementMessages.INITIAL_CONNECT, this.frontendIdProvider.getId()); + } + + protected handleSocketConnected(): void { + if (this.currentChannel) { + this.negogiateReconnect(); } else { - const initialConnectListener = () => { - this._socket.off(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); - this.connectNewChannel(); - }; - this._socket.on(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); - this._socket.emit(ConnectionManagementMessages.INITIAL_CONNECT, this.frontendIdProvider.getId()); + this.negotiateInitialConnect(); } } connectNewChannel(): void { if (this.currentChannel) { - this.writeBuffer.drain(); this.currentChannel.close(); this.currentChannel.onCloseEmitter.fire({ reason: 'reconnecting channel' }); } + this.writeBuffer.drain(); this.currentChannel = this.createChannel(); this.onConnectionDidOpenEmitter.fire(this.currentChannel); } diff --git a/packages/core/src/node/messaging/websocket-frontend-connection-service.ts b/packages/core/src/node/messaging/websocket-frontend-connection-service.ts index 648edd0f14a55..633dc1d410aff 100644 --- a/packages/core/src/node/messaging/websocket-frontend-connection-service.ts +++ b/packages/core/src/node/messaging/websocket-frontend-connection-service.ts @@ -51,7 +51,9 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer if (this.connectionsByFrontend.has(frontEndId)) { this.closeConnection(frontEndId, 'reconnecting same front end'); } - channelCreatedHandler(this.createConnection(socket, frontEndId)); + const channel = this.createConnection(socket, frontEndId); + this.handleSocketDisconnect(socket, channel, frontEndId); + channelCreatedHandler(channel); socket.emit(ConnectionManagementMessages.INITIAL_CONNECT); }; @@ -63,6 +65,7 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer console.info(`Reconnecting to front end ${frontEndId}`); socket.emit(ConnectionManagementMessages.RECONNECT, true); channel.connect(socket); + this.handleSocketDisconnect(socket, channel, frontEndId); const pendingTimeout = this.closeTimeouts.get(frontEndId); clearTimeout(pendingTimeout); this.closeTimeouts.delete(frontEndId); @@ -89,11 +92,16 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer connection.close(); } - protected createConnection(socket: Socket, frontEndId: string): Channel { + protected createConnection(socket: Socket, frontEndId: string): ReconnectableSocketChannel { console.info(`creating connection for ${frontEndId}`); const channel = new ReconnectableSocketChannel(); channel.connect(socket); + this.connectionsByFrontend.set(frontEndId, channel); + return channel; + } + + handleSocketDisconnect(socket: Socket, channel: ReconnectableSocketChannel, frontEndId: string): void { socket.on('disconnect', evt => { console.info('socked closed'); channel.disconnect(); @@ -111,9 +119,6 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer // timeout < 0: never close the back end } }); - - this.connectionsByFrontend.set(frontEndId, channel); - return channel; } markForClose(channelId: string): void {