diff --git a/package.json b/package.json index 1f081d6..bafc8ba 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@hyperswarm/dht-relay": "^0.3.0", "@lumeweb/kernel-peer-discovery-client": "git+https://git.lumeweb.com/LumeWeb/kernel-peer-discovery-client.git", "@lumeweb/libkernel-universal": "git+https://git.lumeweb.com/LumeWeb/libkernel-universal.git", + "async-mutex": "^0.4.0", "eventemitter2": "^6.4.9", "hyperswarm": "^4.3.7" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 13330ef..148e920 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,6 +6,7 @@ specifiers: '@lumeweb/libkernel-universal': git+https://git.lumeweb.com/LumeWeb/libkernel-universal.git '@types/random-number-csprng': ^1.0.0 '@types/ws': ^8.5.4 + async-mutex: ^0.4.0 esbuild: ^0.14.54 eventemitter2: ^6.4.9 hyperswarm: ^4.3.7 @@ -15,8 +16,9 @@ specifiers: dependencies: '@hyperswarm/dht-relay': 0.3.0 - '@lumeweb/kernel-peer-discovery-client': git.lumeweb.com/LumeWeb/kernel-peer-discovery-client/e5ed06e1df9f60bee10980ff8b337fe2990f4b52 - '@lumeweb/libkernel-universal': git.lumeweb.com/LumeWeb/libkernel-universal/fb377db59fd24761d140eb98f49166ea1cc7cc39 + '@lumeweb/kernel-peer-discovery-client': git.lumeweb.com/LumeWeb/kernel-peer-discovery-client/ec966974f6898dc3dc8d484df786bfe940421131 + '@lumeweb/libkernel-universal': git.lumeweb.com/LumeWeb/libkernel-universal/0bc5807c6afdcc95ed960a6ba2c304388fa67f28 + async-mutex: 0.4.0 eventemitter2: 6.4.9 hyperswarm: 4.3.7 @@ -107,6 +109,12 @@ packages: '@types/node': 18.11.18 dev: true + /async-mutex/0.4.0: + resolution: {integrity: sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==} + dependencies: + tslib: 2.5.0 + dev: false + /b4a/1.6.1: resolution: {integrity: sha512-AsKjNhz72yxteo/0EtQEiwkMUgk/tGmycXlbG4g3Ard2/ULtNLUykGOkeK0egmN27h0xMAhb76jYccW+XTBExA==} dev: false @@ -716,6 +724,10 @@ packages: resolution: {integrity: sha512-SVqEcMZBsZF9mA78rjzCrYrUs37LMJk3ShZ851ygZYW1cMeIjs9mL57KO6Iv5mmjSQnOe/29/VAfGXo+oRCiVw==} dev: false + /tslib/2.5.0: + resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + dev: false + /typescript/4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} @@ -749,13 +761,13 @@ packages: resolution: {integrity: sha512-FIr/DEeoHfj7ftfylnoFt3rAIRoWXpx2AoDfrT2qD2wtp7Dp+COajvs/Icb7uHqRW9m60f5iXZwdsJJO3kvb7w==} dev: false - git.lumeweb.com/LumeWeb/kernel-peer-discovery-client/e5ed06e1df9f60bee10980ff8b337fe2990f4b52: - resolution: {commit: e5ed06e1df9f60bee10980ff8b337fe2990f4b52, repo: https://git.lumeweb.com/LumeWeb/kernel-peer-discovery-client.git, type: git} + git.lumeweb.com/LumeWeb/kernel-peer-discovery-client/ec966974f6898dc3dc8d484df786bfe940421131: + resolution: {commit: ec966974f6898dc3dc8d484df786bfe940421131, repo: https://git.lumeweb.com/LumeWeb/kernel-peer-discovery-client.git, type: git} name: '@lumeweb/kernel-peer-discovery-client' version: 0.1.0 dependencies: '@hyperswarm/dht-relay': 0.3.0 - '@lumeweb/libkernel-universal': git.lumeweb.com/LumeWeb/libkernel-universal/fb377db59fd24761d140eb98f49166ea1cc7cc39 + '@lumeweb/libkernel-universal': git.lumeweb.com/LumeWeb/libkernel-universal/0bc5807c6afdcc95ed960a6ba2c304388fa67f28 '@lumeweb/peer-discovery': git.lumeweb.com/LumeWeb/peer-discovery/d2bd926275103d60fdd3c7c432eae5c278f49261 '@siaweb/libweb': git.lumeweb.com/LumeWeb/libsiaweb/5e6cdba3e7d9a4b94e21ddcd5f2b5138fb440ee8 hyperswarm: 4.3.7 @@ -763,12 +775,13 @@ packages: libkmodule: 0.2.53 dev: false - git.lumeweb.com/LumeWeb/libkernel-universal/fb377db59fd24761d140eb98f49166ea1cc7cc39: - resolution: {commit: fb377db59fd24761d140eb98f49166ea1cc7cc39, repo: https://git.lumeweb.com/LumeWeb/libkernel-universal.git, type: git} + git.lumeweb.com/LumeWeb/libkernel-universal/0bc5807c6afdcc95ed960a6ba2c304388fa67f28: + resolution: {commit: 0bc5807c6afdcc95ed960a6ba2c304388fa67f28, repo: https://git.lumeweb.com/LumeWeb/libkernel-universal.git, type: git} name: '@lumeweb/libkernel-universal' version: 0.1.0 dependencies: '@siaweb/libweb': git.lumeweb.com/LumeWeb/libsiaweb/5e6cdba3e7d9a4b94e21ddcd5f2b5138fb440ee8 + eventemitter2: 6.4.9 libkernel: 0.1.48 libkmodule: 0.2.53 dev: false diff --git a/src/index.ts b/src/index.ts index 0b89a7c..0a73f7d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,7 @@ import { load } from "@lumeweb/libkernel-universal"; import Hyperswarm from "hyperswarm"; import randomNumber from "random-number-csprng"; import EventEmitter from "eventemitter2"; +import { Mutex } from "async-mutex"; export default class HyperswarmWeb extends EventEmitter { private _options: any; @@ -20,6 +21,8 @@ export default class HyperswarmWeb extends EventEmitter { private _activeRelay: Hyperswarm; private _discovery: PeerDiscoveryClient; private _queuedEmActions: [string, any][] = []; + + private _connectionMutex: Mutex = new Mutex(); constructor(opts: any = {}) { super(); opts.custodial = false; @@ -34,59 +37,66 @@ export default class HyperswarmWeb extends EventEmitter { private async ensureConnection(): Promise { const logErr = (await load()).logErr; + await this._connectionMutex.waitForUnlock(); + this._connectionMutex.acquire(); + if (this._activeRelay) { return; } const relays = this.relays; - do { - const index = - relays.length > 1 ? await randomNumber(0, relays.length - 1) : 0; - const relay = relays[index]; + if (relays.length > 0) { + do { + const index = + relays.length > 1 ? await randomNumber(0, relays.length - 1) : 0; + const relay = relays[index]; - let ret; - try { - ret = await this._discovery.discover(relay); - } catch (e) { - logErr(e); - relays.splice(index, 1); - continue; - } + let ret; + try { + ret = await this._discovery.discover(relay); + } catch (e) { + logErr(e); + relays.splice(index, 1); + continue; + } - if (!ret) { - relays.splice(index, 1); - continue; - } + if (!ret) { + relays.splice(index, 1); + continue; + } - ret = ret as Peer; + ret = ret as Peer; - const connection = `wss://${ret.host}:${ret.port}`; + const connection = `wss://${ret.host}:${ret.port}`; - if (!(await this.isServerAvailable(connection))) { - relays.splice(index, 1); - continue; - } + if (!(await this.isServerAvailable(connection))) { + relays.splice(index, 1); + continue; + } - this._activeRelay = new Hyperswarm({ - dht: new DhtNode( - new Stream(true, new WebSocket(connection)), - this._options - ), - keyPair: this._options.keyPair, - }); + this._activeRelay = new Hyperswarm({ + dht: new DhtNode( + new Stream(true, new WebSocket(connection)), + this._options + ), + keyPair: this._options.keyPair, + }); - this._activeRelay.on("close", () => { - this._activeRelay = undefined; - }); - } while (relays.length > 0 && !this._activeRelay); + this._activeRelay.on("close", () => { + this._activeRelay = undefined; + }); + } while (relays.length > 0 && !this._activeRelay); + } if (!this._activeRelay) { + this._connectionMutex.release(); throw new Error("Failed to find an available relay"); } this._processQueuedActions(); await this._activeRelay.dht.ready(); + this._connectionMutex.release(); } private async isServerAvailable(connection: string): Promise {