101 lines
2.5 KiB
JavaScript
101 lines
2.5 KiB
JavaScript
"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<void>} 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;
|