mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 15:14:52 +09:00
Re-apply debug dump ignore flag on control connection resume (#1035)
handleDebugDumpWsRequest sets clusterio_ignore_dump on the current socket so the WsServer send wrapper does not echo the dumper's own frames back into debugEvents. The flag was only set on the connector "connect" event, but WsServerConnector.continue() installs a fresh socket and emits "resume", not "connect", so a resumed session lost the flag. After a resume the next outgoing frame was echoed to debugEvents, ws_dumper sent a DebugWsMessageEvent, the wrapper echoed that too, and so on: infinite synchronous recursion ending in a RangeError: Invalid string length from JSON.stringify in link.ts sendEvent. That uncaught error made handleUnhandledErrors exit the controller. Repro: admin sends DebugDumpWsRequest, the control connection drops and resumes (e.g. a network blip), controller crashes on resume. Set the flag on "resume" as well as "connect". Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com>
This commit is contained in:
2 files changed
+40
-4
No files matched your search
@@ -1,12 +1,44 @@
|
||||
import assert from "node:assert/strict";
|
||||
import events from "node:events";
|
||||
import * as lib from "@clusterio/lib";
|
||||
import { ControlConnection, ControllerHooks } from "@clusterio/controller";
|
||||
import { Controller, ControlConnection, ControllerHooks } from "@clusterio/controller";
|
||||
import { MockConnector, MockLogger } from "../mock.js";
|
||||
|
||||
const addr = lib.Address.fromShorthand;
|
||||
|
||||
describe("controller/src/ControlConnection", function() {
|
||||
describe(".handleDebugDumpWsRequest()", function() {
|
||||
function makeConnection() {
|
||||
const controllerConfig = new lib.ControllerConfig("controller");
|
||||
const connector = new lib.VirtualConnector(
|
||||
lib.Address.fromShorthand("controller"),
|
||||
lib.Address.fromShorthand({ controlId: 1 }),
|
||||
);
|
||||
connector._socket = {};
|
||||
const controller = new Controller(lib.logger, [], controllerConfig);
|
||||
const user = controller.users.getOrCreateUser("test");
|
||||
return new ControlConnection({ version: "2.0.0" }, connector, controller, user, 1);
|
||||
}
|
||||
|
||||
it("re-applies clusterio_ignore_dump to the socket on resume", async function() {
|
||||
const connection = makeConnection();
|
||||
await connection.handleDebugDumpWsRequest(new lib.DebugDumpWsRequest());
|
||||
// A resume installs a fresh socket without the flag.
|
||||
const resumedSocket = {};
|
||||
connection.connector._socket = resumedSocket;
|
||||
connection.connector.emit("resume");
|
||||
assert.equal(resumedSocket.clusterio_ignore_dump, true);
|
||||
});
|
||||
|
||||
it("leaves the flag off on resume without a dumper", function() {
|
||||
const connection = makeConnection();
|
||||
const resumedSocket = {};
|
||||
connection.connector._socket = resumedSocket;
|
||||
connection.connector.emit("resume");
|
||||
assert.equal(resumedSocket.clusterio_ignore_dump, false);
|
||||
});
|
||||
});
|
||||
|
||||
describe(".handleControllerRestartRequest()", function() {
|
||||
let mockController;
|
||||
|
||||
|
||||
Reference in new issue
Block a user