Migrate from socket.io

Reviewed By: passy

Differential Revision: D34787674

fbshipit-source-id: 63d7c166ea29d14c96f0646a045e3f6fa93472e2
This commit is contained in:
Andrey Goncharov
2022-03-10 10:31:24 -08:00
committed by Facebook GitHub Bot
parent 6ec3771824
commit f85def32fb
11 changed files with 307 additions and 210 deletions

View File

@@ -11,8 +11,9 @@ import express, {Express} from 'express';
import http from 'http';
import path from 'path';
import fs from 'fs-extra';
import socketio from 'socket.io';
import {VerifyClientCallbackSync, WebSocketServer} from 'ws';
import {WEBSOCKET_MAX_MESSAGE_SIZE} from 'flipper-server-core';
import {parse} from 'url';
type Config = {
port: number;
@@ -23,7 +24,7 @@ type Config = {
export async function startBaseServer(config: Config): Promise<{
app: Express;
server: http.Server;
socket: socketio.Server;
socket: WebSocketServer;
}> {
const {app, server} = await startAssetServer(config);
const socket = addWebsocket(server, config);
@@ -67,36 +68,60 @@ function addWebsocket(server: http.Server, config: Config) {
const localhostIPV6NoBrackets = `::1:${config.port}`;
const possibleHosts = [localhostIPV4, localhostIPV6, localhostIPV6NoBrackets];
const possibleOrigins = possibleHosts.map((host) => `http://${host}`);
const io = new socketio.Server(server, {
maxHttpBufferSize: WEBSOCKET_MAX_MESSAGE_SIZE,
allowRequest(req, callback) {
const noOriginHeader = req.headers.origin === undefined;
if (
noOriginHeader &&
req.headers.host &&
possibleHosts.includes(req.headers.host)
) {
// no origin header? Either the request is not cross-origin,
// or the request is not originating from a browser, so should be OK to pass through
callback(null, true);
} else {
// for now we don't allow cross origin request, so that an arbitrary website cannot try to
// connect a socket to localhost:serverport, and try to use the all powerful Flipper APIs to read
// for example files.
// Potentially in the future we do want to allow this, e.g. if we want to connect to a local flipper-server
// directly from intern. But before that, we should either authenticate the request somehow,
// and discuss security impact and for example scope the files that can be read by Flipper.
console.warn(
`Refused sockect connection from cross domain request, origin: ${
req.headers.origin
}, host: ${req.headers.host}. Expected: ${possibleHosts.join(
' or ',
)}`,
);
callback(null, false);
}
},
const verifyClient: VerifyClientCallbackSync = ({origin, req}) => {
const noOriginHeader = origin === undefined;
if (
(noOriginHeader || possibleOrigins.includes(origin)) &&
req.headers.host &&
possibleHosts.includes(req.headers.host)
) {
// no origin header? The request is not originating from a browser, so should be OK to pass through
// If origin matches our own address, it means we are serving the page
return true;
} else {
// for now we don't allow cross origin request, so that an arbitrary website cannot try to
// connect a socket to localhost:serverport, and try to use the all powerful Flipper APIs to read
// for example files.
// Potentially in the future we do want to allow this, e.g. if we want to connect to a local flipper-server
// directly from intern. But before that, we should either authenticate the request somehow,
// and discuss security impact and for example scope the files that can be read by Flipper.
console.warn(
`Refused socket connection from cross domain request, origin: ${origin}, host: ${
req.headers.host
}. Expected origins: ${possibleOrigins.join(
' or ',
)}. Expected hosts: ${possibleHosts.join(' or ')}`,
);
return false;
}
};
const wss = new WebSocketServer({
noServer: true,
maxPayload: WEBSOCKET_MAX_MESSAGE_SIZE,
verifyClient,
});
return io;
server.on('upgrade', function upgrade(request, socket, head) {
const {pathname} = parse(request.url);
// Handled by Metro
if (pathname === '/hot') {
return;
}
if (pathname === '/') {
wss.handleUpgrade(request, socket, head, function done(ws) {
wss.emit('connection', ws, request);
});
return;
}
console.error('addWebsocket.upgrade -> unknown pathname', pathname);
socket.destroy();
});
return wss;
}