mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
153 lines
4.8 KiB
TypeScript
153 lines
4.8 KiB
TypeScript
// Copyright 2024, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import { handleWaveEvent } from "@/app/store/wps";
|
|
import * as util from "@/util/util";
|
|
import debug from "debug";
|
|
|
|
const dlog = debug("wave:router");
|
|
|
|
const SysRouteName = "sys";
|
|
|
|
type RouteInfo = {
|
|
rpcId: string;
|
|
sourceRouteId: string;
|
|
destRouteId: string;
|
|
};
|
|
|
|
function makeFeBlockRouteId(feBlockId: string): string {
|
|
return `feblock:${feBlockId}`;
|
|
}
|
|
|
|
function makeTabRouteId(tabId: string): string {
|
|
return `tab:${tabId}`;
|
|
}
|
|
|
|
class WshRouter {
|
|
routeMap: Map<string, AbstractWshClient>; // routeid -> client
|
|
upstreamClient: AbstractWshClient;
|
|
rpcMap: Map<string, RouteInfo>; // rpcid -> routeinfo
|
|
|
|
constructor(upstreamClient: AbstractWshClient) {
|
|
this.routeMap = new Map();
|
|
this.rpcMap = new Map();
|
|
if (upstreamClient == null) {
|
|
throw new Error("upstream client cannot be null");
|
|
}
|
|
this.upstreamClient = upstreamClient;
|
|
}
|
|
|
|
reannounceRoutes() {
|
|
for (const [routeId, client] of this.routeMap) {
|
|
const announceMsg: RpcMessage = {
|
|
command: "routeannounce",
|
|
data: routeId,
|
|
source: routeId,
|
|
};
|
|
this.upstreamClient.recvRpcMessage(announceMsg);
|
|
}
|
|
}
|
|
|
|
// returns true if the message was sent
|
|
_sendRoutedMessage(msg: RpcMessage, destRouteId: string) {
|
|
const client = this.routeMap.get(destRouteId);
|
|
if (client) {
|
|
client.recvRpcMessage(msg);
|
|
return;
|
|
}
|
|
// there should always an upstream client
|
|
if (!this.upstreamClient) {
|
|
throw new Error(`no upstream client for message: ${msg}`);
|
|
}
|
|
this.upstreamClient?.recvRpcMessage(msg);
|
|
}
|
|
|
|
_registerRouteInfo(reqid: string, sourceRouteId: string, destRouteId: string) {
|
|
dlog("registering route info", reqid, sourceRouteId, destRouteId);
|
|
if (util.isBlank(reqid)) {
|
|
return;
|
|
}
|
|
const routeInfo: RouteInfo = {
|
|
rpcId: reqid,
|
|
sourceRouteId: sourceRouteId,
|
|
destRouteId: destRouteId,
|
|
};
|
|
this.rpcMap.set(reqid, routeInfo);
|
|
}
|
|
|
|
recvRpcMessage(msg: RpcMessage) {
|
|
dlog("router received message", msg);
|
|
// we are a terminal node by definition, so we don't need to process with announce/unannounce messages
|
|
if (msg.command == "routeannounce" || msg.command == "routeunannounce") {
|
|
return;
|
|
}
|
|
// handle events
|
|
if (msg.command == "eventrecv") {
|
|
handleWaveEvent(msg.data);
|
|
return;
|
|
}
|
|
if (!util.isBlank(msg.command)) {
|
|
// send + register routeinfo
|
|
if (!util.isBlank(msg.reqid)) {
|
|
this._registerRouteInfo(msg.reqid, msg.source, msg.route);
|
|
}
|
|
this._sendRoutedMessage(msg, msg.route);
|
|
return;
|
|
}
|
|
if (!util.isBlank(msg.reqid)) {
|
|
const routeInfo = this.rpcMap.get(msg.reqid);
|
|
if (!routeInfo) {
|
|
// no route info, discard
|
|
dlog("no route info for reqid, discarding", msg);
|
|
return;
|
|
}
|
|
this._sendRoutedMessage(msg, routeInfo.destRouteId);
|
|
return;
|
|
}
|
|
if (!util.isBlank(msg.resid)) {
|
|
const routeInfo = this.rpcMap.get(msg.resid);
|
|
if (!routeInfo) {
|
|
// no route info, discard
|
|
dlog("no route info for resid, discarding", msg);
|
|
return;
|
|
}
|
|
this._sendRoutedMessage(msg, routeInfo.sourceRouteId);
|
|
if (!msg.cont) {
|
|
dlog("deleting route info", msg.resid);
|
|
this.rpcMap.delete(msg.resid);
|
|
}
|
|
return;
|
|
}
|
|
dlog("bad rpc message recevied by router, no command, reqid, or resid (discarding)", msg);
|
|
}
|
|
|
|
registerRoute(routeId: string, client: AbstractWshClient) {
|
|
if (routeId == SysRouteName) {
|
|
throw new Error(`Cannot register route with reserved name (${routeId})`);
|
|
}
|
|
dlog("registering route: ", routeId);
|
|
// announce
|
|
const announceMsg: RpcMessage = {
|
|
command: "routeannounce",
|
|
data: routeId,
|
|
source: routeId,
|
|
};
|
|
this.upstreamClient.recvRpcMessage(announceMsg);
|
|
this.routeMap.set(routeId, client);
|
|
}
|
|
|
|
unregisterRoute(routeId: string) {
|
|
dlog("unregister route: ", routeId);
|
|
// unannounce
|
|
const unannounceMsg: RpcMessage = {
|
|
command: "routeunannounce",
|
|
data: routeId,
|
|
source: routeId,
|
|
};
|
|
this.upstreamClient?.recvRpcMessage(unannounceMsg);
|
|
this.routeMap.delete(routeId);
|
|
}
|
|
}
|
|
|
|
export { makeFeBlockRouteId, makeTabRouteId, WshRouter };
|