@interopio/gateway-server
Version:
## `@glue42/gateway-ent` compatibility
108 lines (102 loc) • 3.92 kB
text/typescript
import * as ws from 'ws';
import {socketKey} from '../../../utils.js';
import {relays} from '../relays/core.js';
import getLogger from '../../../logger.js';
import {HttpServerRequest} from '../../../server/exchange.ts';
const logger = getLogger('mesh.ws.cluster');
function onMessage(key: string, node: string | undefined, socketsByNodeId: Map<string, Map<string, ws.WebSocket>>, msg: string) {
try {
relays.send(key, node, msg, (k, err?: Error) => {
if (err) {
logger.warn(`${k} error writing msg ${msg}: ${err}`);
return;
}
if (logger.enabledFor('debug')) {
logger.debug(`${k} sent msg ${msg}`);
}
});
} catch (ex) {
logger.error(`${key} unable to process message`, ex);
if (node) {
const socket = socketsByNodeId.get(node)?.get(key);
socket?.terminate();
}
}
}
async function create(server: {wss: ws.WebSocketServer}): Promise<{ close?: () => Promise<void> }> {
const socketsByNodeId = new Map<string, Map<string, ws.WebSocket>>();
relays.onMsg = (k, nodeId, msg) => {
try {
const sockets = socketsByNodeId.get(nodeId);
if (sockets && sockets.size > 0) {
for (const [key, socket] of sockets) {
socket.send(msg, {binary: false}, (err?: Error) => {
if (err) {
logger.warn(`${key} error writing from ${k} msg ${msg}: ${err}`);
return;
}
if (logger.enabledFor('debug')) {
logger.debug(`${key} sent from ${k} msg ${msg}`);
}
});
}
} else {
logger.warn(`${k} dropped msg ${msg}.`);
}
} catch (ex) {
logger.error(`${k} unable to process message`, ex);
}
};
server.wss
.on('error', () => {
logger.error(`error starting mesh server`);
})
.on('listening', () => {
logger.info(`mesh server is listening`);
})
.on('connection', (socket, req) => {
const request = new HttpServerRequest(req);
const key = socketKey(request.socket);
const query = new URLSearchParams(request.query ?? undefined);
logger.info(`${key} connected on cluster with ${query}`);
const node = query.get('node');
if (node) {
let sockets = socketsByNodeId.get(node);
if (!sockets) {
sockets = new Map<string, ws.WebSocket>();
socketsByNodeId.set(node, sockets);
}
sockets.set(key, socket);
}
else {
socket.terminate();
return;
}
//onOpen(connectedNodes, key);
socket.on('error', (err: Error) => {
logger.error(`${key} websocket error: ${err}`, err);
});
socket.on('message', (data, _isBinary) => {
if (Array.isArray(data)) {
data = Buffer.concat(data);
}
onMessage(key, node, socketsByNodeId, data as unknown as string);
});
socket.on('close', (_code, _reason) => {
logger.info(`${key} disconnected from cluster`);
const sockets = socketsByNodeId.get(node);
if (sockets) {
sockets.delete(key);
if (sockets.size === 0) {
socketsByNodeId.delete(node);
}
}
});
});
return {
close: async () => {
server.wss.close();
}
};
}
export default create;