dignity.js 0.1.2 → 0.3.0
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/README.md +35 -16
- package/dist/dignity.cjs.js +6880 -43
- package/dist/dignity.cjs.js.map +4 -4
- package/dist/dignity.esm.js +6894 -43
- package/dist/dignity.esm.js.map +4 -4
- package/dist/dignity.min.js +42 -1
- package/package.json +4 -2
- package/src/index.js +2 -0
- package/src/security/message-security-service.js +67 -10
- package/src/signaling/create-default-signaling-pool.js +5 -1
- package/src/signaling/default-signaling-config.js +2 -3
- package/src/signaling/peerjs-signaling-provider.js +210 -0
- package/src/signaling/websocket-signaling-provider.js +24 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dignity.js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "P2P object API for decentralized JavaScript applications",
|
|
5
5
|
"main": "dist/dignity.cjs.js",
|
|
6
6
|
"module": "dist/dignity.esm.js",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"scripts": {
|
|
24
24
|
"test": "jest --coverage",
|
|
25
25
|
"test:unit": "jest tests/unit --runInBand",
|
|
26
|
+
"test:cloudflare-live": "RUN_CLOUDFLARE_LIVE_TESTS=1 jest tests/integration/cloudflare-signaling-live.test.js --runInBand",
|
|
26
27
|
"test:pow-calibrate": "jest tests/unit/sloth-vdf-timing.test.js --runInBand",
|
|
27
28
|
"build": "node scripts/build.js",
|
|
28
29
|
"docs:serve": "npx http-server docs -p 4173 -o",
|
|
@@ -38,12 +39,13 @@
|
|
|
38
39
|
"rest",
|
|
39
40
|
"objects"
|
|
40
41
|
],
|
|
41
|
-
"license": "
|
|
42
|
+
"license": "Apache 2.0",
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"esbuild": "^0.28.0",
|
|
44
45
|
"jest": "^29.7.0"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
48
|
+
"peerjs": "^1.5.5",
|
|
47
49
|
"tweetnacl": "^1.0.3",
|
|
48
50
|
"tweetnacl-util": "^0.15.1"
|
|
49
51
|
},
|
package/src/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const DignityP2P = require('./core/dignity-p2p');
|
|
|
10
10
|
const createDefaultSignalingPool = require('./signaling/create-default-signaling-pool');
|
|
11
11
|
const SignalingPool = require('./signaling/signaling-pool');
|
|
12
12
|
const WebSocketSignalingProvider = require('./signaling/websocket-signaling-provider');
|
|
13
|
+
const PeerJSSignalingProvider = require('./signaling/peerjs-signaling-provider');
|
|
13
14
|
const {
|
|
14
15
|
InMemoryNetworkHub,
|
|
15
16
|
InMemoryNetworkAdapter
|
|
@@ -30,6 +31,7 @@ module.exports = {
|
|
|
30
31
|
createDefaultSignalingPool,
|
|
31
32
|
SignalingPool,
|
|
32
33
|
WebSocketSignalingProvider,
|
|
34
|
+
PeerJSSignalingProvider,
|
|
33
35
|
InMemoryNetworkHub,
|
|
34
36
|
InMemoryNetworkAdapter,
|
|
35
37
|
DEFAULT_CLOUDFLARE_SIGNALING_URLS,
|
|
@@ -12,7 +12,8 @@ const DEFAULT_SECURITY_OPTIONS = {
|
|
|
12
12
|
broadcastPasswords: {},
|
|
13
13
|
resolveBroadcastPassword: null,
|
|
14
14
|
powSteps: 22,
|
|
15
|
-
trustedPeerKeys: {}
|
|
15
|
+
trustedPeerKeys: {},
|
|
16
|
+
kdfIterations: 100000
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
function stableStringify(value) {
|
|
@@ -47,6 +48,37 @@ function utf8ToBytes(value) {
|
|
|
47
48
|
return naclUtil.decodeUTF8(value);
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
async function deriveBroadcastKey(password, salt, iterations) {
|
|
52
|
+
const subtle = globalThis.crypto && globalThis.crypto.subtle;
|
|
53
|
+
|
|
54
|
+
if (subtle) {
|
|
55
|
+
const keyMaterial = await subtle.importKey(
|
|
56
|
+
'raw',
|
|
57
|
+
utf8ToBytes(password),
|
|
58
|
+
'PBKDF2',
|
|
59
|
+
false,
|
|
60
|
+
['deriveBits']
|
|
61
|
+
);
|
|
62
|
+
const bits = await subtle.deriveBits(
|
|
63
|
+
{ name: 'PBKDF2', salt, iterations, hash: 'SHA-256' },
|
|
64
|
+
keyMaterial,
|
|
65
|
+
256
|
|
66
|
+
);
|
|
67
|
+
return new Uint8Array(bits);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const { pbkdf2Sync } = require('crypto');
|
|
72
|
+
return new Uint8Array(pbkdf2Sync(password, Buffer.from(salt), iterations, 32, 'sha256'));
|
|
73
|
+
} catch (_ignored) {
|
|
74
|
+
return hash32(concatBytes(utf8ToBytes(password), salt));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function legacyBroadcastKey(password, salt) {
|
|
79
|
+
return hash32(concatBytes(utf8ToBytes(password), salt));
|
|
80
|
+
}
|
|
81
|
+
|
|
50
82
|
function normalizePeerPublicKey(publicKey) {
|
|
51
83
|
if (!publicKey || typeof publicKey !== 'object') {
|
|
52
84
|
throw new Error('Public key must be an object with signingPublicKey and encryptionPublicKey');
|
|
@@ -153,6 +185,7 @@ class MessageSecurityService {
|
|
|
153
185
|
const pow = await this.generatePow(envelope);
|
|
154
186
|
envelope.security.pow = {
|
|
155
187
|
enabled: true,
|
|
188
|
+
messageHash: pow.messageHash,
|
|
156
189
|
challenge: pow.challenge,
|
|
157
190
|
proof: pow.proof,
|
|
158
191
|
steps: pow.steps,
|
|
@@ -208,6 +241,10 @@ class MessageSecurityService {
|
|
|
208
241
|
});
|
|
209
242
|
}
|
|
210
243
|
|
|
244
|
+
computePowMessageHash(envelope) {
|
|
245
|
+
return bytesToHex(hash32(utf8ToBytes(this.canonicalPowInput(envelope))));
|
|
246
|
+
}
|
|
247
|
+
|
|
211
248
|
async decryptIncomingMessage(envelope) {
|
|
212
249
|
if (!this.options.enabled) {
|
|
213
250
|
return {
|
|
@@ -235,7 +272,7 @@ class MessageSecurityService {
|
|
|
235
272
|
this.verifySignature(envelope);
|
|
236
273
|
}
|
|
237
274
|
|
|
238
|
-
const payload = this.decryptPayload(envelope);
|
|
275
|
+
const payload = await this.decryptPayload(envelope);
|
|
239
276
|
|
|
240
277
|
return {
|
|
241
278
|
ignored: false,
|
|
@@ -315,7 +352,8 @@ class MessageSecurityService {
|
|
|
315
352
|
const nonce = nacl.randomBytes(nacl.secretbox.nonceLength);
|
|
316
353
|
const salt = nacl.randomBytes(16);
|
|
317
354
|
const password = this.resolveBroadcastPassword(scope);
|
|
318
|
-
const
|
|
355
|
+
const iterations = this.options.kdfIterations || DEFAULT_SECURITY_OPTIONS.kdfIterations;
|
|
356
|
+
const key = await deriveBroadcastKey(password, salt, iterations);
|
|
319
357
|
const encrypted = nacl.secretbox(plainText, nonce, key);
|
|
320
358
|
|
|
321
359
|
return {
|
|
@@ -325,12 +363,14 @@ class MessageSecurityService {
|
|
|
325
363
|
mode: 'broadcast',
|
|
326
364
|
scope,
|
|
327
365
|
nonce: naclUtil.encodeBase64(nonce),
|
|
328
|
-
salt: naclUtil.encodeBase64(salt)
|
|
366
|
+
salt: naclUtil.encodeBase64(salt),
|
|
367
|
+
kdf: 'pbkdf2',
|
|
368
|
+
kdfIterations: iterations
|
|
329
369
|
}
|
|
330
370
|
};
|
|
331
371
|
}
|
|
332
372
|
|
|
333
|
-
decryptPayload(envelope) {
|
|
373
|
+
async decryptPayload(envelope) {
|
|
334
374
|
const encryption = envelope.security ? envelope.security.encryption : null;
|
|
335
375
|
|
|
336
376
|
if (!encryption || !encryption.enabled || !this.options.encryptionEnabled) {
|
|
@@ -344,7 +384,15 @@ class MessageSecurityService {
|
|
|
344
384
|
const password = this.resolveBroadcastPassword(scope);
|
|
345
385
|
const salt = naclUtil.decodeBase64(encryption.salt);
|
|
346
386
|
const nonce = naclUtil.decodeBase64(encryption.nonce);
|
|
347
|
-
|
|
387
|
+
|
|
388
|
+
let key;
|
|
389
|
+
if (encryption.kdf === 'pbkdf2') {
|
|
390
|
+
const iterations = encryption.kdfIterations || DEFAULT_SECURITY_OPTIONS.kdfIterations;
|
|
391
|
+
key = await deriveBroadcastKey(password, salt, iterations);
|
|
392
|
+
} else {
|
|
393
|
+
key = legacyBroadcastKey(password, salt);
|
|
394
|
+
}
|
|
395
|
+
|
|
348
396
|
const decrypted = nacl.secretbox.open(encryptedBuffer, nonce, key);
|
|
349
397
|
|
|
350
398
|
if (!decrypted) {
|
|
@@ -420,13 +468,15 @@ class MessageSecurityService {
|
|
|
420
468
|
}
|
|
421
469
|
|
|
422
470
|
async generatePow(envelope) {
|
|
423
|
-
const
|
|
471
|
+
const messageHash = this.computePowMessageHash(envelope);
|
|
472
|
+
const challenge = messageHash;
|
|
424
473
|
const steps = await this.determinePowSteps();
|
|
425
474
|
const start = this.now();
|
|
426
475
|
const proof = await VDF.compute(challenge, steps);
|
|
427
476
|
const durationMs = this.now() - start;
|
|
428
477
|
|
|
429
478
|
return {
|
|
479
|
+
messageHash,
|
|
430
480
|
challenge,
|
|
431
481
|
proof,
|
|
432
482
|
steps: steps.toString(),
|
|
@@ -435,16 +485,21 @@ class MessageSecurityService {
|
|
|
435
485
|
}
|
|
436
486
|
|
|
437
487
|
async verifyPow(envelope) {
|
|
438
|
-
const
|
|
488
|
+
const expectedMessageHash = this.computePowMessageHash(envelope);
|
|
439
489
|
const pow = envelope.security.pow;
|
|
440
490
|
|
|
441
|
-
if (
|
|
491
|
+
if (
|
|
492
|
+
!pow ||
|
|
493
|
+
!pow.messageHash ||
|
|
494
|
+
pow.messageHash !== expectedMessageHash ||
|
|
495
|
+
pow.challenge !== pow.messageHash
|
|
496
|
+
) {
|
|
442
497
|
const error = new Error('PoW challenge mismatch');
|
|
443
498
|
error.code = 'INVALID_POW';
|
|
444
499
|
throw error;
|
|
445
500
|
}
|
|
446
501
|
|
|
447
|
-
const verified = await VDF.verify(pow.
|
|
502
|
+
const verified = await VDF.verify(pow.messageHash, BigInt(pow.steps), pow.proof);
|
|
448
503
|
if (!verified) {
|
|
449
504
|
const error = new Error('PoW verification failed');
|
|
450
505
|
error.code = 'INVALID_POW';
|
|
@@ -458,5 +513,7 @@ class MessageSecurityService {
|
|
|
458
513
|
module.exports = {
|
|
459
514
|
MessageSecurityService,
|
|
460
515
|
stableStringify,
|
|
516
|
+
deriveBroadcastKey,
|
|
517
|
+
legacyBroadcastKey,
|
|
461
518
|
DEFAULT_SECURITY_OPTIONS
|
|
462
519
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const SignalingPool = require('./signaling-pool');
|
|
2
2
|
const WebSocketSignalingProvider = require('./websocket-signaling-provider');
|
|
3
|
+
const PeerJSSignalingProvider = require('./peerjs-signaling-provider');
|
|
3
4
|
const {
|
|
4
5
|
DEFAULT_CLOUDFLARE_SIGNALING_URLS,
|
|
5
6
|
DEFAULT_SIGNALING_FALLBACK_URLS
|
|
@@ -13,8 +14,11 @@ function createDefaultSignalingPool(options = {}) {
|
|
|
13
14
|
const providers = [];
|
|
14
15
|
|
|
15
16
|
cloudflareUrls.forEach((url, index) => {
|
|
17
|
+
const usePeerJsProvider = /^wss:\/\/(peerjs\.92k\.de|0\.peerjs\.com)(\/|$)/.test(url);
|
|
18
|
+
const ProviderClass = usePeerJsProvider ? PeerJSSignalingProvider : WebSocketSignalingProvider;
|
|
19
|
+
|
|
16
20
|
providers.push(
|
|
17
|
-
new
|
|
21
|
+
new ProviderClass({
|
|
18
22
|
id: `cloudflare-${index + 1}`,
|
|
19
23
|
url,
|
|
20
24
|
WebSocketImpl,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const DEFAULT_CLOUDFLARE_SIGNALING_URLS = [
|
|
2
|
-
'wss://
|
|
3
|
-
'wss://
|
|
4
|
-
'wss://trycloudflare-signaling.example'
|
|
2
|
+
'wss://peerjs.92k.de/peerjs?key=peerjs',
|
|
3
|
+
'wss://0.peerjs.com/peerjs?key=peerjs'
|
|
5
4
|
];
|
|
6
5
|
|
|
7
6
|
const DEFAULT_SIGNALING_FALLBACK_URLS = [
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
const WebSocketSignalingProvider = require('./websocket-signaling-provider');
|
|
2
|
+
|
|
3
|
+
class PeerJSSignalingProvider {
|
|
4
|
+
constructor({ id, url, PeerImpl, WebSocketImpl, priority = 0, connectTimeoutMs = 10000 }) {
|
|
5
|
+
if (!url) {
|
|
6
|
+
throw new Error('PeerJS signaling provider requires a url');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
this.id = id || url;
|
|
10
|
+
this.url = url;
|
|
11
|
+
this.priority = priority;
|
|
12
|
+
this.isCustomPeerImpl = Boolean(PeerImpl);
|
|
13
|
+
this.PeerImpl = PeerImpl || this.resolvePeerImplementation();
|
|
14
|
+
this.WebSocketImpl = WebSocketImpl;
|
|
15
|
+
this.connectTimeoutMs = connectTimeoutMs;
|
|
16
|
+
this.peer = null;
|
|
17
|
+
this.peerId = null;
|
|
18
|
+
this.connections = new Map();
|
|
19
|
+
this.messageHandlers = new Set();
|
|
20
|
+
this.fallbackProvider = null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
resolvePeerImplementation() {
|
|
24
|
+
try {
|
|
25
|
+
const peerjs = require('peerjs');
|
|
26
|
+
return peerjs.Peer || peerjs;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
parsePeerJsServerUrl() {
|
|
33
|
+
const parsed = new URL(this.url);
|
|
34
|
+
const secure = parsed.protocol === 'wss:';
|
|
35
|
+
const host = parsed.hostname;
|
|
36
|
+
const port = parsed.port ? Number(parsed.port) : secure ? 443 : 80;
|
|
37
|
+
const path = parsed.pathname || '/';
|
|
38
|
+
const key = parsed.searchParams.get('key') || 'peerjs';
|
|
39
|
+
|
|
40
|
+
return { secure, host, port, path, key };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
shouldUseWebSocketFallback() {
|
|
44
|
+
return !this.isCustomPeerImpl && typeof globalThis.RTCPeerConnection !== 'function';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
useWebSocketFallback() {
|
|
48
|
+
if (!this.fallbackProvider) {
|
|
49
|
+
this.fallbackProvider = new WebSocketSignalingProvider({
|
|
50
|
+
id: `${this.id}-ws-fallback`,
|
|
51
|
+
url: this.url,
|
|
52
|
+
WebSocketImpl: this.WebSocketImpl,
|
|
53
|
+
priority: this.priority
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return this.fallbackProvider;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async connect() {
|
|
60
|
+
if (this.shouldUseWebSocketFallback()) {
|
|
61
|
+
await this.useWebSocketFallback().connect();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!this.PeerImpl) {
|
|
66
|
+
throw new Error('PeerJS implementation is not available');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const server = this.parsePeerJsServerUrl();
|
|
70
|
+
const peerId = `dignityjs_${Math.random().toString(36).slice(2, 12)}`;
|
|
71
|
+
|
|
72
|
+
await new Promise((resolve, reject) => {
|
|
73
|
+
const peer = new this.PeerImpl(peerId, {
|
|
74
|
+
host: server.host,
|
|
75
|
+
port: server.port,
|
|
76
|
+
path: server.path,
|
|
77
|
+
secure: server.secure,
|
|
78
|
+
key: server.key
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const timeout = setTimeout(() => {
|
|
82
|
+
reject(new Error(`Unable to connect to signaling url ${this.url}`));
|
|
83
|
+
}, this.connectTimeoutMs);
|
|
84
|
+
|
|
85
|
+
peer.on('open', () => {
|
|
86
|
+
clearTimeout(timeout);
|
|
87
|
+
this.peer = peer;
|
|
88
|
+
this.peerId = peerId;
|
|
89
|
+
resolve();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
peer.on('connection', (connection) => {
|
|
93
|
+
this.attachConnectionHandlers(connection);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
peer.on('error', async (error) => {
|
|
97
|
+
clearTimeout(timeout);
|
|
98
|
+
if (error && error.type === 'browser-incompatible') {
|
|
99
|
+
try {
|
|
100
|
+
await this.useWebSocketFallback().connect();
|
|
101
|
+
resolve();
|
|
102
|
+
return;
|
|
103
|
+
} catch (fallbackError) {
|
|
104
|
+
reject(new Error(`Unable to connect to signaling url ${this.url}`));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
reject(new Error(`Unable to connect to signaling url ${this.url}`));
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
attachConnectionHandlers(connection) {
|
|
114
|
+
const remoteId = connection.peer;
|
|
115
|
+
this.connections.set(remoteId, connection);
|
|
116
|
+
|
|
117
|
+
connection.on('data', (payload) => {
|
|
118
|
+
for (const handler of this.messageHandlers) {
|
|
119
|
+
handler(payload);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
connection.on('close', () => {
|
|
124
|
+
this.connections.delete(remoteId);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async openConnection(remotePeerId) {
|
|
129
|
+
if (!this.peer) {
|
|
130
|
+
throw new Error('PeerJS is not connected');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const existing = this.connections.get(remotePeerId);
|
|
134
|
+
if (existing && existing.open) {
|
|
135
|
+
return existing;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return await new Promise((resolve, reject) => {
|
|
139
|
+
const connection = this.peer.connect(remotePeerId, { reliable: true, serialization: 'json' });
|
|
140
|
+
const timeout = setTimeout(() => {
|
|
141
|
+
reject(new Error(`Unable to connect peer ${remotePeerId} via ${this.url}`));
|
|
142
|
+
}, this.connectTimeoutMs);
|
|
143
|
+
|
|
144
|
+
connection.on('open', () => {
|
|
145
|
+
clearTimeout(timeout);
|
|
146
|
+
this.attachConnectionHandlers(connection);
|
|
147
|
+
resolve(connection);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
connection.on('error', () => {
|
|
151
|
+
clearTimeout(timeout);
|
|
152
|
+
reject(new Error(`Unable to connect peer ${remotePeerId} via ${this.url}`));
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
onMessage(handler) {
|
|
158
|
+
if (this.fallbackProvider) {
|
|
159
|
+
this.fallbackProvider.onMessage(handler);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
this.messageHandlers.add(handler);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async send(message) {
|
|
166
|
+
if (this.fallbackProvider) {
|
|
167
|
+
await this.fallbackProvider.send(message);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!this.peer) {
|
|
172
|
+
throw new Error(`Signaling socket is not open for ${this.url}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (message && message.to) {
|
|
176
|
+
const connection = await this.openConnection(message.to);
|
|
177
|
+
connection.send(message);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
for (const connection of this.connections.values()) {
|
|
182
|
+
if (connection.open) {
|
|
183
|
+
connection.send(message);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async disconnect() {
|
|
189
|
+
if (this.fallbackProvider) {
|
|
190
|
+
await this.fallbackProvider.disconnect();
|
|
191
|
+
this.fallbackProvider = null;
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (const connection of this.connections.values()) {
|
|
196
|
+
if (typeof connection.close === 'function') {
|
|
197
|
+
connection.close();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
this.connections.clear();
|
|
201
|
+
|
|
202
|
+
if (this.peer && typeof this.peer.destroy === 'function') {
|
|
203
|
+
this.peer.destroy();
|
|
204
|
+
}
|
|
205
|
+
this.peer = null;
|
|
206
|
+
this.peerId = null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = PeerJSSignalingProvider;
|
|
@@ -18,7 +18,7 @@ class WebSocketSignalingProvider {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
await new Promise((resolve, reject) => {
|
|
21
|
-
const socket = new this.WebSocketImpl(this.
|
|
21
|
+
const socket = new this.WebSocketImpl(this.buildConnectionUrl());
|
|
22
22
|
|
|
23
23
|
socket.onopen = () => {
|
|
24
24
|
this.socket = socket;
|
|
@@ -44,6 +44,29 @@ class WebSocketSignalingProvider {
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
buildConnectionUrl() {
|
|
48
|
+
const peerJsHostPattern = /^wss:\/\/(peerjs\.92k\.de|0\.peerjs\.com)(\/|$)/;
|
|
49
|
+
if (!peerJsHostPattern.test(this.url)) {
|
|
50
|
+
return this.url;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const connectionId = `dignityjs_${Math.random().toString(36).slice(2, 12)}`;
|
|
54
|
+
const token = Math.random().toString(36).slice(2, 12);
|
|
55
|
+
const hasQuery = this.url.includes('?');
|
|
56
|
+
const hasId = /[?&]id=/.test(this.url);
|
|
57
|
+
const hasToken = /[?&]token=/.test(this.url);
|
|
58
|
+
|
|
59
|
+
let url = this.url;
|
|
60
|
+
if (!hasId) {
|
|
61
|
+
url += `${hasQuery ? '&' : '?'}id=${connectionId}`;
|
|
62
|
+
}
|
|
63
|
+
if (!hasToken) {
|
|
64
|
+
url += `${url.includes('?') ? '&' : '?'}token=${token}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return url;
|
|
68
|
+
}
|
|
69
|
+
|
|
47
70
|
onMessage(handler) {
|
|
48
71
|
this.messageHandlers.add(handler);
|
|
49
72
|
}
|