ServerAdapter: ServerWebSocketBase

Summary: Make it more clear about that this is. `ServerAdapter` was VERY generic.

Reviewed By: passy

Differential Revision: D47185076

fbshipit-source-id: 7d9b30f423398004bedc92ad22bc0217d1c8453f
This commit is contained in:
Lorenzo Blasa
2023-07-03 05:01:17 -07:00
committed by Facebook GitHub Bot
parent 71751855df
commit 48495c906e
11 changed files with 45 additions and 34 deletions

View File

@@ -16,7 +16,7 @@ import {assertNotNull, parseClientQuery} from './Utilities';
import SecureServerWebSocket, { import SecureServerWebSocket, {
SecureConnectionCtx, SecureConnectionCtx,
} from './SecureServerWebSocket'; } from './SecureServerWebSocket';
import {SecureClientQuery} from './ServerAdapter'; import {SecureClientQuery} from './ServerWebSocketBase';
import {ClientDescription, DeviceOS} from 'flipper-common'; import {ClientDescription, DeviceOS} from 'flipper-common';
import {URL} from 'url'; import {URL} from 'url';
import {isFBBuild} from '../fb-stubs/constants'; import {isFBBuild} from '../fb-stubs/constants';

View File

@@ -8,7 +8,7 @@
*/ */
import ServerWebSocket, {ConnectionCtx} from './ServerWebSocket'; import ServerWebSocket, {ConnectionCtx} from './ServerWebSocket';
import {SecureClientQuery} from './ServerAdapter'; import {SecureClientQuery} from './ServerWebSocketBase';
import {ParsedUrlQuery} from 'querystring'; import {ParsedUrlQuery} from 'querystring';
import {ClientDescription} from 'flipper-common'; import {ClientDescription} from 'flipper-common';
import { import {

View File

@@ -27,10 +27,10 @@ import {
assertNotNull, assertNotNull,
cloneClientQuerySafeForLogging, cloneClientQuerySafeForLogging,
} from './Utilities'; } from './Utilities';
import ServerAdapter, { import ServerWebSocketBase, {
SecureClientQuery, SecureClientQuery,
ServerEventsListener, ServerEventsListener,
} from './ServerAdapter'; } from './ServerWebSocketBase';
import { import {
createBrowserServer, createBrowserServer,
createServer, createServer,
@@ -79,11 +79,11 @@ export class ServerController
connections: Map<string, ClientInfo> = new Map(); connections: Map<string, ClientInfo> = new Map();
timestamps: Map<string, ClientTimestampTracker> = new Map(); timestamps: Map<string, ClientTimestampTracker> = new Map();
secureServer: ServerAdapter | null = null; secureServer: ServerWebSocketBase | null = null;
insecureServer: ServerAdapter | null = null; insecureServer: ServerWebSocketBase | null = null;
altSecureServer: ServerAdapter | null = null; altSecureServer: ServerWebSocketBase | null = null;
altInsecureServer: ServerAdapter | null = null; altInsecureServer: ServerWebSocketBase | null = null;
browserServer: ServerAdapter | null = null; browserServer: ServerWebSocketBase | null = null;
connectionTracker: ConnectionTracker; connectionTracker: ConnectionTracker;

View File

@@ -8,7 +8,7 @@
*/ */
import {SecureServerConfig} from '../utils/certificateUtils'; import {SecureServerConfig} from '../utils/certificateUtils';
import ServerAdapter, {ServerEventsListener} from './ServerAdapter'; import ServerWebSocketBase, {ServerEventsListener} from './ServerWebSocketBase';
import ServerRSocket from './ServerRSocket'; import ServerRSocket from './ServerRSocket';
import SecureServerWebSocket from './SecureServerWebSocket'; import SecureServerWebSocket from './SecureServerWebSocket';
import BrowserServerWebSocket from './BrowserServerWebSocket'; import BrowserServerWebSocket from './BrowserServerWebSocket';
@@ -31,8 +31,8 @@ export async function createServer(
listener: ServerEventsListener, listener: ServerEventsListener,
sslConfig?: SecureServerConfig, sslConfig?: SecureServerConfig,
transportType: TransportType = TransportType.RSocket, transportType: TransportType = TransportType.RSocket,
): Promise<ServerAdapter> { ): Promise<ServerWebSocketBase> {
let server: ServerAdapter; let server: ServerWebSocketBase;
if (transportType === TransportType.RSocket) { if (transportType === TransportType.RSocket) {
server = new ServerRSocket(listener); server = new ServerRSocket(listener);
} else if (sslConfig) { } else if (sslConfig) {
@@ -56,7 +56,7 @@ export async function createServer(
export async function createBrowserServer( export async function createBrowserServer(
port: number, port: number,
listener: ServerEventsListener, listener: ServerEventsListener,
): Promise<ServerAdapter> { ): Promise<ServerWebSocketBase> {
const server = new BrowserServerWebSocket(listener); const server = new BrowserServerWebSocket(listener);
await server.start(port); await server.start(port);
return server; return server;

View File

@@ -8,10 +8,10 @@
*/ */
import {SecureServerConfig} from '../utils/certificateUtils'; import {SecureServerConfig} from '../utils/certificateUtils';
import ServerAdapter, { import ServerWebSocketBase, {
SecureClientQuery, SecureClientQuery,
ServerEventsListener, ServerEventsListener,
} from './ServerAdapter'; } from './ServerWebSocketBase';
import tls from 'tls'; import tls from 'tls';
import net, {AddressInfo, Socket} from 'net'; import net, {AddressInfo, Socket} from 'net';
import {RSocketServer} from 'rsocket-core'; import {RSocketServer} from 'rsocket-core';
@@ -34,7 +34,7 @@ import {transformCertificateExchangeMediumToType} from './Utilities';
* RSocket based server. RSocket uses its own protocol for communication between * RSocket based server. RSocket uses its own protocol for communication between
* client and server. * client and server.
*/ */
class ServerRSocket extends ServerAdapter { class ServerRSocket extends ServerWebSocketBase {
rawServer_: RSocketServer<any, any> | null | undefined; rawServer_: RSocketServer<any, any> | null | undefined;
constructor(listener: ServerEventsListener) { constructor(listener: ServerEventsListener) {
super(listener); super(listener);

View File

@@ -8,7 +8,7 @@
*/ */
import {IncomingMessage} from 'http'; import {IncomingMessage} from 'http';
import ServerAdapter from './ServerAdapter'; import ServerWebSocketBase from './ServerWebSocketBase';
import WebSocket, { import WebSocket, {
AddressInfo, AddressInfo,
Server as WSServer, Server as WSServer,
@@ -35,15 +35,15 @@ export interface ConnectionCtx {
request: IncomingMessage; request: IncomingMessage;
} }
// based on https://github.com/websockets/ws/blob/master/lib/websocket-server.js#L40, // Based on https://github.com/websockets/ws/blob/master/lib/websocket-server.js#L40,
// exposed to share with socket.io defaults // exposed to share with socket.io defaults.
export const WEBSOCKET_MAX_MESSAGE_SIZE = 100 * 1024 * 1024; export const WEBSOCKET_MAX_MESSAGE_SIZE = 100 * 1024 * 1024;
/** /**
* It serves as a base class for WebSocket based servers. It delegates the 'connection' * It serves as a base class for WebSocket based servers. It delegates the 'connection'
* event to subclasses as a customisation point. * event to subclasses as a customisation point.
*/ */
class ServerWebSocket extends ServerAdapter { class ServerWebSocket extends ServerWebSocketBase {
protected wsServer?: WSServer; protected wsServer?: WSServer;
private httpServer?: Server; private httpServer?: Server;
@@ -77,12 +77,14 @@ class ServerWebSocket extends ServerAdapter {
'server', 'server',
); );
// Unsubscribe connection error listener. We'll attach a permanent error listener later // Unsubscribe connection error listener.
// We'll attach a permanent error listener later.
wsServer.off('error', onConnectionError); wsServer.off('error', onConnectionError);
this.listener.onListening(port); this.listener.onListening(port);
this.wsServer = wsServer; this.wsServer = wsServer;
this.httpServer = server; this.httpServer = server;
resolve((server.address() as AddressInfo).port); resolve((server.address() as AddressInfo).port);
}); });
}); });
@@ -99,7 +101,7 @@ class ServerWebSocket extends ServerAdapter {
}); });
try { try {
this.onConnection(ws, request); // insecure connection, with medium. this.onConnection(ws, request);
} catch (error) { } catch (error) {
// TODO: Investigate if we need to close the socket in the `error` listener // TODO: Investigate if we need to close the socket in the `error` listener
// DRI: @aigoncharov // DRI: @aigoncharov
@@ -166,7 +168,8 @@ class ServerWebSocket extends ServerAdapter {
*/ */
onConnection(ws: WebSocket, request: IncomingMessage): void { onConnection(ws: WebSocket, request: IncomingMessage): void {
const ctx: ConnectionCtx = {ws, request}; const ctx: ConnectionCtx = {ws, request};
this.handleClientQuery(ctx);
this.extractClientQuery(ctx);
this.handleConnectionAttempt(ctx); this.handleConnectionAttempt(ctx);
ws.on('message', async (message: WebSocket.RawData) => { ws.on('message', async (message: WebSocket.RawData) => {
@@ -180,15 +183,24 @@ class ServerWebSocket extends ServerAdapter {
// all other plugins might still be working correctly. So let's just report it. // all other plugins might still be working correctly. So let's just report it.
// This avoids ping-ponging connections if an individual plugin sends garbage (e.g. T129428800) // This avoids ping-ponging connections if an individual plugin sends garbage (e.g. T129428800)
// or throws an error when handling messages // or throws an error when handling messages
console.error('Failed to handle message', messageString, error); console.error('[conn] Failed to handle message', messageString, error);
} }
}); });
} }
protected handleClientQuery(ctx: ConnectionCtx): void { /**
* Extract and create a ClientQuery from the request URL. This method will throw if:
* @param ctx The connection context.
* @returns It doesn't return anything, if the client query
* is extracted, this one is set into the connection context.
*/
protected extractClientQuery(ctx: ConnectionCtx): void {
const {request} = ctx; const {request} = ctx;
if (!request.url) {
return;
}
const query = querystring.decode(request.url!.split('?')[1]); const query = querystring.decode(request.url.split('?')[1]);
const clientQuery = this.parseClientQuery(query); const clientQuery = this.parseClientQuery(query);
if (!clientQuery) { if (!clientQuery) {
@@ -218,7 +230,6 @@ class ServerWebSocket extends ServerAdapter {
const parsedMessage = parseMessageToJson(message); const parsedMessage = parseMessageToJson(message);
if (!parsedMessage) { if (!parsedMessage) {
console.error('[conn] Failed to parse message', message); console.error('[conn] Failed to parse message', message);
// TODO: Create custom DeserializationError
throw new Error(`[conn] Failed to parse message`); throw new Error(`[conn] Failed to parse message`);
} }
return parsedMessage; return parsedMessage;
@@ -227,7 +238,7 @@ class ServerWebSocket extends ServerAdapter {
protected async handleMessage( protected async handleMessage(
ctx: ConnectionCtx, ctx: ConnectionCtx,
parsedMessage: object, parsedMessage: object,
// Not used in this method, but left as a reference for overriding classes // Not used in this method, but left as a reference for overriding classes.
_rawMessage: string, _rawMessage: string,
) { ) {
const {clientQuery, ws} = ctx; const {clientQuery, ws} = ctx;

View File

@@ -111,7 +111,7 @@ export interface ServerEventsListener {
* Defines the base class to be used by any server implementation e.g. * Defines the base class to be used by any server implementation e.g.
* RSocket, WebSocket, etc. * RSocket, WebSocket, etc.
*/ */
abstract class ServerAdapter { abstract class ServerWebSocketBase {
constructor(protected listener: ServerEventsListener) {} constructor(protected listener: ServerEventsListener) {}
/** /**
@@ -185,4 +185,4 @@ abstract class ServerAdapter {
} }
} }
export default ServerAdapter; export default ServerWebSocketBase;

View File

@@ -14,7 +14,7 @@ import {
ResponseMessage, ResponseMessage,
} from 'flipper-common'; } from 'flipper-common';
import {ParsedUrlQuery} from 'querystring'; import {ParsedUrlQuery} from 'querystring';
import {SecureClientQuery} from './ServerAdapter'; import {SecureClientQuery} from './ServerWebSocketBase';
/** /**
* Transforms the certificate exchange medium type as number to the * Transforms the certificate exchange medium type as number to the

View File

@@ -18,7 +18,7 @@ import WebSocket from 'ws';
import {BrowserClientConnection} from '../BrowserClientConnection'; import {BrowserClientConnection} from '../BrowserClientConnection';
import {getFlipperServerConfig} from '../../FlipperServerConfig'; import {getFlipperServerConfig} from '../../FlipperServerConfig';
import BrowserServerWebSocket from '../BrowserServerWebSocket'; import BrowserServerWebSocket from '../BrowserServerWebSocket';
import {SecureClientQuery} from '../ServerAdapter'; import {SecureClientQuery} from '../ServerWebSocketBase';
import {createMockSEListener, WSMessageAccumulator} from './utils'; import {createMockSEListener, WSMessageAccumulator} from './utils';
jest.mock('../../FlipperServerConfig'); jest.mock('../../FlipperServerConfig');

View File

@@ -17,7 +17,7 @@ import {toBase64} from 'js-base64';
import WebSocket from 'ws'; import WebSocket from 'ws';
import SecureServerWebSocket from '../SecureServerWebSocket'; import SecureServerWebSocket from '../SecureServerWebSocket';
import {SecureClientQuery} from '../ServerAdapter'; import {SecureClientQuery} from '../ServerWebSocketBase';
import {transformCertificateExchangeMediumToType} from '../Utilities'; import {transformCertificateExchangeMediumToType} from '../Utilities';
import WebSocketClientConnection from '../WebSocketClientConnection'; import WebSocketClientConnection from '../WebSocketClientConnection';
import {createMockSEListener, WSMessageAccumulator} from './utils'; import {createMockSEListener, WSMessageAccumulator} from './utils';

View File

@@ -8,7 +8,7 @@
*/ */
import {ClientQuery, ClientDescription} from 'flipper-common'; import {ClientQuery, ClientDescription} from 'flipper-common';
import {ServerEventsListener} from '../ServerAdapter'; import {ServerEventsListener} from '../ServerWebSocketBase';
export class WSMessageAccumulator { export class WSMessageAccumulator {
private messages: unknown[] = []; private messages: unknown[] = [];