"use strict"; import { EventEmitter, once } from 'events'; import net from 'net'; import Connection from '../transport/Connection.mjs'; import Peer from './Peer.mjs'; class PeerServer extends EventEmitter { constructor(our_id) { super(); this.our_id = our_id; this.peers = []; } /** * Starts the PeerServer listening on the given port and bind address. * @param {Number} [port=5252] The port number to listen on. * @param {String} [host="::"] The address to bind to. * @return {Promise} A Promise that resolves when the server setup is complete. */ listen(port = 5252, host="::") { return new Promise((resolve, reject) => { this.server = net.createServer(async (client) => { await this.handle_client(client); }); this.server.once("error", reject); this.server.on("error", this.handle_error); this.server.listen({ host, port, exclusive: false }, () => { this.server.off("error", reject); resolve(); }); }); } handle_error(error) { throw error; } async handle_client(client) { const peer = new Peer(this, await Connection.Wrap(client)); this.peers.push(peer); peer.on("message", this.handle_message.bind(this, peer)); peer.on("destroy", this.handle_destroy.bind(this, peer)); await once(peer, "connect"); } async handle_message(peer, message) { this.emit("message", peer, message); this.emit(`message-${message.event}`, peer, message.message); } async handle_destroy(peer) { const index = this.peers.indexOf(peer); if(index > -1) this.peers.splice(index, 1); this.emit("disconnect", peer.remote_endpoint); } /** * Returns a list of all currently known peer addresses. * @return {{address:string,port:number}[]} */ peer_addresses() { return this.peers.map((peer) => peer.remote_endpoint) .filter(el => typeof el.addr === "string" && typeof el.port === "number"); } /** * Shuts the server down. * This does not disconnect any existing peers! * @return {Promise} A Promise that resolves once the server has been shutdown. */ shutdown_server() { return new Promise((resolve, _reject) => { this.server.close(resolve); }); } /** * Stops the PeerServer and gracefully (if possible) disconnects all existing peers. * @return {Promise} A Promise that resolves once the shutdown process has completed. */ async destroy() { await this.shutdown_server(); await Promise.all(...this.peers.map(peer => peer.destroy())); } } export default PeerServer;