vrpc 2.5.1 → 3.0.0-alpha.3
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/CHANGELOG.md +66 -0
- package/README.md +31 -56
- package/binding.gyp.no-auto +4 -26
- package/browser/vrpc.js +1 -1
- package/index.js +2 -2
- package/package.json +12 -16
- package/vrpc/VrpcAdapter.js +202 -193
- package/vrpc/VrpcAgent.js +86 -80
- package/vrpc/{VrpcRemote.js → VrpcClient.js} +302 -425
- package/vrpc/VrpcNative.js +307 -0
- package/vrpc/{vrpc.hpp → adapter.hpp} +1001 -1115
- package/vrpc/addon.cpp +5 -5
- package/vrpc/json.hpp +1769 -870
- package/vrpc/vrpc-agent-js +4 -4
- package/README.rst +0 -18
- package/third_party/include/catch.hpp +0 -12749
- package/third_party/install.sh +0 -22
- package/vrpc/VrpcLocal.js +0 -264
- package/vrpc/VrpcLocal.py +0 -171
- package/vrpc/__init__.py +0 -1
- package/vrpc/module.cpp +0 -183
package/vrpc/VrpcAgent.js
CHANGED
|
@@ -6,6 +6,8 @@ const { ArgumentParser } = require('argparse')
|
|
|
6
6
|
const VrpcAdapter = require('./VrpcAdapter')
|
|
7
7
|
const EventEmitter = require('events')
|
|
8
8
|
|
|
9
|
+
const VRPC_PROTOCOL_VERSION = 3
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* Agent capable of making existing code available to remote control by clients.
|
|
11
13
|
*
|
|
@@ -138,8 +140,8 @@ class VrpcAgent extends EventEmitter {
|
|
|
138
140
|
this._baseTopic = `${this._domain}/${this._agent}`
|
|
139
141
|
VrpcAdapter.onCallback(this._handleVrpcCallback.bind(this))
|
|
140
142
|
// maps clientId to instanceId
|
|
141
|
-
this.
|
|
142
|
-
this.
|
|
143
|
+
this._isolatedInstances = new Map()
|
|
144
|
+
this._sharedInstances = new Map()
|
|
143
145
|
|
|
144
146
|
// Handle the internal error event in case the user forgot to implement it
|
|
145
147
|
this.on('error', err => {
|
|
@@ -167,7 +169,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
167
169
|
.createHash('md5')
|
|
168
170
|
.update(this._domain + this._agent)
|
|
169
171
|
.digest('hex')
|
|
170
|
-
.substr(0,
|
|
172
|
+
.substr(0, 20)
|
|
171
173
|
let username = this._username
|
|
172
174
|
let password = this._password
|
|
173
175
|
if (this._token) {
|
|
@@ -179,7 +181,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
179
181
|
password,
|
|
180
182
|
keepalive: 30,
|
|
181
183
|
connectTimeout: 10 * 1000,
|
|
182
|
-
clientId: `
|
|
184
|
+
clientId: `va3${md5}`,
|
|
183
185
|
rejectUnauthorized: false,
|
|
184
186
|
will: {
|
|
185
187
|
topic: `${this._baseTopic}/__agentInfo__`,
|
|
@@ -274,7 +276,8 @@ class VrpcAgent extends EventEmitter {
|
|
|
274
276
|
return JSON.stringify({
|
|
275
277
|
status,
|
|
276
278
|
hostname: os.hostname(),
|
|
277
|
-
version: this._version
|
|
279
|
+
version: this._version,
|
|
280
|
+
v: VRPC_PROTOCOL_VERSION
|
|
278
281
|
})
|
|
279
282
|
}
|
|
280
283
|
|
|
@@ -372,14 +375,11 @@ class VrpcAgent extends EventEmitter {
|
|
|
372
375
|
})
|
|
373
376
|
}
|
|
374
377
|
|
|
375
|
-
_handleVrpcCallback (
|
|
376
|
-
const {
|
|
378
|
+
_handleVrpcCallback (json) {
|
|
379
|
+
const { s } = json
|
|
377
380
|
try {
|
|
378
|
-
this._log.debug(
|
|
379
|
-
|
|
380
|
-
jsonObject
|
|
381
|
-
)
|
|
382
|
-
this._mqttPublish(sender, jsonString)
|
|
381
|
+
this._log.debug(`Forwarding callback to: ${s} with payload:`, json)
|
|
382
|
+
this._mqttPublish(s, VrpcAgent._stringifySafely(json))
|
|
383
383
|
} catch (err) {
|
|
384
384
|
this._log.warn(
|
|
385
385
|
err,
|
|
@@ -388,6 +388,21 @@ class VrpcAgent extends EventEmitter {
|
|
|
388
388
|
}
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
static _stringifySafely (json) {
|
|
392
|
+
let str
|
|
393
|
+
const { c, f } = json
|
|
394
|
+
try {
|
|
395
|
+
str = JSON.stringify(json)
|
|
396
|
+
} catch (err) {
|
|
397
|
+
this._log.debug(
|
|
398
|
+
`Failed serialization of return value for: ${c}::${f}, because: ${err.message}`
|
|
399
|
+
)
|
|
400
|
+
json.r = '__vrpc::not-serializable__'
|
|
401
|
+
str = JSON.stringify(json)
|
|
402
|
+
}
|
|
403
|
+
return str
|
|
404
|
+
}
|
|
405
|
+
|
|
391
406
|
_handleConnect () {
|
|
392
407
|
this._log.info('[OK]')
|
|
393
408
|
if (this._isReconnect) {
|
|
@@ -441,7 +456,8 @@ class VrpcAgent extends EventEmitter {
|
|
|
441
456
|
instances: this._getInstances(className),
|
|
442
457
|
memberFunctions: this._getMemberFunctions(className),
|
|
443
458
|
staticFunctions: this._getStaticFunctions(className),
|
|
444
|
-
meta: this._getMetaData(className)
|
|
459
|
+
meta: this._getMetaData(className),
|
|
460
|
+
v: VRPC_PROTOCOL_VERSION
|
|
445
461
|
}
|
|
446
462
|
try {
|
|
447
463
|
this._mqttPublish(
|
|
@@ -494,69 +510,46 @@ class VrpcAgent extends EventEmitter {
|
|
|
494
510
|
}
|
|
495
511
|
|
|
496
512
|
// Prepare RPC json
|
|
497
|
-
|
|
498
|
-
|
|
513
|
+
// SECURITY: take as much as possible params from topic structure as this
|
|
514
|
+
// can be authorized by the broker. Content can't be checked...
|
|
515
|
+
json.c = instance === '__static__' ? className : instance
|
|
516
|
+
json.f = method
|
|
499
517
|
|
|
500
518
|
// Mutates json and adds return value
|
|
501
|
-
VrpcAdapter._call(json)
|
|
519
|
+
const mustTrack = VrpcAdapter._call(json)
|
|
520
|
+
|
|
521
|
+
if (mustTrack) {
|
|
522
|
+
this._mqttSubscribe(`${json.s}/__clientInfo__`)
|
|
523
|
+
}
|
|
502
524
|
|
|
503
525
|
// Intersecting life-cycle functions
|
|
504
|
-
let publishClassInfo = false
|
|
505
526
|
switch (method) {
|
|
506
|
-
case '
|
|
527
|
+
case '__createIsolated__': {
|
|
507
528
|
// TODO handle instantiation errors
|
|
508
|
-
const instanceId = json.
|
|
529
|
+
const instanceId = json.r
|
|
509
530
|
// TODO await this
|
|
510
531
|
this._subscribeToMethodsOfNewInstance(className, instanceId)
|
|
511
|
-
this.
|
|
532
|
+
this._registerIsolatedInstance(instanceId, json.s)
|
|
512
533
|
break
|
|
513
534
|
}
|
|
514
|
-
case '
|
|
535
|
+
case '__createShared__': {
|
|
515
536
|
// TODO handle instantiation errors
|
|
516
|
-
const instanceId = json.
|
|
517
|
-
if (!this.
|
|
518
|
-
publishClassInfo = true
|
|
537
|
+
const instanceId = json.r
|
|
538
|
+
if (!this._hasSharedInstance(instanceId)) {
|
|
519
539
|
this._subscribeToMethodsOfNewInstance(className, instanceId)
|
|
540
|
+
this._publishClassInfoMessage(className)
|
|
520
541
|
}
|
|
521
|
-
this.
|
|
522
|
-
break
|
|
523
|
-
}
|
|
524
|
-
case '__getNamed__': {
|
|
525
|
-
const {
|
|
526
|
-
data: { _1, e },
|
|
527
|
-
sender
|
|
528
|
-
} = json
|
|
529
|
-
if (!e) this._registerNamedInstance(_1, sender)
|
|
542
|
+
this._registerSharedInstance(instanceId, json.s)
|
|
530
543
|
break
|
|
531
544
|
}
|
|
532
545
|
case '__delete__': {
|
|
533
|
-
const {
|
|
534
|
-
data: { _1 },
|
|
535
|
-
sender
|
|
536
|
-
} = json
|
|
537
546
|
this._unsubscribeMethodsOfDeletedInstance(className, instance)
|
|
538
|
-
const
|
|
539
|
-
if (
|
|
547
|
+
const wasShared = this._unregisterInstance(json.a[0], json.s)
|
|
548
|
+
if (wasShared) this._publishClassInfoMessage(className)
|
|
540
549
|
break
|
|
541
550
|
}
|
|
542
551
|
}
|
|
543
|
-
|
|
544
|
-
try {
|
|
545
|
-
jsonString = JSON.stringify(json)
|
|
546
|
-
} catch (err) {
|
|
547
|
-
this._log.debug(
|
|
548
|
-
`Failed serialization of return value for: ${json.context}::${json.method}, because: ${err.message}`
|
|
549
|
-
)
|
|
550
|
-
json.data.r = '__vrpc::not-serializable__'
|
|
551
|
-
jsonString = JSON.stringify(json)
|
|
552
|
-
}
|
|
553
|
-
if (publishClassInfo && method === '__delete__') {
|
|
554
|
-
this._publishClassInfoMessage(className)
|
|
555
|
-
}
|
|
556
|
-
this._mqttPublish(json.sender, jsonString)
|
|
557
|
-
if (publishClassInfo && method === '__createNamed__') {
|
|
558
|
-
this._publishClassInfoMessage(className)
|
|
559
|
-
}
|
|
552
|
+
this._mqttPublish(json.s, VrpcAgent._stringifySafely(json))
|
|
560
553
|
} catch (err) {
|
|
561
554
|
this._log.error(
|
|
562
555
|
err,
|
|
@@ -567,80 +560,83 @@ class VrpcAgent extends EventEmitter {
|
|
|
567
560
|
|
|
568
561
|
_handleClientInfoMessage (topic, json) {
|
|
569
562
|
// Client went offline
|
|
570
|
-
const clientId = topic.slice(0, -
|
|
563
|
+
const clientId = topic.slice(0, -15) // /__clientInfo__ = 15
|
|
571
564
|
if (json.status === 'offline') {
|
|
572
|
-
const entry = this.
|
|
565
|
+
const entry = this._isolatedInstances.get(clientId)
|
|
573
566
|
if (entry) {
|
|
574
567
|
// anonymous
|
|
575
568
|
entry.forEach(instanceId => {
|
|
576
|
-
const json = {
|
|
569
|
+
const json = { f: '__delete__', a: [instanceId], r: null }
|
|
570
|
+
// TODO Do not mutate!
|
|
577
571
|
VrpcAdapter._call(json)
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
}
|
|
581
|
-
if (r) this._log.debug(`Deleted unnamed instance: ${instanceId}`)
|
|
572
|
+
if (json.r) {
|
|
573
|
+
this._log.debug(`Auto-deleted isolated instance: ${instanceId}`)
|
|
574
|
+
}
|
|
582
575
|
})
|
|
583
576
|
}
|
|
584
577
|
VrpcAdapter._unregisterEventListeners(clientId)
|
|
585
578
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
579
|
+
this.emit('clientGone', clientId)
|
|
586
580
|
}
|
|
587
581
|
}
|
|
588
582
|
|
|
589
|
-
|
|
590
|
-
const entry = this.
|
|
583
|
+
_registerIsolatedInstance (instanceId, clientId) {
|
|
584
|
+
const entry = this._isolatedInstances.get(clientId)
|
|
591
585
|
if (entry) {
|
|
592
586
|
// already seen
|
|
593
587
|
entry.add(instanceId)
|
|
594
588
|
} else {
|
|
595
589
|
// new instance
|
|
596
|
-
this.
|
|
597
|
-
if (!this.
|
|
590
|
+
this._isolatedInstances.set(clientId, new Set([instanceId]))
|
|
591
|
+
if (!this._sharedInstances.has(clientId)) {
|
|
598
592
|
this._mqttSubscribe(`${clientId}/__clientInfo__`)
|
|
599
593
|
}
|
|
600
594
|
this._log.info(`Tracking lifetime of client: ${clientId}`)
|
|
601
595
|
}
|
|
602
596
|
}
|
|
603
597
|
|
|
604
|
-
|
|
605
|
-
const entry = this.
|
|
598
|
+
_registerSharedInstance (instanceId, clientId) {
|
|
599
|
+
const entry = this._sharedInstances.get(clientId)
|
|
606
600
|
if (entry) {
|
|
607
601
|
// already seen
|
|
608
602
|
entry.add(instanceId)
|
|
609
603
|
} else {
|
|
610
604
|
// new instance
|
|
611
|
-
this.
|
|
612
|
-
if (!this.
|
|
605
|
+
this._sharedInstances.set(clientId, new Set([instanceId]))
|
|
606
|
+
if (!this._isolatedInstances.has(clientId)) {
|
|
613
607
|
this._mqttSubscribe(`${clientId}/__clientInfo__`)
|
|
614
608
|
}
|
|
615
609
|
this._log.debug(`Tracking lifetime of client: ${clientId}`)
|
|
616
610
|
}
|
|
617
611
|
}
|
|
618
612
|
|
|
619
|
-
|
|
620
|
-
for (const [, instances] of this.
|
|
613
|
+
_hasSharedInstance (instanceId) {
|
|
614
|
+
for (const [, instances] of this._sharedInstances) {
|
|
621
615
|
if (instances.has(instanceId)) return true
|
|
622
616
|
}
|
|
623
617
|
return false
|
|
624
618
|
}
|
|
625
619
|
|
|
626
620
|
_unregisterInstance (instanceId, clientId) {
|
|
627
|
-
const
|
|
628
|
-
if (
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
621
|
+
const entryIsolated = this._isolatedInstances.get(clientId)
|
|
622
|
+
if (entryIsolated && entryIsolated.has(instanceId)) {
|
|
623
|
+
VrpcAdapter._unregisterEventListenersOfInstance(instanceId)
|
|
624
|
+
entryIsolated.delete(instanceId)
|
|
625
|
+
if (entryIsolated.length === 0) {
|
|
626
|
+
this._isolatedInstances.delete(clientId)
|
|
632
627
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
633
628
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
634
629
|
}
|
|
635
630
|
return false
|
|
636
631
|
}
|
|
637
632
|
let found = false
|
|
638
|
-
this.
|
|
633
|
+
this._sharedInstances.forEach(async v => {
|
|
639
634
|
if (v.has(instanceId)) {
|
|
640
635
|
found = true
|
|
636
|
+
VrpcAdapter._unregisterEventListenersOfInstance(instanceId)
|
|
641
637
|
v.delete(instanceId)
|
|
642
638
|
if (v.length === 0) {
|
|
643
|
-
this.
|
|
639
|
+
this._sharedInstances.delete(clientId)
|
|
644
640
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
645
641
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
646
642
|
}
|
|
@@ -749,4 +745,14 @@ class VrpcAgent extends EventEmitter {
|
|
|
749
745
|
* @event VrpcAgent#end
|
|
750
746
|
*/
|
|
751
747
|
|
|
748
|
+
/**
|
|
749
|
+
* Event 'clientGone'
|
|
750
|
+
*
|
|
751
|
+
* Emitted when a VRPC client exited that before produced state (e.g. created
|
|
752
|
+
* an anonymous or named instance).
|
|
753
|
+
*
|
|
754
|
+
* @event VrpcAgent#clientGone
|
|
755
|
+
* @type {String} clientId
|
|
756
|
+
*/
|
|
757
|
+
|
|
752
758
|
module.exports = VrpcAgent
|