Remove UDS and thus the need for a proxy server

Summary: This change removes the UDS support and thus the need for having a proxy server.

Reviewed By: antonk52

Differential Revision: D48265244

fbshipit-source-id: c76bb4afba63959ddd17901b3887aa278b000beb
This commit is contained in:
Lorenzo Blasa
2023-08-11 08:19:51 -07:00
committed by Facebook GitHub Bot
parent 7f3f1c0507
commit b5ed57b7d0
4 changed files with 6 additions and 163 deletions

View File

@@ -47,7 +47,6 @@ import path from 'path';
import fs from 'fs-extra';
import os from 'os';
import {ElectronIpcClientRenderer} from './electronIpc';
import {checkSocketInUse, makeSocketPath} from 'flipper-server-core';
import {KeytarModule} from 'flipper-server-core/src/utils/keytar';
import {initCompanionEnv} from 'flipper-server-companion';
import ReconnectingWebSocket from 'reconnecting-websocket';
@@ -118,8 +117,6 @@ async function getFlipperServer(
const serverUsageEnabled = gatekeepers['flipper_desktop_use_server'];
const settings = await loadSettings();
const socketPath = await makeSocketPath();
const port = 52342;
/**
* Only attempt to use the auth token if one is available. Otherwise,
@@ -134,7 +131,6 @@ async function getFlipperServer(
// check first with the actual TCP socket
const searchParams = new URLSearchParams(token ? {token} : {});
const TCPconnectionURL = new URL(`ws://localhost:${port}?${searchParams}`);
const UDSconnectionURL = new URL(`ws+unix://${socketPath}`);
/**
* Attempt to shutdown a running instance of Flipper server.
@@ -159,12 +155,7 @@ async function getFlipperServer(
*/
if (await checkPortInUse(port)) {
console.warn(`[flipper-server] TCP port ${port} is already in use.`);
await shutdown(TCPconnectionURL);
} else if (await checkSocketInUse(socketPath)) {
console.warn(`[flipper-server] UDS socket is already in use.`);
await shutdown(UDSconnectionURL);
}
const [homePath, tempPath, desktopPath] = await Promise.all([
@@ -223,9 +214,7 @@ async function getFlipperServer(
await server.connect();
await readyForIncomingConnections(server, companionEnv);
return getExternalServer(
os.platform() === 'win32' ? TCPconnectionURL : UDSconnectionURL,
);
return getExternalServer(TCPconnectionURL);
}
return getEmbeddedServer();
}

View File

@@ -25,7 +25,6 @@
"flipper-server-companion": "0.0.0",
"form-data": "^4.0.0",
"fs-extra": "^11.1.0",
"http-proxy": "^1.18.1",
"invariant": "^2.2.4",
"js-base64": "^3.7.5",
"jsonwebtoken": "^9.0.0",
@@ -52,7 +51,6 @@
"devDependencies": {
"@types/archiver": "^5.3.1",
"@types/express": "^4.17.13",
"@types/http-proxy": "^1.17.8",
"@types/invariant": "^2.2.35",
"@types/jsonwebtoken": "^9.0.1",
"@types/memorystream": "^0.3.0",

View File

@@ -7,18 +7,13 @@
* @format
*/
import os from 'os';
import express, {Express} from 'express';
import http, {ServerResponse} from 'http';
import http from 'http';
import path from 'path';
import fs from 'fs-extra';
import {ServerOptions, VerifyClientCallbackSync, WebSocketServer} from 'ws';
import {WEBSOCKET_MAX_MESSAGE_SIZE} from '../app-connectivity/ServerWebSocket';
import {parse} from 'url';
import {makeSocketPath, checkSocketInUse} from './utilities';
import proxy from 'http-proxy';
import exitHook from 'exit-hook';
import {attachSocketServer} from './attachSocketServer';
import {FlipperServerImpl} from '../FlipperServerImpl';
@@ -124,102 +119,23 @@ async function startHTTPServer(config: Config): Promise<{
app.use(express.static(config.staticPath));
return startProxyServer(config, app);
}
/**
* Creates and starts the HTTP and proxy servers.
* @param config Server configuration.
* @param app Express app.
* @returns Returns both the app and server configured and
* listening.
*/
async function startProxyServer(
config: Config,
app: Express,
): Promise<{
app: Express;
server: http.Server;
socket: WebSocketServer;
readyForIncomingConnections: ReadyForConnections;
}> {
const server = http.createServer(app);
const socket = addWebsocket(server, config);
// For now, we only support domain socket access on POSIX-like systems.
// On Windows, a proxy is not created and the server starts
// listening at the specified port.
if (os.platform() === 'win32') {
return new Promise((resolve) => {
console.log(`Starting server on http://localhost:${config.port}`);
const readyForIncomingConnections = (
serverImpl: FlipperServerImpl,
companionEnv: FlipperServerCompanionEnv,
): Promise<void> => {
attachSocketServer(socket, serverImpl, companionEnv);
return new Promise((resolve) => {
server.listen(config.port, undefined, () => resolve());
});
};
resolve({app, server, socket, readyForIncomingConnections});
});
}
const socketPath = await makeSocketPath();
if (await checkSocketInUse(socketPath)) {
console.warn(
`Cannot start flipper-server because socket ${socketPath} is in use.`,
);
tracker.track('server-socket-already-in-use', {});
} else {
console.info(`Cleaning up stale socket ${socketPath}`);
await fs.rm(socketPath, {force: true});
}
const proxyServer: proxy | undefined = proxy.createProxyServer({
target: {host: 'localhost', port: 0, socketPath},
autoRewrite: true,
ws: true,
});
console.log('Starting socket server on ', socketPath);
if (proxyServer) {
console.log(`Starting proxy server on http://localhost:${config.port}`);
}
const socket = attachWS(server, config);
exitHook(() => {
console.log('Shutdown server');
proxyServer?.close();
server.close();
console.log('Cleaning up socket on exit:', socketPath);
// This *must* run synchronously and we're not blocking any UI loop by definition.
// eslint-disable-next-line node/no-sync
fs.rmSync(socketPath, {force: true});
});
proxyServer?.on('error', (err, _req, _res) => {
console.warn('Error in proxy server:', err);
tracker.track('server-proxy-error', {error: err.message});
// As it stands, if the proxy fails, which is the one
// listening on the supplied TCP port, then we should exit.
// Otherwise we risk staying in an invalid state, unable
// to recover, and thus preventing us to serve incoming requests.
// Bear in mind that exiting the process will trigger the exit hook
// defined above which will clean and close the sockets.
process.exit(0);
});
return new Promise((resolve) => {
console.log(`Starting server on http://localhost:${config.port}`);
const readyForIncomingConnections = (
serverImpl: FlipperServerImpl,
companionEnv: FlipperServerCompanionEnv,
): Promise<void> => {
attachSocketServer(socket, serverImpl, companionEnv);
return new Promise((resolve) => {
proxyServer?.listen(config.port);
server.listen(socketPath, undefined, () => {
server.listen(config.port, undefined, () => {
tracker.track('server-started', {
port: config.port,
});
@@ -238,7 +154,7 @@ async function startProxyServer(
* incoming connections origin.
* @returns Returns the created WS.
*/
function addWebsocket(server: http.Server, config: Config) {
function attachWS(server: http.Server, config: Config) {
const localhost = 'localhost';
const localhostIPV4 = `localhost:${config.port}`;
const localhostIPV6 = `[::1]:${config.port}`;

View File

@@ -39,63 +39,3 @@ export async function checkPortInUse(port: number): Promise<boolean> {
.listen(port);
});
}
/**
* Checks if a socket is in used for given path.
* If the path doesn't exist then is not in use. If it does,
* create a socket client and attempt to connect to it.
* If the connection is established, then there's a process
* already listening. Otherwise, it kind of indicates the
* contrary.
* @param path Path used instead of port number.
* @returns True or false dependning on whether the socket is in
* use or not.
*/
export async function checkSocketInUse(path: string): Promise<boolean> {
if (!(await fs.pathExists(path))) {
return false;
}
return new Promise((resolve, _reject) => {
const client = net
.createConnection(path, () => {
resolve(true);
client.destroy();
})
.on('error', (e) => {
if (e.message.includes('ECONNREFUSED')) {
resolve(false);
} else {
console.warn(
`[conn] Socket ${path} is in use, but we don't know why.`,
e,
);
resolve(false);
}
client.destroy();
});
});
}
/**
* Creates a socket path. Used to open the socket at location.
* Format: flipper-server-${userInfo}.sock
* @returns The created socket path.
*/
export async function makeSocketPath(): Promise<string> {
const runtimeDir = xdgBasedir.runtime || '/tmp';
await fs.mkdirp(runtimeDir);
const filename = `flipper-server-${os.userInfo().uid}.sock`;
const path = `${runtimeDir}/${filename}`;
// Depending on the OS this is between 104 and 108:
// https://unix.stackexchange.com/a/367012/10198
if (path.length >= 104) {
console.warn(
'Ignoring XDG_RUNTIME_DIR as it would exceed the total limit for domain socket paths. See man 7 unix.',
);
// Even with the INT32_MAX userid, we should have plenty of room.
return `/tmp/${filename}`;
}
return path;
}