Files
factorio-clusterio/packages/controller/src/ControllerRouter.ts
T

131 lines
3.8 KiB
TypeScript

import type Controller from "./Controller";
import type BaseConnection from "./BaseConnection";
import * as lib from "@clusterio/lib";
import HostConnection from "./HostConnection";
import ControlConnection from "./ControlConnection";
const { logger } = lib;
export default class ControllerRouter {
pendingRequests: Map<any, any> = new Map();
constructor(
public controller: Controller
) {}
/**
* @param origin - Source link of the message.
* @param message - Link the message originated from.
* @param hasFallback - true if fallback handling is available.
* @returns true if the message was handled
*/
forwardMessage(
origin: ControlConnection | HostConnection,
message: lib.MessageSrcDst,
hasFallback: boolean
): boolean {
if (!["request", "response", "responseError", "event"].includes(message.type)) {
throw new Error(`Message type ${message.type} can't be forwarded`);
}
let dst: lib.Address = message.dst;
let nextHop: ControlConnection | HostConnection | undefined;
let msg: string = "";
if (dst.type === lib.Address.broadcast) {
this.broadcastMessage(origin, message);
return true;
} else if (dst.type === lib.Address.host) {
nextHop = this.controller.wsServer.hostConnections.get(dst.id);
if (!nextHop) {
msg = `Host ${dst.id} is offline`;
}
} else if (dst.type === lib.Address.instance) {
let instance = this.controller.instances!.get(dst.id);
if (!instance) {
msg = `Instance ${dst.id} does not exist`;
} else {
let assignedHost = instance.config.get("instance.assigned_host");
if (assignedHost === null) {
msg = `Instance ${dst.id} is not assigned a host`;
} else {
nextHop = this.controller.wsServer.hostConnections.get(assignedHost);
if (!nextHop) {
msg = `Assigned host for instance ${dst.id} is offline`;
}
}
}
} else if (dst.type === lib.Address.control) {
nextHop = this.controller.wsServer.controlConnections.get(dst.id);
if (!nextHop) {
msg = "Control connection does not exist";
}
} else if (dst.type === lib.Address.controller) {
msg = `Unexpected message forwarded to ${dst}`;
}
if (nextHop === origin) {
msg = `Message would return back to sender ${origin.connector.dst}.`;
nextHop = undefined;
}
if (message.type === "request") {
if (!nextHop) {
if (hasFallback) {
return false;
}
origin.connector.sendResponseError(
new lib.ResponseError(msg || "Unroutable destination"), message.src
);
return true;
}
}
if (nextHop) {
if (message.type === "request") {
nextHop.forwardRequest(message as lib.MessageRequest, origin);
} else {
nextHop.connector.send(message);
}
} else {
this.warnUnrouted(message, msg);
}
return true;
}
broadcastMessage(
origin: lib.Link,
message: lib.MessageSrcDst,
) {
let dst = message.dst;
if (message.type !== "event") {
this.warnUnrouted(message, `Unexpected broadcast of ${message.type}`);
} else if (dst.id === lib.Address.host || dst.id === lib.Address.instance) {
for (let hostConnection of this.controller.wsServer.hostConnections.values()) {
if (hostConnection !== origin) {
hostConnection.connector.send(message);
}
}
} else if (dst.id === lib.Address.control) {
for (let controlConnection of this.controller.wsServer.controlConnections.values()) {
if (controlConnection !== origin) {
controlConnection.connector.send(message);
}
}
} else {
this.warnUnrouted(message, `Unexpected broacdast target ${dst.id}`);
}
}
warnUnrouted(
message: lib.MessageSrcDst,
msg: string
) {
let dst = message.dst;
let baseMsg = `No destination for ${message.constructor.name} routed from ${message.src} to ${dst}`;
logger.warn(msg ? `${baseMsg}: ${msg}.` : `${baseMsg}.`);
}
}
module.exports = ControllerRouter;