vrpc 3.8.3 → 3.9.1
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/browser/vrpc.js +3 -3
- package/package.json +3 -2
- package/vrpc/VrpcAdapter.js +50 -3
- package/vrpc/VrpcAgent.js +22 -5
- package/vrpc/VrpcBrowserAdapter.js +50 -3
- package/vrpc/VrpcBrowserAgent.js +8 -3
- package/vrpc/VrpcClient.js +42 -19
- package/vrpc/VrpcPersistor.js +191 -53
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vrpc",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.1",
|
|
4
4
|
"description": "Variadic Remote Procedure Calls",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -12,13 +12,14 @@
|
|
|
12
12
|
"build:addon": "ln -sf binding.gyp.no-auto binding.gyp && node-gyp rebuild && rm binding.gyp",
|
|
13
13
|
"build:browser": "./node_modules/.bin/webpack --stats-error-details",
|
|
14
14
|
"build:doc": "./node_modules/.bin/jsdoc2md -f vrpc/VrpcAdapter.js vrpc/VrpcAgent.js vrpc/VrpcClient.js vrpc/VrpcNative.js --param-list-format=list --separators > docs/api.md",
|
|
15
|
-
"test": "npm run test:adapter && npm run test:agent && npm run test:client && npm run test:persistor && npm run test:production",
|
|
15
|
+
"test": "npm run test:adapter && npm run test:agent && npm run test:client && npm run test:persistor && npm run test:lifecycle && npm run test:production",
|
|
16
16
|
"test:adapter": "./node_modules/.bin/mocha tests/adapter/*.js --exit",
|
|
17
17
|
"test:native": "./node_modules/.bin/mocha tests/native/*.js --exit",
|
|
18
18
|
"test:agent": "tests/agent/test.sh",
|
|
19
19
|
"test:client": "tests/client/test.sh",
|
|
20
20
|
"test:performance": "tests/performance/test.sh",
|
|
21
21
|
"test:persistor": "./node_modules/.bin/mocha tests/persistor/*.js --timeout 30000 --exit",
|
|
22
|
+
"test:lifecycle": "./node_modules/.bin/mocha tests/lifecycle/*.js --timeout 30000 --exit",
|
|
22
23
|
"test:production": "./node_modules/.bin/mocha tests/production/lifeCycleTest.js --timeout 30000 --exit && tests/production/test.sh"
|
|
23
24
|
},
|
|
24
25
|
"bin": {
|
package/vrpc/VrpcAdapter.js
CHANGED
|
@@ -9,12 +9,10 @@ __\/\\\_______\/\\\__/\\\///////\\\___\/\\\/////////\\\____/\\\////////__
|
|
|
9
9
|
_______\//\\\_______\/\\\______\//\\\_\/\\\_______________\////\\\\\\\\\_
|
|
10
10
|
________\///________\///________\///__\///___________________\/////////__
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
Non-intrusively adapts code and provides access in form of asynchronous remote
|
|
14
13
|
procedure calls (RPC).
|
|
15
14
|
Author: Dr. Burkhard C. Heisen (https://github.com/heisenware/vrpc)
|
|
16
15
|
|
|
17
|
-
|
|
18
16
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
|
19
17
|
Copyright (c) 2018 - 2022 Dr. Burkhard C. Heisen <burkhard.heisen@heisenware.com>.
|
|
20
18
|
|
|
@@ -419,16 +417,45 @@ class VrpcAdapter {
|
|
|
419
417
|
return this._mustTrackClient
|
|
420
418
|
}
|
|
421
419
|
|
|
420
|
+
/**
|
|
421
|
+
* Refuses a request on an isolated instance that belongs to another
|
|
422
|
+
* connection. An isolated instance created over the wire remembers the
|
|
423
|
+
* connection that created it (the request's sender) and answers that
|
|
424
|
+
* connection alone; one the agent created itself has no owner and is
|
|
425
|
+
* open. The agent's own housekeeping passes as VrpcAdapter.LOCAL_SENDER,
|
|
426
|
+
* which no wire message can carry.
|
|
427
|
+
*/
|
|
428
|
+
static _assertOwner (entry, json, instanceId) {
|
|
429
|
+
if (!entry || !entry.isIsolated || entry.owner === undefined) return
|
|
430
|
+
if (json.s === VrpcAdapter.LOCAL_SENDER || json.s === entry.owner) return
|
|
431
|
+
throw new Error(
|
|
432
|
+
`Instance '${instanceId}' is isolated to another connection`
|
|
433
|
+
)
|
|
434
|
+
}
|
|
435
|
+
|
|
422
436
|
static _handleCreateIsolated (json) {
|
|
423
437
|
let instance
|
|
424
438
|
try {
|
|
425
439
|
const className = json.c
|
|
426
440
|
const [instanceId, ...args] = json.a
|
|
441
|
+
const existing = VrpcAdapter._instances.get(instanceId)
|
|
442
|
+
if (existing) {
|
|
443
|
+
// an id is never re-homed: a shared instance stays shared, an
|
|
444
|
+
// isolated one keeps its owner
|
|
445
|
+
if (!existing.isIsolated) {
|
|
446
|
+
throw new Error(`Instance '${instanceId}' exists as a shared instance`)
|
|
447
|
+
}
|
|
448
|
+
VrpcAdapter._assertOwner(existing, json, instanceId)
|
|
449
|
+
json.r = instanceId
|
|
450
|
+
return existing.instance
|
|
451
|
+
}
|
|
427
452
|
instance = VrpcAdapter._create(className, instanceId, ...args)
|
|
428
453
|
VrpcAdapter._instances.set(instanceId, {
|
|
429
454
|
instance,
|
|
430
455
|
className,
|
|
431
|
-
isIsolated: true
|
|
456
|
+
isIsolated: true,
|
|
457
|
+
// undefined for instances the agent creates itself (open)
|
|
458
|
+
owner: typeof json.s === 'string' ? json.s : undefined
|
|
432
459
|
})
|
|
433
460
|
VrpcAdapter._emitter.emit('create', {
|
|
434
461
|
args,
|
|
@@ -449,6 +476,10 @@ class VrpcAdapter {
|
|
|
449
476
|
try {
|
|
450
477
|
const className = json.c
|
|
451
478
|
const [instanceId, ...args] = json.a
|
|
479
|
+
const existing = VrpcAdapter._instances.get(instanceId)
|
|
480
|
+
if (existing && existing.isIsolated) {
|
|
481
|
+
throw new Error(`Instance '${instanceId}' exists as an isolated instance`)
|
|
482
|
+
}
|
|
452
483
|
instance = VrpcAdapter._create(className, instanceId, ...args)
|
|
453
484
|
VrpcAdapter._instances.set(instanceId, {
|
|
454
485
|
instance,
|
|
@@ -524,6 +555,7 @@ class VrpcAdapter {
|
|
|
524
555
|
static _handleDelete (json) {
|
|
525
556
|
try {
|
|
526
557
|
const instance = json.a[0] // first arg is instance to be deleted
|
|
558
|
+
VrpcAdapter._assertOwner(VrpcAdapter._instances.get(instance), json, instance)
|
|
527
559
|
VrpcAdapter._emitter.emit('beforeDelete', {
|
|
528
560
|
instance,
|
|
529
561
|
className: json.c
|
|
@@ -540,6 +572,14 @@ class VrpcAdapter {
|
|
|
540
572
|
}
|
|
541
573
|
|
|
542
574
|
static _handleCall (json) {
|
|
575
|
+
// an isolated instance answers its creating connection only
|
|
576
|
+
try {
|
|
577
|
+
VrpcAdapter._assertOwner(VrpcAdapter._instances.get(json.c), json, json.c)
|
|
578
|
+
} catch (err) {
|
|
579
|
+
const { message, cause } = err
|
|
580
|
+
json.e = { message, cause }
|
|
581
|
+
return
|
|
582
|
+
}
|
|
543
583
|
// special case: removeAllListeners
|
|
544
584
|
if (json.f === 'removeAllListeners') {
|
|
545
585
|
VrpcAdapter._removeAllListeners(json.a[0], json.s, json.c)
|
|
@@ -971,4 +1011,11 @@ VrpcAdapter._blackList = new Set([
|
|
|
971
1011
|
'__lookupSetter__'
|
|
972
1012
|
])
|
|
973
1013
|
|
|
1014
|
+
/**
|
|
1015
|
+
* The sender the agent uses for its own housekeeping calls (deleting the
|
|
1016
|
+
* isolated instances of a departed connection). A Symbol: no message from
|
|
1017
|
+
* the wire can carry it, so it can never be claimed remotely.
|
|
1018
|
+
*/
|
|
1019
|
+
VrpcAdapter.LOCAL_SENDER = Symbol('vrpc.local-sender')
|
|
1020
|
+
|
|
974
1021
|
module.exports = VrpcAdapter
|
package/vrpc/VrpcAgent.js
CHANGED
|
@@ -614,7 +614,14 @@ class VrpcAgent extends EventEmitter {
|
|
|
614
614
|
// Intersecting life-cycle functions
|
|
615
615
|
switch (method) {
|
|
616
616
|
case '__createIsolated__': {
|
|
617
|
-
|
|
617
|
+
if (json.e) {
|
|
618
|
+
// a constructor that threw leaves nothing behind: no instance,
|
|
619
|
+
// no subscription, no lifetime bookkeeping
|
|
620
|
+
this._log.warn(
|
|
621
|
+
`Instantiation of ${className} failed: ${json.e.message}`
|
|
622
|
+
)
|
|
623
|
+
break
|
|
624
|
+
}
|
|
618
625
|
const instanceId = json.r
|
|
619
626
|
// TODO await this
|
|
620
627
|
this._subscribeToMethodsOfNewInstance(className, instanceId)
|
|
@@ -622,7 +629,12 @@ class VrpcAgent extends EventEmitter {
|
|
|
622
629
|
break
|
|
623
630
|
}
|
|
624
631
|
case '__createShared__': {
|
|
625
|
-
|
|
632
|
+
if (json.e) {
|
|
633
|
+
this._log.warn(
|
|
634
|
+
`Instantiation of ${className} failed: ${json.e.message}`
|
|
635
|
+
)
|
|
636
|
+
break
|
|
637
|
+
}
|
|
626
638
|
const instanceId = json.r
|
|
627
639
|
if (!this._hasSharedInstance(instanceId)) {
|
|
628
640
|
this._subscribeToMethodsOfNewInstance(className, instanceId)
|
|
@@ -663,7 +675,12 @@ class VrpcAgent extends EventEmitter {
|
|
|
663
675
|
const entry = this._isolatedInstances.get(connectionId)
|
|
664
676
|
if (entry) {
|
|
665
677
|
entry.forEach(instanceId => {
|
|
666
|
-
const json = {
|
|
678
|
+
const json = {
|
|
679
|
+
f: '__delete__',
|
|
680
|
+
a: [instanceId],
|
|
681
|
+
r: null,
|
|
682
|
+
s: VrpcAdapter.LOCAL_SENDER
|
|
683
|
+
}
|
|
667
684
|
VrpcAdapter._call(json)
|
|
668
685
|
if (json.r) {
|
|
669
686
|
this._log.debug(`Auto-deleted isolated instance: ${instanceId}`)
|
|
@@ -718,7 +735,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
718
735
|
const entryIsolated = this._isolatedInstances.get(clientId)
|
|
719
736
|
if (entryIsolated && entryIsolated.has(instanceId)) {
|
|
720
737
|
entryIsolated.delete(instanceId)
|
|
721
|
-
if (entryIsolated.
|
|
738
|
+
if (entryIsolated.size === 0) {
|
|
722
739
|
this._isolatedInstances.delete(clientId)
|
|
723
740
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
724
741
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
@@ -730,7 +747,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
730
747
|
if (v.has(instanceId)) {
|
|
731
748
|
found = true
|
|
732
749
|
v.delete(instanceId)
|
|
733
|
-
if (v.
|
|
750
|
+
if (v.size === 0) {
|
|
734
751
|
this._sharedInstances.delete(clientId)
|
|
735
752
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
736
753
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
@@ -9,12 +9,10 @@ __\/\\\_______\/\\\__/\\\///////\\\___\/\\\/////////\\\____/\\\////////__
|
|
|
9
9
|
_______\//\\\_______\/\\\______\//\\\_\/\\\_______________\////\\\\\\\\\_
|
|
10
10
|
________\///________\///________\///__\///___________________\/////////__
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
Non-intrusively adapts code and provides access in form of asynchronous remote
|
|
14
13
|
procedure calls (RPC).
|
|
15
14
|
Author: Dr. Burkhard C. Heisen (https://github.com/heisenware/vrpc)
|
|
16
15
|
|
|
17
|
-
|
|
18
16
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
|
19
17
|
Copyright (c) 2018 - 2022 Dr. Burkhard C. Heisen <burkhard.heisen@heisenware.com>.
|
|
20
18
|
|
|
@@ -319,16 +317,45 @@ class VrpcAdapter {
|
|
|
319
317
|
return VrpcAdapter._mustTrackClient
|
|
320
318
|
}
|
|
321
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Refuses a request on an isolated instance that belongs to another
|
|
322
|
+
* connection. An isolated instance created over the wire remembers the
|
|
323
|
+
* connection that created it (the request's sender) and answers that
|
|
324
|
+
* connection alone; one the agent created itself has no owner and is
|
|
325
|
+
* open. The agent's own housekeeping passes as VrpcAdapter.LOCAL_SENDER,
|
|
326
|
+
* which no wire message can carry.
|
|
327
|
+
*/
|
|
328
|
+
static _assertOwner (entry, json, instanceId) {
|
|
329
|
+
if (!entry || !entry.isIsolated || entry.owner === undefined) return
|
|
330
|
+
if (json.s === VrpcAdapter.LOCAL_SENDER || json.s === entry.owner) return
|
|
331
|
+
throw new Error(
|
|
332
|
+
`Instance '${instanceId}' is isolated to another connection`
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
|
|
322
336
|
static _handleCreateIsolated (json) {
|
|
323
337
|
let instance
|
|
324
338
|
try {
|
|
325
339
|
const className = json.c
|
|
326
340
|
const [instanceId, ...args] = json.a
|
|
341
|
+
const existing = VrpcAdapter._instances.get(instanceId)
|
|
342
|
+
if (existing) {
|
|
343
|
+
// an id is never re-homed: a shared instance stays shared, an
|
|
344
|
+
// isolated one keeps its owner
|
|
345
|
+
if (!existing.isIsolated) {
|
|
346
|
+
throw new Error(`Instance '${instanceId}' exists as a shared instance`)
|
|
347
|
+
}
|
|
348
|
+
VrpcAdapter._assertOwner(existing, json, instanceId)
|
|
349
|
+
json.r = instanceId
|
|
350
|
+
return existing.instance
|
|
351
|
+
}
|
|
327
352
|
instance = VrpcAdapter._create(className, instanceId, ...args)
|
|
328
353
|
VrpcAdapter._instances.set(instanceId, {
|
|
329
354
|
instance,
|
|
330
355
|
className,
|
|
331
|
-
isIsolated: true
|
|
356
|
+
isIsolated: true,
|
|
357
|
+
// undefined for instances the agent creates itself (open)
|
|
358
|
+
owner: typeof json.s === 'string' ? json.s : undefined
|
|
332
359
|
})
|
|
333
360
|
VrpcAdapter._emitter.emit('create', {
|
|
334
361
|
args,
|
|
@@ -348,6 +375,10 @@ class VrpcAdapter {
|
|
|
348
375
|
try {
|
|
349
376
|
const className = json.c
|
|
350
377
|
const [instanceId, ...args] = json.a
|
|
378
|
+
const existing = VrpcAdapter._instances.get(instanceId)
|
|
379
|
+
if (existing && existing.isIsolated) {
|
|
380
|
+
throw new Error(`Instance '${instanceId}' exists as an isolated instance`)
|
|
381
|
+
}
|
|
351
382
|
instance = VrpcAdapter._create(className, instanceId, ...args)
|
|
352
383
|
VrpcAdapter._instances.set(instanceId, {
|
|
353
384
|
instance,
|
|
@@ -421,6 +452,7 @@ class VrpcAdapter {
|
|
|
421
452
|
static _handleDelete (json) {
|
|
422
453
|
try {
|
|
423
454
|
const instance = json.a[0] // first arg is instance to be deleted
|
|
455
|
+
VrpcAdapter._assertOwner(VrpcAdapter._instances.get(instance), json, instance)
|
|
424
456
|
VrpcAdapter._emitter.emit('beforeDelete', {
|
|
425
457
|
instance,
|
|
426
458
|
className: json.c
|
|
@@ -436,6 +468,14 @@ class VrpcAdapter {
|
|
|
436
468
|
}
|
|
437
469
|
|
|
438
470
|
static _handleCall (json) {
|
|
471
|
+
// an isolated instance answers its creating connection only
|
|
472
|
+
try {
|
|
473
|
+
VrpcAdapter._assertOwner(VrpcAdapter._instances.get(json.c), json, json.c)
|
|
474
|
+
} catch (err) {
|
|
475
|
+
const { message, cause } = err
|
|
476
|
+
json.e = { message, cause }
|
|
477
|
+
return
|
|
478
|
+
}
|
|
439
479
|
// special case: removeAllListeners
|
|
440
480
|
if (json.f === 'removeAllListeners') {
|
|
441
481
|
VrpcAdapter._removeAllListeners(json.a[0], json.s, json.c)
|
|
@@ -877,4 +917,11 @@ VrpcAdapter._blackList = new Set([
|
|
|
877
917
|
'__lookupSetter__'
|
|
878
918
|
])
|
|
879
919
|
|
|
920
|
+
/**
|
|
921
|
+
* The sender the agent uses for its own housekeeping calls (deleting the
|
|
922
|
+
* isolated instances of a departed connection). A Symbol: no message from
|
|
923
|
+
* the wire can carry it, so it can never be claimed remotely.
|
|
924
|
+
*/
|
|
925
|
+
VrpcAdapter.LOCAL_SENDER = Symbol('vrpc.local-sender')
|
|
926
|
+
|
|
880
927
|
module.exports = VrpcAdapter
|
package/vrpc/VrpcBrowserAgent.js
CHANGED
|
@@ -504,7 +504,12 @@ class VrpcAgent extends EventEmitter {
|
|
|
504
504
|
const entry = this._isolatedInstances.get(connectionId)
|
|
505
505
|
if (entry) {
|
|
506
506
|
entry.forEach(instanceId => {
|
|
507
|
-
const json = {
|
|
507
|
+
const json = {
|
|
508
|
+
f: '__delete__',
|
|
509
|
+
a: [instanceId],
|
|
510
|
+
r: null,
|
|
511
|
+
s: VrpcAdapter.LOCAL_SENDER
|
|
512
|
+
}
|
|
508
513
|
VrpcAdapter._call(json)
|
|
509
514
|
if (json.r) {
|
|
510
515
|
this._log.debug(`Auto-deleted isolated instance: ${instanceId}`)
|
|
@@ -559,7 +564,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
559
564
|
const entryIsolated = this._isolatedInstances.get(clientId)
|
|
560
565
|
if (entryIsolated && entryIsolated.has(instanceId)) {
|
|
561
566
|
entryIsolated.delete(instanceId)
|
|
562
|
-
if (entryIsolated.
|
|
567
|
+
if (entryIsolated.size === 0) {
|
|
563
568
|
this._isolatedInstances.delete(clientId)
|
|
564
569
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
565
570
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
@@ -571,7 +576,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
571
576
|
if (v.has(instanceId)) {
|
|
572
577
|
found = true
|
|
573
578
|
v.delete(instanceId)
|
|
574
|
-
if (v.
|
|
579
|
+
if (v.size === 0) {
|
|
575
580
|
this._sharedInstances.delete(clientId)
|
|
576
581
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
577
582
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
package/vrpc/VrpcClient.js
CHANGED
|
@@ -9,12 +9,10 @@ __\/\\\_______\/\\\__/\\\///////\\\___\/\\\/////////\\\____/\\\////////__
|
|
|
9
9
|
_______\//\\\_______\/\\\______\//\\\_\/\\\_______________\////\\\\\\\\\_
|
|
10
10
|
________\///________\///________\///__\///___________________\/////////__
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
Non-intrusively adapts code and provides access in form of asynchronous remote
|
|
14
13
|
procedure calls (RPC).
|
|
15
14
|
Author: Dr. Burkhard C. Heisen (https://github.com/heisenware/vrpc)
|
|
16
15
|
|
|
17
|
-
|
|
18
16
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
|
19
17
|
Copyright (c) 2018 - 2022 Dr. Burkhard C. Heisen <burkhard.heisen@heisenware.com>.
|
|
20
18
|
|
|
@@ -113,6 +111,18 @@ class VrpcClient extends EventEmitter {
|
|
|
113
111
|
'The identity must NOT contain any of those characters: "+", "/", "#", "$"'
|
|
114
112
|
)
|
|
115
113
|
}
|
|
114
|
+
// mqtt client id sanity check: it becomes the second segment of every
|
|
115
|
+
// topic this client receives on, so it must be one plain segment
|
|
116
|
+
if (mqttClientId !== null && mqttClientId !== undefined) {
|
|
117
|
+
if (typeof mqttClientId !== 'string' || mqttClientId.length === 0) {
|
|
118
|
+
throw new Error('The mqttClientId must be a non-empty string')
|
|
119
|
+
}
|
|
120
|
+
if (mqttClientId.match(/[+/#]/)) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
'The mqttClientId must NOT contain any of those characters: "+", "/", "#"'
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
116
126
|
this._token = token
|
|
117
127
|
this._username = username
|
|
118
128
|
this._password = password
|
|
@@ -128,6 +138,11 @@ class VrpcClient extends EventEmitter {
|
|
|
128
138
|
this._instance = nanoid(8)
|
|
129
139
|
this._mqttClientId = mqttClientId || this._createMqttClientId()
|
|
130
140
|
this._vrpcClientId = this._createVrpcClientId()
|
|
141
|
+
// the part of the connection id nobody else can learn: a broker that
|
|
142
|
+
// confines a client to topics under its own mqtt client id keeps
|
|
143
|
+
// every response, event and presence message of this connection
|
|
144
|
+
// private, and a sender field nobody can read cannot be forged
|
|
145
|
+
this._connectionSecret = nanoid(16)
|
|
131
146
|
this._vrpcConnectionId = this._createVrpcConnectionId()
|
|
132
147
|
this._agents = {}
|
|
133
148
|
this._eventEmitter = new EventEmitter()
|
|
@@ -175,7 +190,12 @@ class VrpcClient extends EventEmitter {
|
|
|
175
190
|
* have distinct connection ids. Agents key all their bookkeeping by it
|
|
176
191
|
* (response routing, event listeners, isolated instances, presence), so
|
|
177
192
|
* one connection ending never disturbs the others of the same identity.
|
|
178
|
-
*
|
|
193
|
+
*
|
|
194
|
+
* It reads `<domain>/<mqttClientId>/<secret>`: the mqtt client id is
|
|
195
|
+
* what the broker knows this connection by, so a broker can confine the
|
|
196
|
+
* client to its own topics, and the secret is random per connection, so
|
|
197
|
+
* nobody who cannot read those topics can impersonate the connection
|
|
198
|
+
* towards an agent. Treat it as a credential: do not log or share it.
|
|
179
199
|
*
|
|
180
200
|
* @returns {String} connectionId
|
|
181
201
|
*/
|
|
@@ -866,11 +886,10 @@ class VrpcClient extends EventEmitter {
|
|
|
866
886
|
}
|
|
867
887
|
|
|
868
888
|
_createVrpcConnectionId () {
|
|
869
|
-
//
|
|
870
|
-
//
|
|
871
|
-
//
|
|
872
|
-
|
|
873
|
-
return `${this._vrpcClientId}:${this._instance}`
|
|
889
|
+
// Three segments, always: agents (this one, the C++ and the Python
|
|
890
|
+
// ports) recognise a presence message by `__clientInfo__` arriving as
|
|
891
|
+
// the fourth topic token, so the layout is part of the wire format.
|
|
892
|
+
return `${this._domain}/${this._mqttClientId}/${this._connectionSecret}`
|
|
874
893
|
}
|
|
875
894
|
|
|
876
895
|
_mqttPublish (topic, message, options) {
|
|
@@ -994,7 +1013,11 @@ class VrpcClient extends EventEmitter {
|
|
|
994
1013
|
this._eventEmitter.once(i, ({ e, r }) => {
|
|
995
1014
|
clearTimeout(timer)
|
|
996
1015
|
if (e) {
|
|
997
|
-
|
|
1016
|
+
// the agent answers a throwing constructor with { message, cause }
|
|
1017
|
+
const { message, cause } = VrpcClient._prepareError(e)
|
|
1018
|
+
reject(
|
|
1019
|
+
new Error(`[vrpc ${agent}-${className}-${f}]: ${message}`, { cause })
|
|
1020
|
+
)
|
|
998
1021
|
} else {
|
|
999
1022
|
const proxy = this._createProxy(agent, className, r)
|
|
1000
1023
|
resolve(proxy)
|
|
@@ -1225,15 +1248,15 @@ class VrpcClient extends EventEmitter {
|
|
|
1225
1248
|
return new Promise((resolve, reject) => {
|
|
1226
1249
|
const handler =
|
|
1227
1250
|
timer =>
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1251
|
+
(instances, { agent, className }) => {
|
|
1252
|
+
if (instances.includes(instance)) {
|
|
1253
|
+
if (options.agent && agent !== options.agent) return
|
|
1254
|
+
if (options.className && className !== options.className) return
|
|
1255
|
+
clearTimeout(timer)
|
|
1256
|
+
this.removeListener('instanceNew', handler)
|
|
1257
|
+
resolve({ agent, className, instance })
|
|
1258
|
+
}
|
|
1235
1259
|
}
|
|
1236
|
-
}
|
|
1237
1260
|
const timer = setTimeout(() => {
|
|
1238
1261
|
this.removeListener('instanceNew', handler(timer))
|
|
1239
1262
|
const msg = `Could not find instance: ${instance} (> ${this._timeout} ms)`
|
|
@@ -1454,8 +1477,8 @@ class VrpcClient extends EventEmitter {
|
|
|
1454
1477
|
return (
|
|
1455
1478
|
variable &&
|
|
1456
1479
|
typeof variable === 'object' &&
|
|
1457
|
-
|
|
1458
|
-
|
|
1480
|
+
Object.prototype.hasOwnProperty.call(variable, 'emitter') &&
|
|
1481
|
+
Object.prototype.hasOwnProperty.call(variable, 'event') &&
|
|
1459
1482
|
typeof variable.emitter === 'object' &&
|
|
1460
1483
|
typeof variable.emitter.emit === 'function'
|
|
1461
1484
|
)
|