diff --git a/src/lib/agent/Agent.mjs b/src/lib/agent/Agent.mjs index 1c2c161..1347647 100644 --- a/src/lib/agent/Agent.mjs +++ b/src/lib/agent/Agent.mjs @@ -2,6 +2,7 @@ import fs from 'fs'; import os from 'os'; +import path from 'path'; import log from 'log'; const l = log.get("agent"); @@ -17,19 +18,19 @@ class Agent { } async find_secret() { - if(this.config.secret_join_filepath !== "CHANGE_ME" - && fs.existsSync(this.config.secret_join_filepath)) - return await fs.promises.readFile( - path.resolve( - path.dirname(settings.cli.config), - this.config.secret_join_filepath - ), - "utf-8"); + if(this.config.secret_join_filepath !== "CHANGE_ME") { + let filepath = path.resolve( + path.dirname(settings.cli.config), + this.config.secret_join_filepath + ); + if(fs.existsSync(filepath)) + return await fs.promises.readFile(filepath, "utf-8"); + } if(this.config.secret_join !== "CHANGE_ME") return this.config.secret_join; - return null; + throw new Error(`Error: Failed to find join secret.`); } async init() { diff --git a/src/lib/agent/Peer.mjs b/src/lib/agent/Peer.mjs index 533f401..838f953 100644 --- a/src/lib/agent/Peer.mjs +++ b/src/lib/agent/Peer.mjs @@ -12,6 +12,10 @@ class Peer extends EventEmitter { get remote_endpoint() { return { address: this.address, port: this.port }; } + get id_short() { + if(this.id === null) return null; + return this.id.substring(0, 7); + } constructor(server, connection) { super(); @@ -78,7 +82,9 @@ class Peer extends EventEmitter { __handle_hello(msg) { this.id = msg.id; this.known_peers = msg.peers; - if(msg.id === this.server.our_id) return false; + if(msg.id === this.server.our_id) { + return false; + } return true; } diff --git a/src/lib/agent/PeerServer.mjs b/src/lib/agent/PeerServer.mjs index 4221d73..0c1e2eb 100644 --- a/src/lib/agent/PeerServer.mjs +++ b/src/lib/agent/PeerServer.mjs @@ -6,6 +6,7 @@ import net from 'net'; import p_retry from 'p-retry'; import log from 'log'; const l = log.get("peerserver"); +import settings from '../../settings.mjs'; import Connection from '../transport/Connection.mjs'; import ErrorWrapper from '../core/ErrorWrapper.mjs'; @@ -39,6 +40,8 @@ class PeerServer extends EventEmitter { */ listen(port = 5252, host="::") { return new Promise((resolve, reject) => { + this.host = host; + this.port = port; this.server = net.createServer(async (client) => { await this.handle_client(client); }); @@ -137,13 +140,24 @@ class PeerServer extends EventEmitter { // The arrow function here is NOT async because the promise from // this.add_peer is returned directly to await Promise.all(). return (await Promise.all(new_peers.map( - new_peer => p_retry(async () => await this.add_peer( + new_peer => p_retry(async () => await this.__add_peer( new_peer.address, new_peer.port ), { retries: this.retries, - onFailedAttempt: (error, attempt, left) => - l.error(`[attempt ${attempt} / ${left}] Error while connecting to ${new_peer.address}:${new_peer.port}: ${error}`) + onFailedAttempt: (error) => { + switch(error instanceof ErrorWrapper ? error.inner_exception.code : error.code) { + case "ECONNREFUSED": + l.error(`Failed to connect to peer at ${new_peer.address}:${new_peer.port}.`); + break; + + default: + let msg = `[attempt ${error.attemptNumber} / ${error.retriesLeft+error.attemptNumber}] Error while connecting to ${new_peer.address}:${new_peer.port}: ${error}`; + if(settings.cli.verbose) msg += `\n${error.stack}`; + l.error(msg); + break; + } + } }) ))).filter(peer => peer instanceof Peer); } @@ -153,22 +167,29 @@ class PeerServer extends EventEmitter { * Note: This does NOT automatically connect to all the peers known to the * peer you're connecting to! * You need to do this manually. + * Note that you should NOT use this function directly. Instead, use + * add_peers(), which supports multiple peers and also automatically + * retries on failure too. * @throws {ErrorWrapper} Throws if the connection failed. This could be for a large number of different reasons, from an incorrect join secret from the remote to connection issues. * @param {string} address The address to connect to. * @param {number} port The port number to connect to. * @return {Promise} A Promise that resolves to the resulting Peer connection, or null if the connection wasn't attemped. */ - async add_peer(address, port) { + async __add_peer(address, port) { // If we're already connected, don't bother reconnecting again if(this.peers().some(el => el.address === address && el.port === port)) return; + // If we are attempting to connect to ourselves, then don't bother + if(address == this.host && port == this.port) + return; const peer_string = `peer:${address}:${port}`; this.connecting_peers.push(peer_string); - let conn; + let conn = null; try { conn = await Peer.Initiate(this, address, port); peer_initialise(conn); + console.log(`[DEBUG] conn before`, conn); } catch(error) { throw new ErrorWrapper(`Error: Failed to connect to peer.`, error); @@ -177,6 +198,9 @@ class PeerServer extends EventEmitter { this.connecting_peers.splice(this.connecting_peers.indexOf(peer_string)); } + if(conn === null) return null; + + l.notice(`Peer ${conn.id_short} from ${address}:${port} connected`) this.emit(`peer`, conn); return conn; }