@yz-social/kdht 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dht/kbucket.js +0 -1
- package/dht/nodeStorage.js +1 -1
- package/package.json +4 -3
- package/portals/node.js +2 -2
- package/portals/router.js +5 -3
- package/spec/bots.js +16 -6
- package/spec/portal.js +10 -3
- package/spec/{webrtcTests.js → testWebrtc.js} +14 -15
- package/transports/contact.js +9 -8
- package/transports/webrtc.js +13 -12
package/dht/kbucket.js
CHANGED
|
@@ -36,7 +36,6 @@ export class KBucket {
|
|
|
36
36
|
}
|
|
37
37
|
async refresh() { // Refresh specified bucket using LocateNodes for a random key in the specified bucket's range.
|
|
38
38
|
if (this.node.isStopped() || !this.contacts.length) return false; // fixme skip isStopped?
|
|
39
|
-
this.node.ilog('refresh bucket', this.index);
|
|
40
39
|
const targetKey = this.randomTarget;
|
|
41
40
|
await this.node.locateNodes(targetKey); // Side-effect is to update this bucket.
|
|
42
41
|
return true;
|
package/dht/nodeStorage.js
CHANGED
|
@@ -23,7 +23,7 @@ export class NodeStorage extends NodeRefresh {
|
|
|
23
23
|
}
|
|
24
24
|
async replicateCloserStorage(contact) { // Replicate to new contact any of our data for which contact is closer than us.
|
|
25
25
|
for (const key in this.storage.keys()) {
|
|
26
|
-
if (contact.distance(key) <= this.distance(key)) {
|
|
26
|
+
if (contact.connection && (contact.distance(key) <= this.distance(key))) {
|
|
27
27
|
await contact.store(key, this.retrieveLocally(key));
|
|
28
28
|
}
|
|
29
29
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yz-social/kdht",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Pure Kademlia base, for testing variations.",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
"background": "npm stop; (npm start 1>server.log 2>&1 &); sleep 1",
|
|
15
15
|
"bots": "node spec/bots.js",
|
|
16
16
|
"thrashbots": "node spec/bots.js --thrash",
|
|
17
|
-
"
|
|
17
|
+
"testWebrtc": "npx jasmine spec/testWebrtc.js",
|
|
18
|
+
"test": "npx jasmine && npm run testWebrtc && echo '-- SUCCESS --' || echo '**** FAIL ****'"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
|
-
"@yz-social/webrtc": "^0.1.
|
|
21
|
+
"@yz-social/webrtc": "^0.1.3",
|
|
21
22
|
"uuid": "^13.0.0"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
package/portals/node.js
CHANGED
|
@@ -3,14 +3,14 @@ import cluster from 'node:cluster';
|
|
|
3
3
|
import { v4 as uuidv4 } from 'uuid';
|
|
4
4
|
import { WebContact, Node } from '../index.js';
|
|
5
5
|
|
|
6
|
-
export async function setup({baseURL, externalBaseURL = '',
|
|
6
|
+
export async function setup({baseURL, externalBaseURL = '', debug, fixedSpacing, info, variableSpacing}) {
|
|
7
7
|
const hostName = uuidv4();
|
|
8
8
|
process.title = 'kdht-portal-' + hostName;
|
|
9
9
|
// For debugging:
|
|
10
10
|
// process.on('uncaughtException', error => console.error(hostName, 'Global uncaught exception:', error));
|
|
11
11
|
// process.on('unhandledRejection', error => console.error(hostName, 'Global unhandled promise rejection:', error));
|
|
12
12
|
|
|
13
|
-
const contact = await WebContact.create({name: hostName, isServerNode: true, info
|
|
13
|
+
const contact = await WebContact.create({name: hostName, isServerNode: true, info, debug});
|
|
14
14
|
// Handle signaling that comes as a message from the server.
|
|
15
15
|
process.on('message', async ([senderSname, ...incomingSignals]) => { // Signals from a sender through the server.
|
|
16
16
|
const response = await contact.signals(senderSname, ...incomingSignals);
|
package/portals/router.js
CHANGED
|
@@ -5,8 +5,7 @@ import { Node } from '../index.js';
|
|
|
5
5
|
export const router = express.Router();
|
|
6
6
|
|
|
7
7
|
const portals = {}; // Maps worker sname => worker, for the full lifetime of the program. NOTE: MAY get filed in out of order from workers.
|
|
8
|
-
|
|
9
|
-
for (const worker of workers) {
|
|
8
|
+
function initWorker(worker) {
|
|
10
9
|
worker.on('message', message => { // Message from a worker, in response to a POST.
|
|
11
10
|
if (!worker.tag) { // The very first message from a worker (during setup) will identify its tag.
|
|
12
11
|
portals[message] = worker;
|
|
@@ -23,8 +22,11 @@ for (const worker of workers) {
|
|
|
23
22
|
}
|
|
24
23
|
});
|
|
25
24
|
}
|
|
26
|
-
cluster.
|
|
25
|
+
Object.values(cluster.workers).forEach(initWorker);
|
|
26
|
+
cluster.on('exit', (worker, code, signal) => { // Tell us about dead workers and restart them.
|
|
27
27
|
console.error(`\n\n*** Crashed worker ${worker.id}:${worker.tag} received code: ${code} signal: ${signal}. ***\n`);
|
|
28
|
+
delete worker.tag;
|
|
29
|
+
initWorker(cluster.fork());
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
router.get('/name/:label', (req, res, next) => { // Answer the actual sname corresponding to label.
|
package/spec/bots.js
CHANGED
|
@@ -17,7 +17,7 @@ const argv = yargs(hideBin(process.argv))
|
|
|
17
17
|
alias: 'n',
|
|
18
18
|
alias: 'nbots',
|
|
19
19
|
type: 'number',
|
|
20
|
-
default:
|
|
20
|
+
default: logicalCores,
|
|
21
21
|
description: "The number of bots, which can only be reached through the network."
|
|
22
22
|
})
|
|
23
23
|
.option('baseURL', {
|
|
@@ -37,9 +37,16 @@ const argv = yargs(hideBin(process.argv))
|
|
|
37
37
|
default: 0,
|
|
38
38
|
description: "The number of test writes to check."
|
|
39
39
|
})
|
|
40
|
+
.option('info', {
|
|
41
|
+
alias: 'i',
|
|
42
|
+
type: 'boolean',
|
|
43
|
+
default: true,
|
|
44
|
+
description: "Run with info logging."
|
|
45
|
+
})
|
|
40
46
|
.option('verbose', {
|
|
41
47
|
alias: 'v',
|
|
42
48
|
type: 'boolean',
|
|
49
|
+
default: false,
|
|
43
50
|
description: "Run with verbose logging."
|
|
44
51
|
})
|
|
45
52
|
.parse();
|
|
@@ -48,21 +55,24 @@ const host = uuidv4();
|
|
|
48
55
|
process.title = 'kdht-bot-' + host;
|
|
49
56
|
|
|
50
57
|
if (cluster.isPrimary) {
|
|
51
|
-
console.log(`${cpus()[0].model}, ${logicalCores} logical cores. Starting ${argv.nBots} over ${Node.refreshTimeIntervalMS/1000} seconds.`);
|
|
58
|
+
console.log(`${cpus()[0].model}, ${logicalCores} logical cores. Starting ${argv.nBots} ${argv.thrash ? 'thrashbots' : 'bots'} over ${Node.refreshTimeIntervalMS/1000} seconds.`);
|
|
52
59
|
for (let i = 1; i < argv.nBots; i++) { // The cluster primary becomes bot 0.
|
|
53
60
|
cluster.fork();
|
|
54
61
|
}
|
|
62
|
+
cluster.on('exit', (worker, code, signal) => { // Tell us about dead workers and restart them.
|
|
63
|
+
console.error(`\n\n*** Crashed worker ${worker.id}:${worker.tag} received code: ${code} signal: ${signal}. ***\n`);
|
|
64
|
+
cluster.fork();
|
|
65
|
+
});
|
|
55
66
|
if (argv.nWrites) {
|
|
56
67
|
console.log(new Date(), 'Waiting a refresh interval while bots get randomly created before write/read test');
|
|
57
68
|
await Node.delay(2 * Node.refreshTimeIntervalMS);
|
|
58
69
|
launchWriteRead(argv.nWrites, argv.baseURL, Node.refreshTimeIntervalMS, argv.verbose);
|
|
59
|
-
}
|
|
70
|
+
}
|
|
60
71
|
}
|
|
61
72
|
|
|
62
|
-
const info = false;
|
|
63
73
|
await Node.delay(Node.randomInteger(Node.refreshTimeIntervalMS));
|
|
64
74
|
console.log(cluster.worker?.id || 0, host);
|
|
65
|
-
let contact = await WebContact.create({name: host, info, debug: argv.verbose});
|
|
75
|
+
let contact = await WebContact.create({name: host, info: argv.info, debug: argv.verbose});
|
|
66
76
|
let bootstrapName = await contact.fetchBootstrap(argv.baseURL);
|
|
67
77
|
let bootstrapContact = await contact.ensureRemoteContact(bootstrapName, argv.baseURL);
|
|
68
78
|
await contact.join(bootstrapContact);
|
|
@@ -80,7 +90,7 @@ while (argv.thrash) {
|
|
|
80
90
|
await contact.disconnect();
|
|
81
91
|
await Node.delay(1e3); // TODO: remove?
|
|
82
92
|
|
|
83
|
-
contact = await WebContact.create({name: next, info, debug: argv.verbose});
|
|
93
|
+
contact = await WebContact.create({name: next, info: argv.info, debug: argv.verbose});
|
|
84
94
|
bootstrapName = await contact.fetchBootstrap(argv.baseURL);
|
|
85
95
|
bootstrapContact = await contact.ensureRemoteContact(bootstrapName, argv.baseURL);
|
|
86
96
|
await contact.join(bootstrapContact);
|
package/spec/portal.js
CHANGED
|
@@ -21,7 +21,7 @@ const argv = yargs(hideBin(process.argv))
|
|
|
21
21
|
alias: 'nportals',
|
|
22
22
|
alias: 'p',
|
|
23
23
|
type: 'number',
|
|
24
|
-
default: Math.max(logicalCores
|
|
24
|
+
default: Math.max(2, logicalCores - 1),
|
|
25
25
|
description: "The number of steady nodes that handle initial connections."
|
|
26
26
|
})
|
|
27
27
|
.option('nBots', {
|
|
@@ -52,9 +52,16 @@ const argv = yargs(hideBin(process.argv))
|
|
|
52
52
|
default: '',
|
|
53
53
|
description: "The base URL of the some other portal server to which we should connect ours, if any."
|
|
54
54
|
})
|
|
55
|
+
.option('info', {
|
|
56
|
+
alias: 'i',
|
|
57
|
+
type: 'boolean',
|
|
58
|
+
default: true,
|
|
59
|
+
description: "Run with info logging."
|
|
60
|
+
})
|
|
55
61
|
.option('verbose', {
|
|
56
62
|
alias: 'v',
|
|
57
63
|
type: 'boolean',
|
|
64
|
+
default: false,
|
|
58
65
|
description: "Run with verbose logging."
|
|
59
66
|
})
|
|
60
67
|
.option('fixedSpacing', {
|
|
@@ -118,6 +125,6 @@ if (cluster.isPrimary) { // Parent process with portal webserver through which c
|
|
|
118
125
|
|
|
119
126
|
} else { // A portal node through which client's can connect.
|
|
120
127
|
const portalNode = await import('../portals/node.js');
|
|
121
|
-
const {baseURL, externalBaseURL, fixedSpacing, variableSpacing, verbose} = argv;
|
|
122
|
-
await portalNode.setup({baseURL, externalBaseURL, fixedSpacing, variableSpacing, verbose});
|
|
128
|
+
const {baseURL, externalBaseURL, fixedSpacing, variableSpacing, info, verbose} = argv;
|
|
129
|
+
await portalNode.setup({baseURL, externalBaseURL, fixedSpacing, variableSpacing, info, debug: verbose});
|
|
123
130
|
}
|
|
@@ -10,18 +10,19 @@ import path from 'path';
|
|
|
10
10
|
|
|
11
11
|
describe("DHT write/read", function () {
|
|
12
12
|
let contact, portalProcess, botProcess;
|
|
13
|
+
const botInfo = false;
|
|
13
14
|
const verbose = false;
|
|
15
|
+
const testNodeVerbose = verbose;
|
|
14
16
|
const baseURL = 'http://localhost:3000/kdht';
|
|
15
17
|
const logicalCores = availableParallelism();
|
|
16
18
|
console.log(`Model description "${cpus()[0].model}", ${logicalCores} logical cores.`);
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const nBots =
|
|
19
|
+
const nPortals = Math.max(2, logicalCores - 1);
|
|
20
|
+
const thrash = false;
|
|
21
|
+
const nBots = Math.max(2, (thrash ? 0.5 : 1) * logicalCores);
|
|
20
22
|
const fixedSpacing = 2; // Between portals.
|
|
21
23
|
const variableSpacing = 5; // Additional random between portals.
|
|
22
24
|
const nWrites = 40;
|
|
23
25
|
const waitBeforeRead = 15e3;
|
|
24
|
-
const thrash = true;
|
|
25
26
|
const showPortals = true;
|
|
26
27
|
const showBots = true;
|
|
27
28
|
|
|
@@ -35,7 +36,7 @@ describe("DHT write/read", function () {
|
|
|
35
36
|
function echo(data) { data = data.slice(0, -1); console.log(data.toString()); }
|
|
36
37
|
|
|
37
38
|
console.log(new Date(), 'starting', nPortals, 'portals over', portalSeconds, 'seconds');
|
|
38
|
-
portalProcess = spawn('node', [path.resolve(__dirname, 'portal.js'), '--nPortals', nPortals, '--verbose', verbose.toString()]);
|
|
39
|
+
portalProcess = spawn('node', [path.resolve(__dirname, 'portal.js'), '--nPortals', nPortals, '--info', botInfo, '--verbose', verbose.toString()]);
|
|
39
40
|
if (showPortals) {
|
|
40
41
|
portalProcess.stdout.on('data', echo);
|
|
41
42
|
portalProcess.stderr.on('data', echo);
|
|
@@ -43,18 +44,16 @@ describe("DHT write/read", function () {
|
|
|
43
44
|
await Node.delay(portalSeconds * 1e3);
|
|
44
45
|
|
|
45
46
|
if (nBots) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
botProcess.stderr.on('data', echo);
|
|
52
|
-
}
|
|
53
|
-
await Node.delay(botsMilliseconds);
|
|
47
|
+
console.log(new Date(), 'starting', nBots, thrash ? 'thrashbots' : 'bots', 'over', botsMilliseconds/1e3, 'seconds');
|
|
48
|
+
botProcess = spawn('node', [path.resolve(__dirname, 'bots.js'), '--nBots', nBots, '--thrash', thrash.toString(), '--info', botInfo, '--verbose', verbose.toString()]);
|
|
49
|
+
if (showBots) {
|
|
50
|
+
botProcess.stdout.on('data', echo);
|
|
51
|
+
botProcess.stderr.on('data', echo);
|
|
54
52
|
}
|
|
53
|
+
await Node.delay(botsMilliseconds);
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
contact = await WebContact.create({name: uuidv4(), debug:
|
|
56
|
+
contact = await WebContact.create({name: uuidv4(), debug: testNodeVerbose});
|
|
58
57
|
const bootstrapName = await contact.fetchBootstrap(baseURL);
|
|
59
58
|
const bootstrapContact = await contact.ensureRemoteContact(bootstrapName, baseURL);
|
|
60
59
|
console.log(new Date(), 'client node', contact.sname, 'joining', bootstrapContact.sname);
|
|
@@ -69,7 +68,7 @@ describe("DHT write/read", function () {
|
|
|
69
68
|
await Node.delay(waitBeforeRead);
|
|
70
69
|
}
|
|
71
70
|
console.log(new Date(), 'Reading');
|
|
72
|
-
}, 5e3 * nWrites + (1 +
|
|
71
|
+
}, 5e3 * nWrites + (1 + nBots) * Node.refreshTimeIntervalMS);
|
|
73
72
|
afterAll(async function () {
|
|
74
73
|
contact.disconnect();
|
|
75
74
|
console.log(new Date(), 'killing portals and bots');
|
package/transports/contact.js
CHANGED
|
@@ -123,7 +123,7 @@ export class Contact {
|
|
|
123
123
|
distance(key) { return this.host.constructor.distance(this.key, key); }
|
|
124
124
|
|
|
125
125
|
// RPC
|
|
126
|
-
static
|
|
126
|
+
static maxPingMS = 330; // Not including connect time. These are single-hop WebRTC data channels.
|
|
127
127
|
serializeRequest(...rest) { // Return the composite datum suitable for transport over the wire.
|
|
128
128
|
return rest; // Non-simulation subclases must override.
|
|
129
129
|
}
|
|
@@ -140,7 +140,7 @@ export class Contact {
|
|
|
140
140
|
let hops = 15; // recursive calls
|
|
141
141
|
if (method === 'signals') hops = 2;
|
|
142
142
|
else if (['ping', 'findNodes', 'findValue', 'store'].includes(method)) hops = 1;
|
|
143
|
-
return Node.delay(hops * this.constructor.
|
|
143
|
+
return Node.delay(hops * this.constructor.maxPingMS, null);
|
|
144
144
|
}
|
|
145
145
|
async sendRPC(method, ...rest) { // Promise the result of a network call to node, or null if not possible.
|
|
146
146
|
const sender = this.host.contact;
|
|
@@ -208,6 +208,7 @@ export class Contact {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
// Signaling
|
|
211
|
+
static forwardingTimeoutMS = 3/2 * this.maxPingMS - 0.2 * this.maxPingMS;
|
|
211
212
|
async messageSignals(signals) { // send signals through the network, promising the response signals.
|
|
212
213
|
// If contact cannot be reached, remove it and promise [].
|
|
213
214
|
if (this.host.isStopped()) return [];
|
|
@@ -235,14 +236,15 @@ export class Contact {
|
|
|
235
236
|
|
|
236
237
|
if (this.host.isStopped()) return [];
|
|
237
238
|
if (this.node.isRunning)
|
|
238
|
-
this.host.
|
|
239
|
+
this.host.log('Using recursive signal routing to', this.sname, 'after trying', sponsors.length, 'sponsors.');
|
|
239
240
|
|
|
240
241
|
const start = Date.now();
|
|
241
|
-
const
|
|
242
|
+
const response = await this.host.recursiveSignals(this.key, payload, [], Date.now + this.constructor.forwardingTimeoutMS, this.name);
|
|
243
|
+
const {forwardingExclusions, result} = response || {};
|
|
242
244
|
const elapsed = Date.now() - start;
|
|
243
245
|
if (!!this.isRunning !== !!result) // Of course, only simulations can really know isRunning to be false.
|
|
244
|
-
this.host.ilog('Recursive
|
|
245
|
-
'in', forwardingExclusions?.length, 'steps over',
|
|
246
|
+
this.host.ilog('Recursive', response ? 'data from' : 'failure from', this.sname,
|
|
247
|
+
'in', forwardingExclusions?.length || 'unknown', 'steps over',
|
|
246
248
|
elapsed, 'ms, after trying',
|
|
247
249
|
sponsors.length, 'sponsors.',
|
|
248
250
|
);
|
|
@@ -263,8 +265,7 @@ export class Contact {
|
|
|
263
265
|
//return `${this.connection ? '_' : ''}${this.sname}v${this.counter}${this.isRunning ? '' : '*'}`;
|
|
264
266
|
return `${this.connection ? '_' : ''}${this.sname}${this.isRunning ? '' : '*'}`; // simpler version
|
|
265
267
|
}
|
|
266
|
-
static
|
|
267
|
-
static pingTimeMS = 40; // ms
|
|
268
|
+
static pingTimeMS = 40; // ms to consume each RPC in simulations
|
|
268
269
|
static async ensureTime(thunk, ms = this.pingTimeMS) { // Promise that thunk takes at least ms to execute.
|
|
269
270
|
const start = Date.now();
|
|
270
271
|
const result = await thunk();
|
package/transports/webrtc.js
CHANGED
|
@@ -12,8 +12,9 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
|
|
|
12
12
|
get isServerNode() { return this.node.isServerNode; } // It it reachable through a server.
|
|
13
13
|
|
|
14
14
|
checkResponse(response) { // Return a fetch response, or throw error if response is not a 200 series.
|
|
15
|
-
if (
|
|
16
|
-
|
|
15
|
+
if (response?.ok) return true;
|
|
16
|
+
this.host.xlog(`*** Unable to reach portal ${response?.url || this.sname}, ${response?.status || 'failed fetch'}: ${response?.statusText || 'Unknown reason'}. ***`);
|
|
17
|
+
return false;
|
|
17
18
|
}
|
|
18
19
|
// connection:close is far more robust against pooling issues common to some implementations (e.g., NodeJS).
|
|
19
20
|
// https://github.com/nodejs/undici/issues/3492
|
|
@@ -21,7 +22,7 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
|
|
|
21
22
|
// worker index or the string 'random' to an available sname to which we can connect().
|
|
22
23
|
const url = `${baseURL}/name/${label}`;
|
|
23
24
|
const response = await fetch(url, {headers: { 'Connection': 'close' } }).catch(e => this.host.xlog(e));
|
|
24
|
-
this.checkResponse(response);
|
|
25
|
+
if (!this.checkResponse(response)) return this.fetchBootstrap(baseURL, label);
|
|
25
26
|
return await response.json();
|
|
26
27
|
}
|
|
27
28
|
async fetchSignals(url, signalsToSend) {
|
|
@@ -30,7 +31,7 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
|
|
|
30
31
|
headers: { 'Content-Type': 'application/json', 'Connection': 'close' },
|
|
31
32
|
body: JSON.stringify(signalsToSend)
|
|
32
33
|
}).catch(e => this.host.xlog(e));
|
|
33
|
-
this.checkResponse(response);
|
|
34
|
+
if (!this.checkResponse(response)) return this.fetchSignals(url, signalsToSend);
|
|
34
35
|
return this.checkSignals(await response?.json());
|
|
35
36
|
}
|
|
36
37
|
async signals(senderSname, ...signals) { // Accept directed WebRTC signals from a sender sname, creating if necessary the
|
|
@@ -38,18 +39,18 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
|
|
|
38
39
|
//this.host.xlog('contact signals', senderSname, signals);
|
|
39
40
|
let contact = await this.ensureRemoteContact(senderSname);
|
|
40
41
|
|
|
41
|
-
if (contact.webrtc) return await contact.webrtc.respond(signals);
|
|
42
|
+
if (contact.webrtc?.pc) return await contact.webrtc.respond(signals);
|
|
42
43
|
|
|
43
44
|
this.host.noteContactForTransport(contact);
|
|
44
|
-
contact.
|
|
45
|
+
contact.createWebRTC(false);
|
|
45
46
|
return await contact.webrtc.respond(signals);
|
|
46
47
|
}
|
|
47
48
|
get webrtcLabel() {
|
|
48
49
|
return `@${this.host.contact.sname} ==> ${this.sname}`;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
//
|
|
52
|
+
createWebRTC(initiate = false, timeoutMS = this.host.timeoutMS || 30e3) { // Ensure we are connected, if possible.
|
|
53
|
+
// Sets up contact to have properties:
|
|
53
54
|
// - connection - a promise for an open webrtc data channel:
|
|
54
55
|
// this.send(string) puts data on the channel
|
|
55
56
|
// incomming messages are dispatched to receiveWebRTC(string)
|
|
@@ -73,7 +74,7 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
|
|
|
73
74
|
this.webrtc = this.connection = this.unsafeData = null;
|
|
74
75
|
};
|
|
75
76
|
if (initiate) {
|
|
76
|
-
if (bootstrapHost
|
|
77
|
+
if (bootstrapHost && !host.connections.length) {
|
|
77
78
|
const url = `${bootstrapHost || 'http://localhost:3000/kdht'}/join/${host.contact.sname}/${this.sname}`;
|
|
78
79
|
this.webrtc.transferSignals = signals => this.fetchSignals(url, signals);
|
|
79
80
|
} else {
|
|
@@ -101,6 +102,7 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
|
|
|
101
102
|
}
|
|
102
103
|
const timerPromise = new Promise(expired => {
|
|
103
104
|
timeout = setTimeout(async () => {
|
|
105
|
+
if (this.host.isStopped()) return;
|
|
104
106
|
const now = Date.now();
|
|
105
107
|
this.host.ilog('Unable to connect to', this.sname);
|
|
106
108
|
// this.host.xlog('**** connection timeout', this.sname, now - start,
|
|
@@ -125,9 +127,8 @@ export class WebContact extends Contact { // Our wrapper for the means of contac
|
|
|
125
127
|
// Anyone can connect to a server node using the server's connect endpoint.
|
|
126
128
|
// Anyone in the DHT can connect to another DHT node through a sponsor.
|
|
127
129
|
if (contact.connection) return contact.connection;
|
|
128
|
-
contact.
|
|
129
|
-
await this.connection;
|
|
130
|
-
return this.connection;
|
|
130
|
+
contact.createWebRTC(true);
|
|
131
|
+
return await this.connection;
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
async send(message) { // Promise to send through previously opened connection promise.
|