Files
flipper/desktop/flipper-server-core/src/comms/SecureServerWebSocket.tsx
Andres Suarez 79023ee190 Update copyright headers from Facebook to Meta
Reviewed By: bhamodi

Differential Revision: D33331422

fbshipit-source-id: 016e8dcc0c0c7f1fc353a348b54fda0d5e2ddc01
2021-12-27 14:31:45 -08:00

134 lines
4.3 KiB
TypeScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import ServerWebSocket, {ConnectionCtx} from './ServerWebSocket';
import {SecureClientQuery} from './ServerAdapter';
import {ParsedUrlQuery} from 'querystring';
import {ClientDescription} from 'flipper-common';
import {
isWsResponseMessage,
parseSecureClientQuery,
assertNotNull,
} from './Utilities';
import WebSocketClientConnection from './WebSocketClientConnection';
import {serializeError} from 'serialize-error';
import {WSCloseCode} from '../utils/WSCloseCode';
export interface SecureConnectionCtx extends ConnectionCtx {
clientQuery?: SecureClientQuery;
clientConnection?: WebSocketClientConnection;
clientPromise?: Promise<ClientDescription>;
client?: ClientDescription;
}
/**
* WebSocket-based server.
* A secure connection has been established between the server and a client. Once a client
* has a valid certificate, it can use a secure connection with Flipper and start exchanging
* messages.
* https://fbflipper.com/docs/extending/new-clients
* https://fbflipper.com/docs/extending/establishing-a-connection
*/
class SecureServerWebSocket extends ServerWebSocket {
protected handleConnectionAttempt(ctx: SecureConnectionCtx): void {
const {clientQuery, ws} = ctx;
assertNotNull(clientQuery);
console.info(
`[conn] Secure websocket connection attempt: ${clientQuery.app} on ${clientQuery.device_id}. Medium ${clientQuery.medium}. CSR: ${clientQuery.csr_path}`,
);
this.listener.onSecureConnectionAttempt(clientQuery);
const clientConnection = new WebSocketClientConnection(ws);
// TODO: Could we just await it here? How much time could it be, potentially?
// DRI: @aigoncharov
const clientPromise: Promise<ClientDescription> = this.listener
.onConnectionCreated(clientQuery, clientConnection)
.then((client) => {
ctx.client = client;
return client;
})
.catch((e) => {
throw new Error(
`Failed to resolve client ${clientQuery.app} on ${
clientQuery.device_id
} medium ${clientQuery.medium}. Reason: ${JSON.stringify(
serializeError(e),
)}`,
);
});
ctx.clientConnection = clientConnection;
ctx.clientPromise = clientPromise;
}
protected async handleMessage(
ctx: SecureConnectionCtx,
parsedMessage: object,
rawMessage: string,
) {
const {clientQuery, clientConnection, clientPromise, client, ws} = ctx;
assertNotNull(clientQuery);
assertNotNull(clientConnection);
assertNotNull(clientPromise);
// We can recieve either "execute" messages from the client or "responses" to our messages
// https://fbflipper.com/docs/extending/new-clients#responding-to-messages
// Received a response message
if (isWsResponseMessage(parsedMessage)) {
const callbacks = clientConnection.matchPendingRequest(parsedMessage.id);
if (!callbacks) {
return;
}
callbacks.resolve({
...parsedMessage,
length: rawMessage.length,
});
return;
}
// Received an "execute" message
if (client) {
this.listener.onClientMessage(client.id, rawMessage);
} else {
// Client promise is not resolved yet
// So we schedule the execution for when it is resolved
clientPromise
.then((client) => {
this.listener.onClientMessage(client.id, rawMessage);
})
.catch((error) => {
// It is an async action, which might run after the socket is closed
if (ws.readyState === ws.OPEN) {
// See the reasoning in the error handler for a `connection` event in ServerWebSocket
ws.emit('error', error);
ws.close(WSCloseCode.InternalError);
}
});
}
}
/**
* Parse and extract a SecureClientQuery instance from a message. The ClientQuery
* data will be contained in the message url query string.
* @param message An incoming web socket message.
*/
protected parseClientQuery(
query: ParsedUrlQuery,
): SecureClientQuery | undefined {
return parseSecureClientQuery(query);
}
}
export default SecureServerWebSocket;