🪵
This commit is contained in:
@@ -1,22 +1,30 @@
|
||||
import { WebSocket } from 'ws';
|
||||
import { NetworkClient } from '../NetworkClient.js';
|
||||
import EventEmitter from 'events';
|
||||
import pino from 'pino';
|
||||
import { pino, type Logger } from 'pino';
|
||||
|
||||
export default class WSClient extends EventEmitter implements NetworkClient {
|
||||
socket: WebSocket;
|
||||
ip: string;
|
||||
uuid: string;
|
||||
enforceTextOnly = true
|
||||
private logger = pino({ name: "CVMTS.WebsocketClient" });
|
||||
private logger: Logger;
|
||||
|
||||
constructor(ws: WebSocket, ip: string) {
|
||||
constructor(ws: WebSocket, ip: string, uuid: string) {
|
||||
super();
|
||||
this.socket = ws;
|
||||
this.ip = ip;
|
||||
this.uuid = uuid;
|
||||
this.logger = pino().child({
|
||||
name: "CVMTS.WebsocketClient",
|
||||
"uuid/websocket/client": uuid,
|
||||
src_ip: ip,
|
||||
});
|
||||
this.socket.on('message', (buf: Buffer, isBinary: boolean) => {
|
||||
// Close the user's connection if they send a binary message
|
||||
// when we are not expecting them yet.
|
||||
if (isBinary && this.enforceTextOnly) {
|
||||
this.logger.info({event: "received unexpected binary message"});
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
@@ -25,10 +33,11 @@ export default class WSClient extends EventEmitter implements NetworkClient {
|
||||
});
|
||||
|
||||
this.socket.on('error', (err: Error) => {
|
||||
this.logger.error(err, 'WebSocket recv error');
|
||||
this.logger.error({event: "websocket recv error", msg: err});
|
||||
})
|
||||
|
||||
this.socket.on('close', () => {
|
||||
this.logger.info({event: "disconnecting client"});
|
||||
this.emit('disconnect');
|
||||
});
|
||||
}
|
||||
@@ -42,12 +51,13 @@ export default class WSClient extends EventEmitter implements NetworkClient {
|
||||
}
|
||||
|
||||
send(msg: string): Promise<void> {
|
||||
this.logger.trace({event: "outgoing message", msg});
|
||||
return new Promise((res, rej) => {
|
||||
if (!this.isOpen()) return res();
|
||||
|
||||
this.socket.send(msg, (err) => {
|
||||
if (err) {
|
||||
this.logger.error(err, 'WebSocket send error');
|
||||
this.logger.error({event: "websocket send error", msg: err});
|
||||
this.close();
|
||||
res();
|
||||
return;
|
||||
@@ -58,12 +68,13 @@ export default class WSClient extends EventEmitter implements NetworkClient {
|
||||
}
|
||||
|
||||
sendBinary(msg: Uint8Array): Promise<void> {
|
||||
this.logger.trace({event: "outgoing message", msg});
|
||||
return new Promise((res, rej) => {
|
||||
if (!this.isOpen()) return res();
|
||||
|
||||
this.socket.send(msg, (err) => {
|
||||
if (err) {
|
||||
this.logger.error(err, 'WebSocket send error');
|
||||
this.logger.error({event: "websocket send error", msg: err});
|
||||
this.close();
|
||||
res();
|
||||
return;
|
||||
|
||||
@@ -8,8 +8,9 @@ import { isIP } from 'net';
|
||||
import { IPDataManager } from '../../IPData.js';
|
||||
import WSClient from './WSClient.js';
|
||||
import { User } from '../../User.js';
|
||||
import pino from 'pino';
|
||||
import { pino, type Logger } from 'pino';
|
||||
import { BanManager } from '../../BanManager.js';
|
||||
import { v4 as uuid4 } from 'uuid';
|
||||
|
||||
const kAllowedProtocols = [
|
||||
"guacamole" // Regular ol' collabvm1 protocol
|
||||
@@ -20,17 +21,25 @@ export default class WSServer extends EventEmitter implements NetworkServer {
|
||||
private wsServer: WebSocketServer;
|
||||
private clients: WSClient[];
|
||||
private Config: IConfig;
|
||||
private logger = pino({ name: 'CVMTS.WSServer' });
|
||||
private logger: Logger;
|
||||
private banmgr: BanManager;
|
||||
private uuid: string;
|
||||
|
||||
constructor(config: IConfig, banmgr: BanManager) {
|
||||
super();
|
||||
this.Config = config;
|
||||
this.clients = [];
|
||||
this.uuid = uuid4();
|
||||
this.logger = pino().child({
|
||||
stream: 'CVMTS.WSServer',
|
||||
"uuid/websocket/server": this.uuid,
|
||||
node: config.collabvm.node,
|
||||
});
|
||||
this.httpServer = http.createServer();
|
||||
this.wsServer = new WebSocketServer({ noServer: true, perMessageDeflate: false, clientTracking: false });
|
||||
this.httpServer.on('upgrade', (req: http.IncomingMessage, socket: internal.Duplex, head: Buffer) => this.httpOnUpgrade(req, socket, head));
|
||||
this.httpServer.on('request', (req, res) => {
|
||||
this.logger.debug({ event: "request", path: req.url });
|
||||
res.writeHead(426);
|
||||
res.write('This server only accepts WebSocket connections.');
|
||||
res.end();
|
||||
@@ -39,13 +48,33 @@ export default class WSServer extends EventEmitter implements NetworkServer {
|
||||
}
|
||||
|
||||
start(): void {
|
||||
this.logger.info({
|
||||
event: "websocket server starting",
|
||||
host: this.Config.http.host,
|
||||
port: this.Config.http.port,
|
||||
});
|
||||
this.httpServer.listen(this.Config.http.port, this.Config.http.host, () => {
|
||||
this.logger.info(`WebSocket server listening on ${this.Config.http.host}:${this.Config.http.port}`);
|
||||
this.logger.info({
|
||||
event: "websocket server started",
|
||||
host: this.Config.http.host,
|
||||
port: this.Config.http.port,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
this.httpServer.close();
|
||||
this.logger.info({
|
||||
event: "websocket server stopping",
|
||||
host: this.Config.http.host,
|
||||
port: this.Config.http.port,
|
||||
});
|
||||
this.httpServer.close(() => {
|
||||
this.logger.info({
|
||||
event: "websocket server stopped",
|
||||
host: this.Config.http.host,
|
||||
port: this.Config.http.port,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async httpOnUpgrade(req: http.IncomingMessage, socket: internal.Duplex, head: Buffer) {
|
||||
@@ -142,17 +171,34 @@ export default class WSServer extends EventEmitter implements NetworkServer {
|
||||
}
|
||||
|
||||
private onConnection(ws: WebSocket, req: http.IncomingMessage, ip: string, protocol: string) {
|
||||
let client = new WSClient(ws, ip);
|
||||
const uuid = uuid4();
|
||||
const connectionId = {
|
||||
"uuid/websocket/client": uuid,
|
||||
src_ip: ip
|
||||
};
|
||||
this.logger.info({ ...connectionId, event: "websocket client connecting" });
|
||||
|
||||
let client = new WSClient(ws, ip, uuid);
|
||||
this.clients.push(client);
|
||||
|
||||
let user = new User(client, protocol, IPDataManager.GetIPData(ip), this.Config);
|
||||
this.logger.info({
|
||||
...connectionId,
|
||||
event: "websocket client connection bound to user",
|
||||
"uuid/user": user.uuid
|
||||
});
|
||||
|
||||
this.emit('connect', user);
|
||||
|
||||
ws.on('error', (e) => {
|
||||
this.logger.error(`${e} (caused by connection ${ip})`);
|
||||
this.logger.error({ ...connectionId, event: "websocket connection error" });
|
||||
ws.close();
|
||||
});
|
||||
|
||||
this.logger.info(`New WebSocket connection from ${user.IP.address}`);
|
||||
ws.on('close', () => {
|
||||
this.logger.error({ ...connectionId, event: "websocket connection closed" });
|
||||
});
|
||||
|
||||
this.logger.info({ ...connectionId, event: "websocket client connected" });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user