vrpc 2.6.0 → 3.0.0-alpha.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/CHANGELOG.md +29 -0
- package/README.md +33 -56
- package/binding.gyp.no-auto +4 -26
- package/browser/vrpc.js +1 -1
- package/index.js +2 -2
- package/package.json +11 -15
- package/vrpc/VrpcAdapter.js +95 -173
- package/vrpc/VrpcAgent.js +77 -77
- package/vrpc/{VrpcRemote.js → VrpcClient.js} +98 -152
- package/vrpc/VrpcNative.js +307 -0
- package/vrpc/{vrpc.hpp → adapter.hpp} +1000 -1114
- 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 -269
- 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 => {
|
|
@@ -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,42 @@ 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
519
|
VrpcAdapter._call(json)
|
|
502
520
|
|
|
503
521
|
// Intersecting life-cycle functions
|
|
504
|
-
let publishClassInfo = false
|
|
505
522
|
switch (method) {
|
|
506
|
-
case '
|
|
523
|
+
case '__createIsolated__': {
|
|
507
524
|
// TODO handle instantiation errors
|
|
508
|
-
const instanceId = json.
|
|
525
|
+
const instanceId = json.r
|
|
509
526
|
// TODO await this
|
|
510
527
|
this._subscribeToMethodsOfNewInstance(className, instanceId)
|
|
511
|
-
this.
|
|
528
|
+
this._registerIsolatedInstance(instanceId, json.s)
|
|
512
529
|
break
|
|
513
530
|
}
|
|
514
|
-
case '
|
|
531
|
+
case '__createShared__': {
|
|
515
532
|
// TODO handle instantiation errors
|
|
516
|
-
const instanceId = json.
|
|
517
|
-
if (!this.
|
|
518
|
-
publishClassInfo = true
|
|
533
|
+
const instanceId = json.r
|
|
534
|
+
if (!this._hasSharedInstance(instanceId)) {
|
|
519
535
|
this._subscribeToMethodsOfNewInstance(className, instanceId)
|
|
536
|
+
this._publishClassInfoMessage(className)
|
|
520
537
|
}
|
|
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)
|
|
538
|
+
this._registerSharedInstance(instanceId, json.s)
|
|
530
539
|
break
|
|
531
540
|
}
|
|
532
541
|
case '__delete__': {
|
|
533
|
-
const {
|
|
534
|
-
data: { _1 },
|
|
535
|
-
sender
|
|
536
|
-
} = json
|
|
537
542
|
this._unsubscribeMethodsOfDeletedInstance(className, instance)
|
|
538
|
-
const
|
|
539
|
-
if (
|
|
543
|
+
const wasShared = this._unregisterInstance(json.a[0], json.s)
|
|
544
|
+
if (wasShared) this._publishClassInfoMessage(className)
|
|
540
545
|
break
|
|
541
546
|
}
|
|
542
547
|
}
|
|
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
|
-
}
|
|
548
|
+
this._mqttPublish(json.s, VrpcAgent._stringifySafely(json))
|
|
560
549
|
} catch (err) {
|
|
561
550
|
this._log.error(
|
|
562
551
|
err,
|
|
@@ -567,80 +556,81 @@ class VrpcAgent extends EventEmitter {
|
|
|
567
556
|
|
|
568
557
|
_handleClientInfoMessage (topic, json) {
|
|
569
558
|
// Client went offline
|
|
570
|
-
const clientId = topic.slice(0, -
|
|
559
|
+
const clientId = topic.slice(0, -15) // /__clientInfo__ = 15
|
|
571
560
|
if (json.status === 'offline') {
|
|
572
|
-
const entry = this.
|
|
561
|
+
const entry = this._isolatedInstances.get(clientId)
|
|
573
562
|
if (entry) {
|
|
574
563
|
// anonymous
|
|
575
564
|
entry.forEach(instanceId => {
|
|
576
|
-
const json = {
|
|
565
|
+
const json = { f: '__delete__', a: [instanceId], r: null }
|
|
566
|
+
// TODO Do not mutate!
|
|
577
567
|
VrpcAdapter._call(json)
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
}
|
|
581
|
-
if (r) this._log.debug(`Deleted unnamed instance: ${instanceId}`)
|
|
568
|
+
if (json.r) {
|
|
569
|
+
this._log.debug(`Auto-deleted isolated instance: ${instanceId}`)
|
|
570
|
+
}
|
|
582
571
|
})
|
|
583
572
|
}
|
|
584
573
|
VrpcAdapter._unregisterEventListeners(clientId)
|
|
585
574
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
575
|
+
this.emit('clientGone', clientId)
|
|
586
576
|
}
|
|
587
577
|
}
|
|
588
578
|
|
|
589
|
-
|
|
590
|
-
const entry = this.
|
|
579
|
+
_registerIsolatedInstance (instanceId, clientId) {
|
|
580
|
+
const entry = this._isolatedInstances.get(clientId)
|
|
591
581
|
if (entry) {
|
|
592
582
|
// already seen
|
|
593
583
|
entry.add(instanceId)
|
|
594
584
|
} else {
|
|
595
585
|
// new instance
|
|
596
|
-
this.
|
|
597
|
-
if (!this.
|
|
586
|
+
this._isolatedInstances.set(clientId, new Set([instanceId]))
|
|
587
|
+
if (!this._sharedInstances.has(clientId)) {
|
|
598
588
|
this._mqttSubscribe(`${clientId}/__clientInfo__`)
|
|
599
589
|
}
|
|
600
590
|
this._log.info(`Tracking lifetime of client: ${clientId}`)
|
|
601
591
|
}
|
|
602
592
|
}
|
|
603
593
|
|
|
604
|
-
|
|
605
|
-
const entry = this.
|
|
594
|
+
_registerSharedInstance (instanceId, clientId) {
|
|
595
|
+
const entry = this._sharedInstances.get(clientId)
|
|
606
596
|
if (entry) {
|
|
607
597
|
// already seen
|
|
608
598
|
entry.add(instanceId)
|
|
609
599
|
} else {
|
|
610
600
|
// new instance
|
|
611
|
-
this.
|
|
612
|
-
if (!this.
|
|
601
|
+
this._sharedInstances.set(clientId, new Set([instanceId]))
|
|
602
|
+
if (!this._isolatedInstances.has(clientId)) {
|
|
613
603
|
this._mqttSubscribe(`${clientId}/__clientInfo__`)
|
|
614
604
|
}
|
|
615
605
|
this._log.debug(`Tracking lifetime of client: ${clientId}`)
|
|
616
606
|
}
|
|
617
607
|
}
|
|
618
608
|
|
|
619
|
-
|
|
620
|
-
for (const [, instances] of this.
|
|
609
|
+
_hasSharedInstance (instanceId) {
|
|
610
|
+
for (const [, instances] of this._sharedInstances) {
|
|
621
611
|
if (instances.has(instanceId)) return true
|
|
622
612
|
}
|
|
623
613
|
return false
|
|
624
614
|
}
|
|
625
615
|
|
|
626
616
|
_unregisterInstance (instanceId, clientId) {
|
|
627
|
-
const
|
|
628
|
-
if (
|
|
629
|
-
|
|
630
|
-
if (
|
|
631
|
-
this.
|
|
617
|
+
const entryIsolated = this._isolatedInstances.get(clientId)
|
|
618
|
+
if (entryIsolated && entryIsolated.has(instanceId)) {
|
|
619
|
+
entryIsolated.delete(instanceId)
|
|
620
|
+
if (entryIsolated.length === 0) {
|
|
621
|
+
this._isolatedInstances.delete(clientId)
|
|
632
622
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
633
623
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
634
624
|
}
|
|
635
625
|
return false
|
|
636
626
|
}
|
|
637
627
|
let found = false
|
|
638
|
-
this.
|
|
628
|
+
this._sharedInstances.forEach(async v => {
|
|
639
629
|
if (v.has(instanceId)) {
|
|
640
630
|
found = true
|
|
641
631
|
v.delete(instanceId)
|
|
642
632
|
if (v.length === 0) {
|
|
643
|
-
this.
|
|
633
|
+
this._sharedInstances.delete(clientId)
|
|
644
634
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
645
635
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
646
636
|
}
|
|
@@ -749,4 +739,14 @@ class VrpcAgent extends EventEmitter {
|
|
|
749
739
|
* @event VrpcAgent#end
|
|
750
740
|
*/
|
|
751
741
|
|
|
742
|
+
/**
|
|
743
|
+
* Event 'clientGone'
|
|
744
|
+
*
|
|
745
|
+
* Emitted when a VRPC client exited that before produced state (e.g. created
|
|
746
|
+
* an anonymous or named instance).
|
|
747
|
+
*
|
|
748
|
+
* @event VrpcAgent#clientGone
|
|
749
|
+
* @type {String} clientId
|
|
750
|
+
*/
|
|
751
|
+
|
|
752
752
|
module.exports = VrpcAgent
|