UNPKG

slavery-js

Version:

A simple clustering app that allows you to scale an application on multiple thread, containers or machines

187 lines (170 loc) 6.93 kB
import { Server } from "socket.io"; import http from "http"; import { createServer } from "http"; import { Socket } from "socket.io"; import { log, Pool } from '../utils/index.js'; import type Listener from './types/Listener.js'; import Connection from "./Connection.js"; class NetworkServer { /* this class will handle the logic managing the server conenctions with clilent, * it will keep track of the node id and it will handle connection and dicoections */ private io: Server; private host: string; private port: number; private maxTransferSize: number; private clients: Pool<Connection>; public name: string; public isLan: boolean; public connectionCallback: any; public disconnectCallback: any; public listeners: Listener[]; public httpServer?: http.Server; public isReady: boolean; private ioOptions: any; constructor({ name, host, port, listeners } : { name: string, host: string, port: number, listeners: Listener[] }, options?: { maxTransferSize: number }) { this.host = host || "localhost"; this.isLan = this.host !== 'localhost' this.port = port || 0; // zero means random port this.isReady = false; this.maxTransferSize = options?.maxTransferSize || 1e9; // default 1GB this.name = name? name : "server"; this.connectionCallback = null; this.disconnectCallback = null; this.clients = new Pool<Connection>(); this.listeners = listeners || []; this.ioOptions = { maxHttpBufferSize: this.maxTransferSize, }; // initiate with the server if(this.isLan){ // if we are in a over lan // create a http server this.httpServer = createServer(); this.io = new Server(this.httpServer, this.ioOptions); this.httpServer.listen(this.port, this.host, () => { let address = this.httpServer?.address(); if(!address || typeof address === "string") { console.error("Server is not running"); return; } this.isReady = true; this.port = address.port }); }else{ // if we are in localhost this.io = new Server(this.port, this.ioOptions); // get the port number this.port = (this.io as any).httpServer.address().port; } // create a new socket.io client instance this.io.on("connection", this.handleConnection.bind(this)); this.io.on("reconnect", () => log("[Server] on reconnect triggered")); this.io.on("disconnect", this.handleDisconnection.bind(this)); // set the listener on the server socket this.setListeners(this.listeners); } private async handleConnection(socket: Socket) { log("[Server] got new connection"); // make a new connectection instance let connection = new Connection({ socket, name: this.name, listeners: this.listeners }); // await fo connection to be established await connection.connected(); // get the id of the connection let id = connection.getTargetId(); log("[Server] connection id: ", id); // check if id is null if(id == null) throw new Error("Connection id is null"); // check if connection already exists if(this.clients.has(id)) { let client = this.clients.remove(id); client && client.close(); } // give server listeners to the connection // await connection.setListeners(this.listeners); // add connection to pool this.clients.add(id, connection); // run callback if(this.connectionCallback) this.connectionCallback(connection); } private handleDisconnection(socket: Socket) { console.log('[Server] got disconnection from node'); let socketId = socket.id console.log('[Server] socket id: ', socketId); // filter every client based on the socket id let conn = this.clients.toArray() .filter((client: Connection) => client.socketId === socketId)[0]; // cast conn as type Connection if(conn) { // close the connection conn.close(); let id = conn.getTargetId(); if(id === undefined) throw new Error("Connection id is undefined"); this.clients.remove(conn.getTargetId() as string); // run the disconnect callback if(this.disconnectCallback) this.disconnectCallback(conn); } } public setListeners(listeners: Listener[]) { // set the listeners on the server socket listeners.forEach((listener: Listener) => { // run the listener callback and emit the result to the client let callback = async ( ...args: any[] ) => { // run the listener callback let result = await listener.callback(...args); // emit the result to the client this.io.emit(listener.event, result); } // remove any previous listeners this.io.removeAllListeners(listener.event); // set the new listener this.io.on(listener.event, callback); }); // store the listeners this.listeners = listeners; // broadcast the new listeners to all clients this.io.emit('_set_listeners', this.listeners); } public broadcast(event: string, data: any) { // broadcast an event to all clients this.io.emit(event, data); } public addListeners(listeners: Listener[]) { // add a new listener to the server // if we have the same event name, we will overwrite it const eventMap = new Map(this.listeners.map(l => [l.event, l])); listeners.forEach(l => eventMap.set(l.event, l)); listeners = Array.from(eventMap.values()); // set the listener on the server socket this.setListeners(this.listeners); } public getClient(id: string) : Connection | null { return this.clients.get(id); } public getClients() : Connection[] { return this.clients.toArray(); } public onConnection(callback: any) { this.connectionCallback = callback; } public onDisconnect(callback: any) { this.disconnectCallback = callback; } public getListeners() { // the the listneres from the first client let client = this.clients.toArray()[0]; // if client is null return an empty array if(!client) return []; // return the listeners return client.getListeners(); } async close() { // close all sockets this.io.close(); } } export default NetworkServer;