mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-12 07:04:52 +09:00
Host not connected, host shutting down and instance not running are now UnavailableError (503) instead of ConflictError. The request error migration notes move to a new 2.1 guide, since this ships in 2.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
133 lines
4.0 KiB
TypeScript
133 lines
4.0 KiB
TypeScript
import type Controller from "./Controller.js";
|
|
import type BaseConnection from "./BaseConnection.js";
|
|
|
|
import * as lib from "@clusterio/lib";
|
|
import { logger } from "@clusterio/lib";
|
|
import HostConnection from "./HostConnection.js";
|
|
import ControlConnection from "./ControlConnection.js";
|
|
|
|
export default class ControllerRouter {
|
|
constructor(
|
|
public controller: Controller
|
|
) {}
|
|
|
|
/**
|
|
* @param origin - Source link of the message.
|
|
* @param message - Link the message originated from.
|
|
* @param entry - message entry if message is a request or an event.
|
|
* @param hasFallback - true if fallback handling is available.
|
|
* @returns true if the message was handled
|
|
*/
|
|
forwardMessage(
|
|
origin: ControlConnection | HostConnection,
|
|
message: lib.MessageRoutable,
|
|
entry: lib.RequestEntry | lib.EventEntry | undefined,
|
|
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, entry as lib.EventEntry);
|
|
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", "UnavailableError"), message.src
|
|
);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (nextHop) {
|
|
if (message.type === "request") {
|
|
nextHop.forwardRequest(message as lib.MessageRequest, origin);
|
|
} else {
|
|
nextHop.connector.forward(message);
|
|
}
|
|
} else {
|
|
this.warnUnrouted(message, msg);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
broadcastMessage(
|
|
origin: lib.Link,
|
|
message: lib.MessageRoutable,
|
|
entry: lib.EventEntry,
|
|
) {
|
|
let dst = message.dst;
|
|
if (message.type !== "event") {
|
|
this.warnUnrouted(message, `Unexpected broadcast of ${message.type}`);
|
|
return;
|
|
}
|
|
const plugin = entry.Event.plugin;
|
|
if (dst.id === lib.Address.host || dst.id === lib.Address.instance) {
|
|
for (let hostConnection of this.controller.wsServer.hostConnections.values()) {
|
|
if (hostConnection !== origin && (!plugin || hostConnection.plugins.has(plugin))) {
|
|
hostConnection.connector.forward(message);
|
|
}
|
|
}
|
|
} else if (dst.id === lib.Address.control) {
|
|
for (let controlConnection of this.controller.wsServer.controlConnections.values()) {
|
|
if (controlConnection !== origin) {
|
|
controlConnection.connector.forward(message);
|
|
}
|
|
}
|
|
} else {
|
|
this.warnUnrouted(message, `Unexpected broacdast target ${dst.id}`);
|
|
}
|
|
}
|
|
|
|
warnUnrouted(
|
|
message: lib.MessageRoutable,
|
|
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}.`);
|
|
}
|
|
}
|