Decouple JS device from Store

Summary: Made a start with decoupling JS device. Incomplete as there are still Electron deps.

Reviewed By: timur-valiev

Differential Revision: D30309257

fbshipit-source-id: b8002170cbbe8d68e1795ce7c12ffce4c8eac853
This commit is contained in:
Michel Weststrate
2021-08-17 07:50:43 -07:00
committed by Facebook GitHub Bot
parent a9c6351cf0
commit 3736cbc480
4 changed files with 63 additions and 89 deletions

View File

@@ -10,14 +10,14 @@
import React from 'react'; import React from 'react';
import {Store} from '../reducers/index'; import {Store} from '../reducers/index';
import {Logger} from '../fb-interfaces/Logger'; import {Logger} from '../fb-interfaces/Logger';
import {startFlipperServer} from '../server/FlipperServer'; import {FlipperServer} from '../server/FlipperServer';
import {selectClient, selectDevice} from '../reducers/connections'; import {selectClient, selectDevice} from '../reducers/connections';
import Client from '../Client'; import Client from '../Client';
import {notification} from 'antd'; import {notification} from 'antd';
export default async (store: Store, logger: Logger) => { export default async (store: Store, logger: Logger) => {
const {enableAndroid, androidHome} = store.getState().settingsState; const {enableAndroid, androidHome} = store.getState().settingsState;
const server = startFlipperServer( const server = new FlipperServer(
{ {
enableAndroid, enableAndroid,
androidHome, androidHome,
@@ -101,7 +101,7 @@ export default async (store: Store, logger: Logger) => {
} }
server server
.waitForServerStarted() .start()
.then(() => { .then(() => {
console.log( console.log(
'Flipper server started and accepting device / client connections', 'Flipper server started and accepting device / client connections',

View File

@@ -48,18 +48,18 @@ export interface FlipperServerConfig {
serverPorts: ServerPorts; serverPorts: ServerPorts;
} }
export function startFlipperServer(
config: FlipperServerConfig,
store: Store,
logger: Logger,
): FlipperServer {
const server = new FlipperServer(config, store, logger);
server.start();
return server;
}
type ServerState = 'pending' | 'starting' | 'started' | 'error' | 'closed'; type ServerState = 'pending' | 'starting' | 'started' | 'error' | 'closed';
// defaultConfig should be used for testing only, and disables by default all features
const defaultConfig: FlipperServerConfig = {
androidHome: '',
enableAndroid: false,
serverPorts: {
insecure: -1,
secure: -1,
},
};
/** /**
* FlipperServer takes care of all incoming device & client connections. * FlipperServer takes care of all incoming device & client connections.
* It will set up managers per device type, and create the incoming * It will set up managers per device type, and create the incoming
@@ -69,7 +69,10 @@ type ServerState = 'pending' | 'starting' | 'started' | 'error' | 'closed';
* using '.on'. All events are strongly typed. * using '.on'. All events are strongly typed.
*/ */
export class FlipperServer { export class FlipperServer {
public config: FlipperServerConfig;
private readonly events = new EventEmitter(); private readonly events = new EventEmitter();
// server handles the incoming RSocket / WebSocket connections from Flipper clients
readonly server: ServerController; readonly server: ServerController;
readonly disposers: ((() => void) | void)[] = []; readonly disposers: ((() => void) | void)[] = [];
private readonly devices = new Map<string, BaseDevice>(); private readonly devices = new Map<string, BaseDevice>();
@@ -78,60 +81,14 @@ export class FlipperServer {
// TODO: remove store argument // TODO: remove store argument
constructor( constructor(
public config: FlipperServerConfig, config: Partial<FlipperServerConfig>,
/** @deprecated remove! */ /** @deprecated remove! */
public store: Store, public store: Store,
public logger: Logger, public logger: Logger,
) { ) {
this.server = new ServerController(logger, store); this.config = {...defaultConfig, ...config};
const server = (this.server = new ServerController(this));
this.android = new AndroidDeviceManager(this); this.android = new AndroidDeviceManager(this);
}
setServerState(state: ServerState, error?: Error) {
this.state = state;
this.emit('server-state', {state, error});
}
async waitForServerStarted() {
return new Promise<void>((resolve, reject) => {
switch (this.state) {
case 'closed':
return reject(new Error('Server was closed already'));
case 'error':
return reject(new Error('Server has errored already'));
case 'started':
return resolve();
default: {
const listener = ({
state,
error,
}: {
state: ServerState;
error: Error;
}) => {
switch (state) {
case 'error':
return reject(error);
case 'started':
return resolve();
case 'closed':
return reject(new Error('Server closed'));
}
this.events.off('server-state', listener);
};
this.events.on('server-state', listener);
}
}
});
}
/** @private */
async start() {
if (this.state !== 'pending') {
throw new Error('Server already started');
}
this.setServerState('starting');
const server = this.server;
server.addListener('new-client', (client: Client) => { server.addListener('new-client', (client: Client) => {
this.emit('client-connected', client); this.emit('client-connected', client);
@@ -214,9 +171,24 @@ export class FlipperServer {
); );
}, },
); );
}
setServerState(state: ServerState, error?: Error) {
this.state = state;
this.emit('server-state', {state, error});
}
/**
* Starts listening to parts and watching for devices
*/
async start() {
if (this.state !== 'pending') {
throw new Error('Server already started');
}
this.setServerState('starting');
try { try {
await server.init(); await this.server.init();
await this.startDeviceListeners(); await this.startDeviceListeners();
this.setServerState('started'); this.setServerState('started');
} catch (e) { } catch (e) {

View File

@@ -34,6 +34,7 @@ import ServerAdapter, {
ServerEventsListener, ServerEventsListener,
} from './ServerAdapter'; } from './ServerAdapter';
import {createBrowserServer, createServer} from './ServerFactory'; import {createBrowserServer, createServer} from './ServerFactory';
import {FlipperServer} from '../FlipperServer';
type ClientInfo = { type ClientInfo = {
connection: ClientConnection | null | undefined; connection: ClientConnection | null | undefined;
@@ -70,29 +71,38 @@ class ServerController extends EventEmitter implements ServerEventsListener {
certificateProvider: CertificateProvider; certificateProvider: CertificateProvider;
connectionTracker: ConnectionTracker; connectionTracker: ConnectionTracker;
logger: Logger; flipperServer: FlipperServer;
store: Store;
timeHandler: NodeJS.Timeout | undefined; timeHandler: NodeJS.Timeout | undefined;
constructor(logger: Logger, store: Store) { constructor(flipperServer: FlipperServer) {
super(); super();
this.logger = logger; this.flipperServer = flipperServer;
this.connections = new Map(); this.connections = new Map();
this.certificateProvider = new CertificateProvider( this.certificateProvider = new CertificateProvider(
this, this,
logger, this.logger,
store.getState().settingsState, this.store.getState().settingsState,
); );
this.connectionTracker = new ConnectionTracker(logger); this.connectionTracker = new ConnectionTracker(this.logger);
this.secureServer = null; this.secureServer = null;
this.insecureServer = null; this.insecureServer = null;
this.browserServer = null; this.browserServer = null;
this.initialized = null; this.initialized = null;
this.store = store;
this.timeHandler = undefined; this.timeHandler = undefined;
} }
get logger(): Logger {
return this.flipperServer.logger;
}
/**
* @deprecated
*/
get store(): Store {
return this.flipperServer.store;
}
/** /**
* Loads the secure server configuration and starts any necessary servers. * Loads the secure server configuration and starts any necessary servers.
* Initialisation is complete once the initialized promise is fullfilled at * Initialisation is complete once the initialized promise is fullfilled at
@@ -117,7 +127,7 @@ class ServerController extends EventEmitter implements ServerEventsListener {
reportPlatformFailures(this.initialized, 'initializeServer'); reportPlatformFailures(this.initialized, 'initializeServer');
if (GK.get('flipper_js_client_emulator')) { if (GK.get('flipper_js_client_emulator')) {
initJsEmulatorIPC(this.store, this.logger, this, this.connections); initJsEmulatorIPC(this.flipperServer, this.connections);
} }
return this.initialized; return this.initialized;

View File

@@ -16,11 +16,8 @@ import {
} from '../../comms/ClientConnection'; } from '../../comms/ClientConnection';
import {ipcRenderer, remote, IpcRendererEvent} from 'electron'; import {ipcRenderer, remote, IpcRendererEvent} from 'electron';
import JSDevice from './JSDevice'; import JSDevice from './JSDevice';
import {Store} from '../../../reducers';
import {Logger} from '../../../fb-interfaces/Logger';
import ServerController from '../../comms/ServerController';
import {buildClientId} from '../../../utils/clientUtils'; import {buildClientId} from '../../../utils/clientUtils';
import {destroyDevice} from '../../../reducers/connections'; import {FlipperServer} from '../../FlipperServer';
const connections: Map<number, JSClientFlipperConnection> = new Map(); const connections: Map<number, JSClientFlipperConnection> = new Map();
@@ -31,9 +28,7 @@ function jsDeviceId(windowId: number): string {
} }
export function initJsEmulatorIPC( export function initJsEmulatorIPC(
store: Store, flipperServer: FlipperServer,
logger: Logger,
flipperServer: ServerController,
flipperConnections: Map< flipperConnections: Map<
string, string,
{ {
@@ -48,10 +43,7 @@ export function initJsEmulatorIPC(
const {windowId} = message; const {windowId} = message;
const {plugins, appName} = message.payload; const {plugins, appName} = message.payload;
const device = new JSDevice(jsDeviceId(windowId), 'jsEmulator', windowId); const device = new JSDevice(jsDeviceId(windowId), 'jsEmulator', windowId);
store.dispatch({ flipperServer.registerDevice(device);
type: 'REGISTER_DEVICE',
payload: device,
});
const connection = new JSClientFlipperConnection(windowId); const connection = new JSClientFlipperConnection(windowId);
connections.set(windowId, connection); connections.set(windowId, connection);
@@ -70,8 +62,8 @@ export function initJsEmulatorIPC(
clientId, clientId,
query, query,
connection, connection,
logger, flipperServer.logger,
store, flipperServer.store,
plugins, plugins,
device, device,
); );
@@ -87,8 +79,8 @@ export function initJsEmulatorIPC(
status == ConnectionStatus.CLOSED status == ConnectionStatus.CLOSED
) { ) {
console.debug(`Device disconnected ${client.id}`, 'server'); console.debug(`Device disconnected ${client.id}`, 'server');
flipperServer.removeConnection(client.id); flipperServer.server.removeConnection(client.id);
destroyDevice(store, logger, jsDeviceId(windowId)); flipperServer.unregisterDevice(jsDeviceId(windowId));
connections.delete(windowId); connections.delete(windowId);
availablePlugins.delete(windowId); availablePlugins.delete(windowId);
} }
@@ -98,8 +90,8 @@ export function initJsEmulatorIPC(
.init() .init()
.then(() => { .then(() => {
console.log(client); console.log(client);
flipperServer.emit('new-client', client); flipperServer.server.emit('new-client', client);
flipperServer.emit('clients-change'); flipperServer.server.emit('clients-change');
client.emit('plugins-change'); client.emit('plugins-change');
ipcRenderer.on( ipcRenderer.on(