Merge pull request #1064 from ILLISIS/fix-ws-error-before-handshake

Handle WebSocket errors before the handshake completes
This commit is contained in:
Cooldude2606 authored and GitHub committed 2026-10-12 22:03:08 +01:00
commit 48e8c53776
3 files changed
+26 -5

No files matched your search

+7
View File
@@ -116,6 +116,13 @@ export default class WsServer {
handleConnection(socket: lib.WebSocketClusterio, req: IncomingMessage) {
logger.verbose(`WsServer | new connection from ${this.remoteAddr(req)}`);
// ws emits errors like invalid frames on the socket, and an error with
// no listener crashes the controller. The connector adds its own
// listener, but only once the handshake has completed.
socket.on("error", (err: Error) => {
logger.verbose(`WsServer | error on connection from ${this.remoteAddr(req)}: ${err.message}`);
});
// Track statistics
wsConnectionsCounter.inc();
socket.on("message", (message: WebSocket.RawData) => {
@@ -144,11 +144,6 @@ export default class WsServerConnector extends lib.WebSocketBaseConnector {
}
});
this._socket.on("error", (err: any) => {
// It's assumed that close is always called by ws
logger.verbose("Connector | Error:", err);
});
this._socket.on("open", () => {
logger.verbose("Connector | Open");
});
+19
View File
@@ -1,4 +1,5 @@
import assert from "node:assert/strict";
import events from "node:events";
import { Controller, WsServer } from "@clusterio/controller";
import { ControllerConfig } from "@clusterio/lib";
@@ -68,5 +69,23 @@ describe("controller/src/WsServer", function() {
assert.equal(ipv6, "2000::1");
});
});
describe(".handleConnection()", function() {
it("should handle socket errors before the handshake completes", function() {
// A ws socket emits "error" for e.g. a close frame with the reserved code 1006
const socket = new events.EventEmitter();
socket.send = () => {};
socket.close = () => {};
socket.terminate = () => {};
const req = { socket: { remoteAddress: "1.2.3.4", remoteFamily: "IPv4" }, headers: {} };
wsServer.handleConnection(socket, req);
try {
assert.doesNotThrow(() => {
socket.emit("error", new RangeError("Invalid WebSocket frame: invalid status code 1006"));
});
} finally {
socket.emit("message", "not json"); // ends the handshake and its timeout
}
});
});
});
});