Merge pull request #1057 from bbassie/fix/bounded-send-buffer

Limit unacknowledged messages buffered for a connection
This commit is contained in:
Cooldude2606 authored and GitHub committed 2026-10-08 17:25:57 +01:00
commit 8a170eb2ac
7 files changed
+124 -11

No files matched your search

+29 -1
View File
@@ -3,8 +3,9 @@ import events from "node:events";
import jwt from "jsonwebtoken";
import {
TestControlConnector, TestHostConnector, TestControl, url, controlToken,
TestControlConnector, TestHostConnector, TestControl, url, controlToken, getControl,
} from "./index.js";
import * as lib from "@clusterio/lib";
import { ConnectionClosed, ProtocolError, PolicyViolation, AuthenticationFailed } from "@clusterio/lib";
import { slowTest } from "../common.js";
@@ -92,6 +93,33 @@ describe("Integration of lib/link", function() {
await events.once(controlConnector, "connect");
});
it("should start a new session when the send buffer limit is exceeded", async function() {
await getControl().send(new lib.ControllerConfigSetFieldRequest("controller.max_send_buffer_size", "0.01"));
try {
await controlConnector.connect();
} finally {
await getControl().send(new lib.ControllerConfigSetFieldRequest("controller.max_send_buffer_size", "64"));
}
// Never acknowledge received messages
controlConnector._doHeartbeat = function() {
this._sendInternal(new lib.MessageHeartbeat());
};
let invalidated = false;
controlConnector.once("invalidate", () => { invalidated = true; });
const connected = events.once(controlConnector, "invalidate").then(
() => events.once(controlConnector, "connect")
);
for (let i = 0; i < 100; i++) {
if (invalidated) {
break;
}
await control.send(new lib.ControllerConfigGetRequest()).catch(() => {});
}
assert(invalidated, "session was not invalidated");
await connected;
});
it("should properly close connector if close is called during reconnect wait", async function() {
await controlConnector.connect();
controlConnector._socket.close(ConnectionClosed.Normal, "Test");
+36 -3
View File
@@ -13,10 +13,11 @@ describe("lib/link/connectors", function() {
describe("._dropSendBufferSeq()", function() {
beforeEach(function() {
testConnector._sendBuffer = [
{ seq: 1, type: "test", data: {} },
{ seq: 2, type: "test", data: {} },
{ seq: 4, type: "test", data: {} },
{ seq: 1, text: "a" },
{ seq: 2, text: "bb" },
{ seq: 4, text: "cccc" },
];
testConnector._sendBufferSize = 7;
});
it("should do nothing if passed null", function() {
testConnector._dropSendBufferSeq(null);
@@ -25,6 +26,7 @@ describe("lib/link/connectors", function() {
it("should drop up to sequence", function() {
testConnector._dropSendBufferSeq(2);
assert.equal(testConnector._sendBuffer.length, 1, "incorrect number of entries dropped");
assert.equal(testConnector._sendBufferSize, 4);
});
it("should ignore holes", function() {
testConnector._dropSendBufferSeq(3);
@@ -61,6 +63,37 @@ describe("lib/link/connectors", function() {
});
});
describe("send buffer limit", function() {
afterEach(function() {
testConnector.maxSendBufferSize = 0;
testConnector._closing = false;
});
it("should keep unacknowledged messages under the limit", function() {
testConnector._state = "connected";
testConnector._sendBuffer = [];
testConnector._sendBufferSize = 0;
testConnector._socket.closeCalled = false;
testConnector.maxSendBufferSize = 1000;
testConnector.send(new lib.MessageHeartbeat(1));
assert.equal(testConnector._sendBuffer.length, 1);
assert(!testConnector._socket.closeCalled, "Close was called on the socket");
});
it("should clear the buffer and close when the limit is exceeded", function() {
testConnector._state = "connected";
testConnector._sendBuffer = [];
testConnector._sendBufferSize = 0;
testConnector._socket.closeCalled = false;
testConnector._socket.sentMessages = [];
testConnector.maxSendBufferSize = 30;
testConnector.send(new lib.MessageHeartbeat(1));
testConnector.send(new lib.MessageHeartbeat(2));
assert.equal(testConnector._sendBuffer.length, 0);
assert.equal(testConnector._sendBufferSize, 0);
assert(testConnector._socket.closeCalled, "Close was not called on the socket");
assert.equal(testConnector._socket.sentMessages.length, 1);
});
});
describe(".close()", function() {
it("should call close", function() {
testConnector._socket.closeCalled = false;