UDS/TCP options
Summary: Provide an option to enable/disable TCP connections on flipper-server. The only change at this stage is that Flipper Desktop will use UDS to connect to flipper-server. Reviewed By: passy Differential Revision: D37519656 fbshipit-source-id: 3d02084666fde532ec76134edf8cf6a231060a48
This commit is contained in:
committed by
Facebook GitHub Bot
parent
f46cf2b0ce
commit
646b9d5a5d
@@ -22,7 +22,9 @@
|
|||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"invariant": "^2.2.2",
|
"invariant": "^2.2.2",
|
||||||
"metro-runtime": "^0.70.2",
|
"metro-runtime": "^0.70.2",
|
||||||
"pretty-format": "^27.5.0"
|
"pretty-format": "^27.5.0",
|
||||||
|
"reconnecting-websocket": "^4.4.0",
|
||||||
|
"ws": "8.8.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {},
|
"optionalDependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ import {
|
|||||||
_setGlobalInteractionReporter,
|
_setGlobalInteractionReporter,
|
||||||
_LoggerContext,
|
_LoggerContext,
|
||||||
} from 'flipper-plugin';
|
} from 'flipper-plugin';
|
||||||
import {createFlipperServer, FlipperServerState} from 'flipper-frontend-core';
|
import {
|
||||||
|
createFlipperServerWithSocket,
|
||||||
|
FlipperServerState,
|
||||||
|
} from 'flipper-frontend-core';
|
||||||
import {
|
import {
|
||||||
FlipperServerImpl,
|
FlipperServerImpl,
|
||||||
getEnvironmentInfo,
|
getEnvironmentInfo,
|
||||||
@@ -45,6 +48,8 @@ import {ElectronIpcClientRenderer} from './electronIpc';
|
|||||||
import {checkSocketInUse, makeSocketPath} from 'flipper-server-core';
|
import {checkSocketInUse, makeSocketPath} from 'flipper-server-core';
|
||||||
import {KeytarModule} from 'flipper-server-core/src/utils/keytar';
|
import {KeytarModule} from 'flipper-server-core/src/utils/keytar';
|
||||||
import {initCompanionEnv} from 'flipper-server-companion';
|
import {initCompanionEnv} from 'flipper-server-companion';
|
||||||
|
import ReconnectingWebSocket from 'reconnecting-websocket';
|
||||||
|
import WS from 'ws';
|
||||||
|
|
||||||
enableMapSet();
|
enableMapSet();
|
||||||
|
|
||||||
@@ -70,10 +75,13 @@ async function getKeytarModule(staticPath: string): Promise<KeytarModule> {
|
|||||||
return keytar;
|
return keytar;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getExternalServer() {
|
async function getExternalServer(path: string) {
|
||||||
const server = await createFlipperServer(
|
const options = {
|
||||||
'localhost',
|
WebSocket: WS,
|
||||||
52342,
|
};
|
||||||
|
const socket = new ReconnectingWebSocket(`ws+unix://${path}`, [], options);
|
||||||
|
const server = await createFlipperServerWithSocket(
|
||||||
|
socket,
|
||||||
(_state: FlipperServerState) => {},
|
(_state: FlipperServerState) => {},
|
||||||
);
|
);
|
||||||
return server;
|
return server;
|
||||||
@@ -105,7 +113,7 @@ async function getFlipperServer(
|
|||||||
|
|
||||||
const getEmbeddedServer = async () => {
|
const getEmbeddedServer = async () => {
|
||||||
if (serverRunning) {
|
if (serverRunning) {
|
||||||
const server = await getExternalServer();
|
const server = await getExternalServer(socketPath);
|
||||||
await server.exec('shutdown').catch(() => {
|
await server.exec('shutdown').catch(() => {
|
||||||
/** shutdown will ultimately make this request fail, ignore error. */
|
/** shutdown will ultimately make this request fail, ignore error. */
|
||||||
});
|
});
|
||||||
@@ -114,7 +122,6 @@ async function getFlipperServer(
|
|||||||
{
|
{
|
||||||
environmentInfo,
|
environmentInfo,
|
||||||
env: parseEnvironmentVariables(env),
|
env: parseEnvironmentVariables(env),
|
||||||
// TODO: make username parameterizable
|
|
||||||
gatekeepers: gatekeepers,
|
gatekeepers: gatekeepers,
|
||||||
paths: {
|
paths: {
|
||||||
appPath,
|
appPath,
|
||||||
@@ -142,9 +149,10 @@ async function getFlipperServer(
|
|||||||
console.info('flipper-server: not running/listening, start');
|
console.info('flipper-server: not running/listening, start');
|
||||||
|
|
||||||
const {readyForIncomingConnections} = await startServer({
|
const {readyForIncomingConnections} = await startServer({
|
||||||
port: 52342,
|
|
||||||
staticDir: staticPath,
|
staticDir: staticPath,
|
||||||
entry: 'index.web.dev.html',
|
entry: 'index.web.dev.html',
|
||||||
|
tcp: false,
|
||||||
|
port: 52342,
|
||||||
});
|
});
|
||||||
|
|
||||||
const server = await startFlipperServer(
|
const server = await startFlipperServer(
|
||||||
@@ -167,7 +175,7 @@ async function getFlipperServer(
|
|||||||
tailServerLogs(path.join(staticPath, loggerOutputFile));
|
tailServerLogs(path.join(staticPath, loggerOutputFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
return getExternalServer();
|
return getExternalServer(socketPath);
|
||||||
}
|
}
|
||||||
return getEmbeddedServer();
|
return getEmbeddedServer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ export function createFlipperServer(
|
|||||||
host: string,
|
host: string,
|
||||||
port: number,
|
port: number,
|
||||||
onStateChange: (state: FlipperServerState) => void,
|
onStateChange: (state: FlipperServerState) => void,
|
||||||
|
): Promise<FlipperServer> {
|
||||||
|
const socket = new ReconnectingWebSocket(`ws://${host}:${port}`);
|
||||||
|
return createFlipperServerWithSocket(socket, onStateChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createFlipperServerWithSocket(
|
||||||
|
socket: ReconnectingWebSocket,
|
||||||
|
onStateChange: (state: FlipperServerState) => void,
|
||||||
): Promise<FlipperServer> {
|
): Promise<FlipperServer> {
|
||||||
onStateChange(FlipperServerState.CONNECTING);
|
onStateChange(FlipperServerState.CONNECTING);
|
||||||
|
|
||||||
@@ -40,7 +48,6 @@ export function createFlipperServer(
|
|||||||
|
|
||||||
const eventEmitter = new EventEmitter();
|
const eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
const socket = new ReconnectingWebSocket(`ws://${host}:${port}`);
|
|
||||||
const pendingRequests: Map<
|
const pendingRequests: Map<
|
||||||
number,
|
number,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import express, {Express} from 'express';
|
|||||||
import http, {ServerResponse} from 'http';
|
import http, {ServerResponse} from 'http';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import {VerifyClientCallbackSync, WebSocketServer} from 'ws';
|
import {ServerOptions, VerifyClientCallbackSync, WebSocketServer} from 'ws';
|
||||||
import {WEBSOCKET_MAX_MESSAGE_SIZE} from '../comms/ServerWebSocket';
|
import {WEBSOCKET_MAX_MESSAGE_SIZE} from '../comms/ServerWebSocket';
|
||||||
import {parse} from 'url';
|
import {parse} from 'url';
|
||||||
import {makeSocketPath, checkSocketInUse} from './utilities';
|
import {makeSocketPath, checkSocketInUse} from './utilities';
|
||||||
@@ -28,6 +28,7 @@ type Config = {
|
|||||||
port: number;
|
port: number;
|
||||||
staticDir: string;
|
staticDir: string;
|
||||||
entry: string;
|
entry: string;
|
||||||
|
tcp: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ReadyForConnections = (
|
type ReadyForConnections = (
|
||||||
@@ -108,6 +109,12 @@ async function startProxyServer(
|
|||||||
// On Windows, a proxy is not created and the server starts
|
// On Windows, a proxy is not created and the server starts
|
||||||
// listening at the specified port.
|
// listening at the specified port.
|
||||||
if (os.platform() === 'win32') {
|
if (os.platform() === 'win32') {
|
||||||
|
if (!config.tcp) {
|
||||||
|
console.error(
|
||||||
|
'No port was supplied and domain socket access is not available for non-POSIX systems, unable to start server',
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
console.log(`Starting server on http://localhost:${config.port}`);
|
console.log(`Starting server on http://localhost:${config.port}`);
|
||||||
const readyForIncomingConnections = (): Promise<void> => {
|
const readyForIncomingConnections = (): Promise<void> => {
|
||||||
@@ -129,17 +136,22 @@ async function startProxyServer(
|
|||||||
await fs.rm(socketPath, {force: true});
|
await fs.rm(socketPath, {force: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
const proxyServer = proxy.createProxyServer({
|
const proxyServer: proxy | undefined = config.tcp
|
||||||
|
? proxy.createProxyServer({
|
||||||
target: {host: 'localhost', port: 0, socketPath},
|
target: {host: 'localhost', port: 0, socketPath},
|
||||||
autoRewrite: true,
|
autoRewrite: true,
|
||||||
ws: true,
|
ws: true,
|
||||||
});
|
})
|
||||||
|
: undefined;
|
||||||
|
|
||||||
console.log('Starting socket server on ', socketPath);
|
console.log('Starting socket server on ', socketPath);
|
||||||
|
if (proxyServer) {
|
||||||
console.log(`Starting proxy server on http://localhost:${config.port}`);
|
console.log(`Starting proxy server on http://localhost:${config.port}`);
|
||||||
|
}
|
||||||
|
|
||||||
exitHook(() => {
|
exitHook(() => {
|
||||||
console.log('Shutdown server');
|
console.log('Shutdown server');
|
||||||
proxyServer.close();
|
proxyServer?.close();
|
||||||
server.close();
|
server.close();
|
||||||
|
|
||||||
console.log('Cleaning up socket on exit:', socketPath);
|
console.log('Cleaning up socket on exit:', socketPath);
|
||||||
@@ -148,7 +160,7 @@ async function startProxyServer(
|
|||||||
fs.rmSync(socketPath, {force: true});
|
fs.rmSync(socketPath, {force: true});
|
||||||
});
|
});
|
||||||
|
|
||||||
proxyServer.on('error', (err, _req, res) => {
|
proxyServer?.on('error', (err, _req, res) => {
|
||||||
console.warn('Error in proxy server:', err);
|
console.warn('Error in proxy server:', err);
|
||||||
if (res instanceof ServerResponse) {
|
if (res instanceof ServerResponse) {
|
||||||
res.writeHead(502, 'Failed to proxy request');
|
res.writeHead(502, 'Failed to proxy request');
|
||||||
@@ -163,7 +175,7 @@ async function startProxyServer(
|
|||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
attachSocketServer(socket, serverImpl, companionEnv);
|
attachSocketServer(socket, serverImpl, companionEnv);
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
proxyServer.listen(config.port);
|
proxyServer?.listen(config.port);
|
||||||
server.listen(socketPath, undefined, () => resolve());
|
server.listen(socketPath, undefined, () => resolve());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -222,12 +234,15 @@ function addWebsocket(server: http.Server, config: Config) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const wss = new WebSocketServer({
|
const options: ServerOptions = {
|
||||||
noServer: true,
|
noServer: true,
|
||||||
maxPayload: WEBSOCKET_MAX_MESSAGE_SIZE,
|
maxPayload: WEBSOCKET_MAX_MESSAGE_SIZE,
|
||||||
verifyClient,
|
};
|
||||||
});
|
if (config.tcp) {
|
||||||
|
options.verifyClient = verifyClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wss = new WebSocketServer(options);
|
||||||
server.on('upgrade', function upgrade(request, socket, head) {
|
server.on('upgrade', function upgrade(request, socket, head) {
|
||||||
const {pathname} = parse(request.url!);
|
const {pathname} = parse(request.url!);
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const argv = yargs
|
|||||||
.usage('yarn flipper-server [args]')
|
.usage('yarn flipper-server [args]')
|
||||||
.options({
|
.options({
|
||||||
port: {
|
port: {
|
||||||
describe: 'Port to serve on',
|
describe: 'TCP port to serve on',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
default: 52342,
|
default: 52342,
|
||||||
},
|
},
|
||||||
@@ -55,6 +55,12 @@ const argv = yargs
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
tcp: {
|
||||||
|
describe:
|
||||||
|
'Open a TCP port (--no-tcp can be specified as to use unix-domain-socket exclusively)',
|
||||||
|
type: 'boolean',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.version('DEV')
|
.version('DEV')
|
||||||
.help()
|
.help()
|
||||||
@@ -94,9 +100,10 @@ async function start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {app, server, socket, readyForIncomingConnections} = await startServer({
|
const {app, server, socket, readyForIncomingConnections} = await startServer({
|
||||||
port: argv.port,
|
|
||||||
staticDir,
|
staticDir,
|
||||||
entry: `index.web${argv.bundler ? '.dev' : ''}.html`,
|
entry: `index.web${argv.bundler ? '.dev' : ''}.html`,
|
||||||
|
port: argv.port,
|
||||||
|
tcp: argv.tcp,
|
||||||
});
|
});
|
||||||
|
|
||||||
const flipperServer = await startFlipperServer(
|
const flipperServer = await startFlipperServer(
|
||||||
@@ -150,6 +157,10 @@ process.on('unhandledRejection', (reason, promise) => {
|
|||||||
|
|
||||||
start()
|
start()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
if (!argv.tcp) {
|
||||||
|
console.log('Flipper server started and listening');
|
||||||
|
return;
|
||||||
|
}
|
||||||
console.log(
|
console.log(
|
||||||
'Flipper server started and listening at port ' + chalk.green(argv.port),
|
'Flipper server started and listening at port ' + chalk.green(argv.port),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -71,6 +71,11 @@ const argv = yargs
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
tcp: {
|
||||||
|
describe: 'Enable TCP connections on flipper-server.',
|
||||||
|
type: 'boolean',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
'rebuild-plugins': {
|
'rebuild-plugins': {
|
||||||
describe:
|
describe:
|
||||||
'Enables rebuilding of default plugins on Flipper build. Only make sense in conjunction with "--no-bundled-plugins". Enabled by default, but if disabled using "--no-plugin-rebuild", then plugins are just released as is without rebuilding. This can save some time if you know plugin bundles are already up-to-date.',
|
'Enables rebuilding of default plugins on Flipper build. Only make sense in conjunction with "--no-bundled-plugins". Enabled by default, but if disabled using "--no-plugin-rebuild", then plugins are just released as is without rebuilding. This can save some time if you know plugin bundles are already up-to-date.',
|
||||||
@@ -296,16 +301,28 @@ async function runPostBuildAction(archive: string, dir: string) {
|
|||||||
// didn't change
|
// didn't change
|
||||||
console.log(`⚙️ Installing flipper-server.tgz using npx`);
|
console.log(`⚙️ Installing flipper-server.tgz using npx`);
|
||||||
await fs.remove(path.join(homedir(), '.npm', '_npx'));
|
await fs.remove(path.join(homedir(), '.npm', '_npx'));
|
||||||
await spawn('npx', [archive, argv.open ? '--open' : '--no-open'], {
|
await spawn(
|
||||||
|
'npx',
|
||||||
|
[
|
||||||
|
archive,
|
||||||
|
argv.open ? '--open' : '--no-open',
|
||||||
|
argv.tcp ? '--tcp' : '--no-tcp',
|
||||||
|
],
|
||||||
|
{
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
});
|
},
|
||||||
|
);
|
||||||
} else if (argv.start) {
|
} else if (argv.start) {
|
||||||
console.log(`⚙️ Starting flipper-server from build dir`);
|
console.log(`⚙️ Starting flipper-server from build dir`);
|
||||||
await yarnInstall(dir);
|
await yarnInstall(dir);
|
||||||
await spawn('./server.js', [argv.open ? '--open' : '--no-open'], {
|
await spawn(
|
||||||
|
'./server.js',
|
||||||
|
[argv.open ? '--open' : '--no-open', argv.tcp ? '--tcp' : '--no-tcp'],
|
||||||
|
{
|
||||||
cwd: dir,
|
cwd: dir,
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -603,7 +603,11 @@ export function sleep(ms: number) {
|
|||||||
|
|
||||||
let proc: child.ChildProcess | undefined;
|
let proc: child.ChildProcess | undefined;
|
||||||
|
|
||||||
export async function launchServer(startBundler: boolean, open: boolean) {
|
export async function launchServer(
|
||||||
|
startBundler: boolean,
|
||||||
|
open: boolean,
|
||||||
|
tcp: boolean,
|
||||||
|
) {
|
||||||
if (proc) {
|
if (proc) {
|
||||||
console.log('⚙️ Killing old flipper-server...');
|
console.log('⚙️ Killing old flipper-server...');
|
||||||
proc.kill(9);
|
proc.kill(9);
|
||||||
@@ -617,6 +621,7 @@ export async function launchServer(startBundler: boolean, open: boolean) {
|
|||||||
`../flipper-server/server.js`,
|
`../flipper-server/server.js`,
|
||||||
startBundler ? `--bundler` : `--no-bundler`,
|
startBundler ? `--bundler` : `--no-bundler`,
|
||||||
open ? `--open` : `--no-open`,
|
open ? `--open` : `--no-open`,
|
||||||
|
tcp ? `--tcp` : `--no-tcp`,
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: serverDir,
|
cwd: serverDir,
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ const argv = yargs
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
tcp: {
|
||||||
|
describe: 'Enable TCP connections on flipper-server.',
|
||||||
|
type: 'boolean',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
channel: {
|
channel: {
|
||||||
description: 'Release channel for the build',
|
description: 'Release channel for the build',
|
||||||
choices: ['stable', 'insiders'],
|
choices: ['stable', 'insiders'],
|
||||||
@@ -131,7 +136,7 @@ let startCount = 0;
|
|||||||
async function restartServer() {
|
async function restartServer() {
|
||||||
try {
|
try {
|
||||||
await compileServerMain(true);
|
await compileServerMain(true);
|
||||||
await launchServer(true, argv.open && ++startCount === 1); // only open on the first time
|
await launchServer(true, argv.open && ++startCount === 1, argv.tcp); // only open on the first time
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(
|
console.error(
|
||||||
chalk.red(
|
chalk.red(
|
||||||
|
|||||||
@@ -14605,6 +14605,11 @@ write-json-file@^4.1.1:
|
|||||||
sort-keys "^4.0.0"
|
sort-keys "^4.0.0"
|
||||||
write-file-atomic "^3.0.0"
|
write-file-atomic "^3.0.0"
|
||||||
|
|
||||||
|
ws@8.8.0:
|
||||||
|
version "8.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769"
|
||||||
|
integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==
|
||||||
|
|
||||||
ws@^7.0.0, ws@^7.4.5, ws@^7.5.1:
|
ws@^7.0.0, ws@^7.4.5, ws@^7.5.1:
|
||||||
version "7.5.7"
|
version "7.5.7"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67"
|
||||||
|
|||||||
Reference in New Issue
Block a user