cvmts: rate limit the 'sync' instruction
the original Guacamole code did this too I'm pretty sure, and it was even more aggressive about it.
This commit is contained in:
@@ -30,6 +30,9 @@ const kCVMTSAssetsRoot = path.resolve(__dirname, '../../assets');
|
|||||||
|
|
||||||
const kRestartTimeout = 5000;
|
const kRestartTimeout = 5000;
|
||||||
|
|
||||||
|
// Rate in milliseconds that the 'sync' instruction should be sent to users.
|
||||||
|
const kSyncRateMs = 500;
|
||||||
|
|
||||||
type ChatHistory = {
|
type ChatHistory = {
|
||||||
user: string;
|
user: string;
|
||||||
msg: string;
|
msg: string;
|
||||||
@@ -85,6 +88,8 @@ export default class CollabVMServer {
|
|||||||
private ModPerms: number;
|
private ModPerms: number;
|
||||||
private VM: VM;
|
private VM: VM;
|
||||||
|
|
||||||
|
private lastSync: number = Date.now();
|
||||||
|
|
||||||
// Authentication manager
|
// Authentication manager
|
||||||
private auth: AuthManager | null;
|
private auth: AuthManager | null;
|
||||||
|
|
||||||
@@ -94,9 +99,6 @@ export default class CollabVMServer {
|
|||||||
// Ban manager
|
// Ban manager
|
||||||
private banmgr: BanManager;
|
private banmgr: BanManager;
|
||||||
|
|
||||||
// queue of rects, reset every frame
|
|
||||||
private rectQueue: Rect[] = [];
|
|
||||||
|
|
||||||
private logger = pino({ name: 'CVMTS.Server' });
|
private logger = pino({ name: 'CVMTS.Server' });
|
||||||
|
|
||||||
constructor(config: IConfig, vm: VM, banmgr: BanManager, auth: AuthManager | null, geoipReader: ReaderModel | null) {
|
constructor(config: IConfig, vm: VM, banmgr: BanManager, auth: AuthManager | null, geoipReader: ReaderModel | null) {
|
||||||
@@ -139,7 +141,6 @@ export default class CollabVMServer {
|
|||||||
// add events
|
// add events
|
||||||
self.VM.GetDisplay()?.on('resize', (size: Size) => self.OnDisplayResized(size));
|
self.VM.GetDisplay()?.on('resize', (size: Size) => self.OnDisplayResized(size));
|
||||||
self.VM.GetDisplay()?.on('rect', (rect: Rect) => self.OnDisplayRectangle(rect));
|
self.VM.GetDisplay()?.on('rect', (rect: Rect) => self.OnDisplayRectangle(rect));
|
||||||
self.VM.GetDisplay()?.on('frame', () => self.OnDisplayFrame());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,20 +850,7 @@ export default class CollabVMServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private OnDisplayRectangle(rect: Rect) {
|
private async OnDisplayRectangle(rect: Rect) {
|
||||||
this.rectQueue.push(rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OnDisplayResized(size: Size) {
|
|
||||||
this.clients
|
|
||||||
.filter((c) => c.connectedToNode || c.viewMode == 1)
|
|
||||||
.forEach((c) => {
|
|
||||||
if (this.screenHidden && c.rank == Rank.Unregistered) return;
|
|
||||||
c.sendMsg(cvm.guacEncode('size', '0', size.width.toString(), size.height.toString()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async OnDisplayFrame() {
|
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
let doRect = async (rect: Rect) => {
|
let doRect = async (rect: Rect) => {
|
||||||
@@ -887,20 +875,35 @@ export default class CollabVMServer {
|
|||||||
c.socket.sendBinary(encodedbin);
|
c.socket.sendBinary(encodedbin);
|
||||||
} else {
|
} else {
|
||||||
c.sendMsg(cvm.guacEncode('png', '0', '0', rect.x.toString(), rect.y.toString(), encodedb64));
|
c.sendMsg(cvm.guacEncode('png', '0', '0', rect.x.toString(), rect.y.toString(), encodedb64));
|
||||||
c.sendMsg(cvm.guacEncode('sync', Date.now().toString()));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let promises: Promise<void>[] = [];
|
await Promise.all([doRect(rect)]);
|
||||||
|
|
||||||
for (let rect of self.rectQueue) promises.push(doRect(rect));
|
// Send a sync if the time since the last sync has hit or went above the rate
|
||||||
|
// to send one
|
||||||
|
let syncDeltaMs = Date.now() - self.lastSync;
|
||||||
|
if (syncDeltaMs >= kSyncRateMs) {
|
||||||
|
let syncNow = Date.now();
|
||||||
|
self.clients
|
||||||
|
.filter((c) => c.connectedToNode || c.viewMode == 1)
|
||||||
|
.forEach((c) => {
|
||||||
|
if (self.screenHidden && c.rank == Rank.Unregistered) return;
|
||||||
|
c.sendMsg(cvm.guacEncode('sync', syncNow.toString()));
|
||||||
|
});
|
||||||
|
|
||||||
// javascript is a very solidly designed language with no holes
|
self.lastSync = syncNow;
|
||||||
// or usability traps inside of it whatsoever
|
}
|
||||||
this.rectQueue.length = 0;
|
}
|
||||||
|
|
||||||
await Promise.all(promises);
|
private OnDisplayResized(size: Size) {
|
||||||
|
this.clients
|
||||||
|
.filter((c) => c.connectedToNode || c.viewMode == 1)
|
||||||
|
.forEach((c) => {
|
||||||
|
if (this.screenHidden && c.rank == Rank.Unregistered) return;
|
||||||
|
c.sendMsg(cvm.guacEncode('size', '0', size.width.toString(), size.height.toString()));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async SendFullScreenWithSize(client: User) {
|
private async SendFullScreenWithSize(client: User) {
|
||||||
|
|||||||
Reference in New Issue
Block a user