From 5693ac7205b3dff1c86e9c3dc6c8e9f69af816bc Mon Sep 17 00:00:00 2001 From: Andrey Goncharov Date: Wed, 22 Nov 2023 02:59:27 -0800 Subject: [PATCH] Disconnect all mobile clients when all UI clients leave Summary: Context https://fb.workplace.com/groups/flippersupport/permalink/1730762380737746/ Reviewed By: lblasa Differential Revision: D51510348 fbshipit-source-id: afafcdd6b89bf1038fec65a7c3e8c2dd9cfd0768 --- .../src/FlipperServerImpl.tsx | 31 +++++++++++++++++++ .../BrowserServerWebSocket.tsx | 4 +++ .../src/app-connectivity/ServerRSocket.tsx | 5 +++ .../src/app-connectivity/ServerWebSocket.tsx | 9 ++++++ .../app-connectivity/ServerWebSocketBase.tsx | 20 ++++++++++++ .../src/server/attachSocketServer.tsx | 5 +++ 6 files changed, 74 insertions(+) diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index ef9c437af..577075b7f 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -59,6 +59,7 @@ import {DebuggableDevice} from './devices/DebuggableDevice'; import {jfUpload} from './fb-stubs/jf'; import path from 'path'; import {movePWA} from './utils/findInstallation'; +import GK from './fb-stubs/GK'; const {access, copyFile, mkdir, unlink, stat, readlink, readFile, writeFile} = promises; @@ -109,6 +110,7 @@ export class FlipperServerImpl implements FlipperServer { keytarManager: KeytarManager; pluginManager: PluginManager; unresponsiveClients: Set = new Set(); + private acceptingNewConections = true; constructor( public config: FlipperServerConfig, @@ -175,6 +177,35 @@ export class FlipperServerImpl implements FlipperServer { ); } + startAcceptingNewConections() { + if (!GK.get('flipper_disconnect_device_when_ui_offline')) { + return; + } + if (this.acceptingNewConections) { + return; + } + this.acceptingNewConections = true; + + this.server.insecureServer?.startAcceptingNewConections(); + this.server.altInsecureServer?.startAcceptingNewConections(); + this.server.secureServer?.startAcceptingNewConections(); + this.server.altSecureServer?.startAcceptingNewConections(); + this.server.browserServer?.startAcceptingNewConections(); + } + + stopAcceptingNewConections() { + if (!GK.get('flipper_disconnect_device_when_ui_offline')) { + return; + } + this.acceptingNewConections = false; + + this.server.insecureServer?.stopAcceptingNewConections(); + this.server.altInsecureServer?.stopAcceptingNewConections(); + this.server.secureServer?.stopAcceptingNewConections(); + this.server.altSecureServer?.stopAcceptingNewConections(); + this.server.browserServer?.stopAcceptingNewConections(); + } + setServerState(state: FlipperServerState, error?: Error) { this.state = state; this.stateError = '' + error; diff --git a/desktop/flipper-server-core/src/app-connectivity/BrowserServerWebSocket.tsx b/desktop/flipper-server-core/src/app-connectivity/BrowserServerWebSocket.tsx index 72512b872..f1afa65c7 100644 --- a/desktop/flipper-server-core/src/app-connectivity/BrowserServerWebSocket.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/BrowserServerWebSocket.tsx @@ -148,6 +148,10 @@ class BrowserServerWebSocket extends SecureServerWebSocket { protected verifyClient(): ws.VerifyClientCallbackSync { return (info: {origin: string; req: IncomingMessage; secure: boolean}) => { + if (!this.acceptingNewConections) { + return false; + } + if (isFBBuild) { try { const urlObj = new URL(info.origin); diff --git a/desktop/flipper-server-core/src/app-connectivity/ServerRSocket.tsx b/desktop/flipper-server-core/src/app-connectivity/ServerRSocket.tsx index 84515b68d..375101781 100644 --- a/desktop/flipper-server-core/src/app-connectivity/ServerRSocket.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/ServerRSocket.tsx @@ -293,6 +293,11 @@ class ServerRSocket extends ServerWebSocketBase { }, }; }; + + protected stopAcceptingNewConectionsImpl(): void { + // Did not find a straightforard way to iterate through RSocket open connections and close them. + // We probably should not care and invest in it anyway as we are going to remove RScokets. + } } export default ServerRSocket; diff --git a/desktop/flipper-server-core/src/app-connectivity/ServerWebSocket.tsx b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocket.tsx index 100ba2d48..58511fb81 100644 --- a/desktop/flipper-server-core/src/app-connectivity/ServerWebSocket.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocket.tsx @@ -294,11 +294,20 @@ class ServerWebSocket extends ServerWebSocketBase { */ protected verifyClient(): VerifyClientCallbackSync { return (_info: {origin: string; req: IncomingMessage; secure: boolean}) => { + if (!this.acceptingNewConections) { + return false; + } // Client verification is not necessary. The connected client has // already been verified using its certificate signed by the server. return true; }; } + + protected stopAcceptingNewConectionsImpl(): void { + this.wsServer?.clients.forEach((client) => + client.close(WSCloseCode.GoingAway), + ); + } } export default ServerWebSocket; diff --git a/desktop/flipper-server-core/src/app-connectivity/ServerWebSocketBase.tsx b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocketBase.tsx index b7ae2824e..84e1cbdb5 100644 --- a/desktop/flipper-server-core/src/app-connectivity/ServerWebSocketBase.tsx +++ b/desktop/flipper-server-core/src/app-connectivity/ServerWebSocketBase.tsx @@ -15,6 +15,7 @@ import { SignCertificateMessage, } from 'flipper-common'; import {SecureServerConfig} from './certificate-exchange/certificate-utils'; +import GK from '../fb-stubs/GK'; /** * Defines an interface for events triggered by a running server interacting @@ -98,6 +99,8 @@ export interface ServerEventsListener { * RSocket, WebSocket, etc. */ abstract class ServerWebSocketBase { + protected acceptingNewConections = true; + constructor(protected listener: ServerEventsListener) {} /** @@ -169,6 +172,23 @@ abstract class ServerWebSocketBase { return undefined; } + + startAcceptingNewConections() { + if (!GK.get('flipper_disconnect_device_when_ui_offline')) { + return; + } + this.acceptingNewConections = true; + } + + stopAcceptingNewConections() { + if (!GK.get('flipper_disconnect_device_when_ui_offline')) { + return; + } + this.acceptingNewConections = false; + this.stopAcceptingNewConectionsImpl(); + } + + protected abstract stopAcceptingNewConectionsImpl(): void; } export default ServerWebSocketBase; diff --git a/desktop/flipper-server-core/src/server/attachSocketServer.tsx b/desktop/flipper-server-core/src/server/attachSocketServer.tsx index 7a56d498b..aad29f673 100644 --- a/desktop/flipper-server-core/src/server/attachSocketServer.tsx +++ b/desktop/flipper-server-core/src/server/attachSocketServer.tsx @@ -72,6 +72,7 @@ export function attachSocketServer( server.emit('browser-connection-created', {}); let connected = true; + server.startAcceptingNewConections(); let flipperServerCompanion: FlipperServerCompanion | undefined; if (req.url) { @@ -253,6 +254,10 @@ export function attachSocketServer( sessionLength: performance.now() - t0, }); + if (numberOfConnectedClients === 0) { + server.stopAcceptingNewConections(); + } + if ( getFlipperServerConfig().environmentInfo.isHeadlessBuild && isProduction()