vrpc 2.6.0 → 3.0.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/CHANGELOG.md +83 -0
- package/LICENSE +1 -1
- package/README.md +34 -60
- package/binding.gyp.no-auto +4 -26
- package/browser/vrpc.js +1 -1
- package/index.js +2 -2
- package/package.json +16 -17
- package/vrpc/VrpcAdapter.js +293 -311
- package/vrpc/VrpcAgent.js +94 -87
- package/vrpc/{VrpcRemote.js → VrpcClient.js} +375 -413
- package/vrpc/VrpcNative.js +330 -0
- package/vrpc/{vrpc.hpp → adapter.hpp} +1003 -1115
- package/vrpc/addon.cpp +6 -6
- 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 => {
|
|
@@ -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
|
|
|
@@ -343,23 +346,23 @@ class VrpcAgent extends EventEmitter {
|
|
|
343
346
|
}
|
|
344
347
|
|
|
345
348
|
_getClasses () {
|
|
346
|
-
return VrpcAdapter.
|
|
349
|
+
return VrpcAdapter._getClassesArray()
|
|
347
350
|
}
|
|
348
351
|
|
|
349
352
|
_getInstances (className) {
|
|
350
|
-
return VrpcAdapter.
|
|
353
|
+
return VrpcAdapter._getInstancesArray(className)
|
|
351
354
|
}
|
|
352
355
|
|
|
353
356
|
_getMemberFunctions (className) {
|
|
354
|
-
return VrpcAdapter.
|
|
357
|
+
return VrpcAdapter._getMemberFunctionsArray(className)
|
|
355
358
|
}
|
|
356
359
|
|
|
357
360
|
_getStaticFunctions (className) {
|
|
358
|
-
return VrpcAdapter.
|
|
361
|
+
return VrpcAdapter._getStaticFunctionsArray(className)
|
|
359
362
|
}
|
|
360
363
|
|
|
361
364
|
_getMetaData (className) {
|
|
362
|
-
return VrpcAdapter.
|
|
365
|
+
return VrpcAdapter._getMetaData(className)
|
|
363
366
|
}
|
|
364
367
|
|
|
365
368
|
async _ensureConnected () {
|
|
@@ -372,22 +375,38 @@ class VrpcAgent extends EventEmitter {
|
|
|
372
375
|
})
|
|
373
376
|
}
|
|
374
377
|
|
|
375
|
-
_handleVrpcCallback (
|
|
376
|
-
const {
|
|
378
|
+
_handleVrpcCallback (json) {
|
|
379
|
+
const { s, i, r, e, a } = json
|
|
380
|
+
const topic = i.startsWith('__e__') ? i.slice(5) : s
|
|
377
381
|
try {
|
|
378
|
-
this._log.debug(
|
|
379
|
-
|
|
380
|
-
|
|
382
|
+
this._log.debug(`Forwarding callback to: ${topic} with payload:`, json)
|
|
383
|
+
this._mqttPublish(
|
|
384
|
+
topic,
|
|
385
|
+
VrpcAgent._stringifySafely({ a, r, e, i, v: VRPC_PROTOCOL_VERSION })
|
|
381
386
|
)
|
|
382
|
-
this._mqttPublish(sender, jsonString)
|
|
383
387
|
} catch (err) {
|
|
384
388
|
this._log.warn(
|
|
385
389
|
err,
|
|
386
|
-
`Problem publishing vrpc callback to ${
|
|
390
|
+
`Problem publishing vrpc callback to ${topic} because of: ${err.message}`
|
|
387
391
|
)
|
|
388
392
|
}
|
|
389
393
|
}
|
|
390
394
|
|
|
395
|
+
static _stringifySafely (json) {
|
|
396
|
+
let str
|
|
397
|
+
const { c, f } = json
|
|
398
|
+
try {
|
|
399
|
+
str = JSON.stringify(json)
|
|
400
|
+
} catch (err) {
|
|
401
|
+
this._log.debug(
|
|
402
|
+
`Failed serialization of return value for: ${c}::${f}, because: ${err.message}`
|
|
403
|
+
)
|
|
404
|
+
json.r = '__vrpc::not-serializable__'
|
|
405
|
+
str = JSON.stringify(json)
|
|
406
|
+
}
|
|
407
|
+
return str
|
|
408
|
+
}
|
|
409
|
+
|
|
391
410
|
_handleConnect () {
|
|
392
411
|
this._log.info('[OK]')
|
|
393
412
|
if (this._isReconnect) {
|
|
@@ -441,7 +460,8 @@ class VrpcAgent extends EventEmitter {
|
|
|
441
460
|
instances: this._getInstances(className),
|
|
442
461
|
memberFunctions: this._getMemberFunctions(className),
|
|
443
462
|
staticFunctions: this._getStaticFunctions(className),
|
|
444
|
-
meta: this._getMetaData(className)
|
|
463
|
+
meta: this._getMetaData(className),
|
|
464
|
+
v: VRPC_PROTOCOL_VERSION
|
|
445
465
|
}
|
|
446
466
|
try {
|
|
447
467
|
this._mqttPublish(
|
|
@@ -494,69 +514,48 @@ class VrpcAgent extends EventEmitter {
|
|
|
494
514
|
}
|
|
495
515
|
|
|
496
516
|
// Prepare RPC json
|
|
497
|
-
|
|
498
|
-
|
|
517
|
+
// SECURITY: take as much as possible params from topic structure as this
|
|
518
|
+
// can be authorized by the broker. Content can't be checked...
|
|
519
|
+
json.c = instance === '__static__' ? className : instance
|
|
520
|
+
json.f = method
|
|
499
521
|
|
|
500
522
|
// Mutates json and adds return value
|
|
501
|
-
VrpcAdapter._call(json)
|
|
523
|
+
const mustTrack = VrpcAdapter._call(json)
|
|
524
|
+
|
|
525
|
+
if (mustTrack) {
|
|
526
|
+
this._mqttSubscribe(`${json.s}/__clientInfo__`)
|
|
527
|
+
}
|
|
502
528
|
|
|
503
529
|
// Intersecting life-cycle functions
|
|
504
|
-
let publishClassInfo = false
|
|
505
530
|
switch (method) {
|
|
506
|
-
case '
|
|
531
|
+
case '__createIsolated__': {
|
|
507
532
|
// TODO handle instantiation errors
|
|
508
|
-
const instanceId = json.
|
|
533
|
+
const instanceId = json.r
|
|
509
534
|
// TODO await this
|
|
510
535
|
this._subscribeToMethodsOfNewInstance(className, instanceId)
|
|
511
|
-
this.
|
|
536
|
+
this._registerIsolatedInstance(instanceId, json.s)
|
|
512
537
|
break
|
|
513
538
|
}
|
|
514
|
-
case '
|
|
539
|
+
case '__createShared__': {
|
|
515
540
|
// TODO handle instantiation errors
|
|
516
|
-
const instanceId = json.
|
|
517
|
-
if (!this.
|
|
518
|
-
publishClassInfo = true
|
|
541
|
+
const instanceId = json.r
|
|
542
|
+
if (!this._hasSharedInstance(instanceId)) {
|
|
519
543
|
this._subscribeToMethodsOfNewInstance(className, instanceId)
|
|
544
|
+
this._publishClassInfoMessage(className)
|
|
520
545
|
}
|
|
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)
|
|
546
|
+
this._registerSharedInstance(instanceId, json.s)
|
|
530
547
|
break
|
|
531
548
|
}
|
|
532
549
|
case '__delete__': {
|
|
533
|
-
const {
|
|
534
|
-
data: { _1 },
|
|
535
|
-
sender
|
|
536
|
-
} = json
|
|
537
550
|
this._unsubscribeMethodsOfDeletedInstance(className, instance)
|
|
538
|
-
const
|
|
539
|
-
if (
|
|
551
|
+
const wasShared = this._unregisterInstance(json.a[0], json.s)
|
|
552
|
+
if (wasShared) this._publishClassInfoMessage(className)
|
|
540
553
|
break
|
|
541
554
|
}
|
|
542
555
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
-
}
|
|
556
|
+
const { a, r, e, i, v } = json
|
|
557
|
+
const res = e ? { a, r, e, i, v } : { a, r, i, v }
|
|
558
|
+
this._mqttPublish(json.s, VrpcAgent._stringifySafely(res))
|
|
560
559
|
} catch (err) {
|
|
561
560
|
this._log.error(
|
|
562
561
|
err,
|
|
@@ -567,80 +566,79 @@ class VrpcAgent extends EventEmitter {
|
|
|
567
566
|
|
|
568
567
|
_handleClientInfoMessage (topic, json) {
|
|
569
568
|
// Client went offline
|
|
570
|
-
const clientId = topic.slice(0, -
|
|
569
|
+
const clientId = topic.slice(0, -15) // /__clientInfo__ = 15
|
|
571
570
|
if (json.status === 'offline') {
|
|
572
|
-
|
|
571
|
+
VrpcAdapter._unregisterClient(clientId)
|
|
572
|
+
const entry = this._isolatedInstances.get(clientId)
|
|
573
573
|
if (entry) {
|
|
574
|
-
// anonymous
|
|
575
574
|
entry.forEach(instanceId => {
|
|
576
|
-
const json = {
|
|
575
|
+
const json = { f: '__delete__', a: [instanceId], r: null }
|
|
577
576
|
VrpcAdapter._call(json)
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
}
|
|
581
|
-
if (r) this._log.debug(`Deleted unnamed instance: ${instanceId}`)
|
|
577
|
+
if (json.r) {
|
|
578
|
+
this._log.debug(`Auto-deleted isolated instance: ${instanceId}`)
|
|
579
|
+
}
|
|
582
580
|
})
|
|
583
581
|
}
|
|
584
|
-
VrpcAdapter._unregisterEventListeners(clientId)
|
|
585
582
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
583
|
+
this.emit('clientGone', clientId)
|
|
586
584
|
}
|
|
587
585
|
}
|
|
588
586
|
|
|
589
|
-
|
|
590
|
-
const entry = this.
|
|
587
|
+
_registerIsolatedInstance (instanceId, clientId) {
|
|
588
|
+
const entry = this._isolatedInstances.get(clientId)
|
|
591
589
|
if (entry) {
|
|
592
590
|
// already seen
|
|
593
591
|
entry.add(instanceId)
|
|
594
592
|
} else {
|
|
595
593
|
// new instance
|
|
596
|
-
this.
|
|
597
|
-
if (!this.
|
|
594
|
+
this._isolatedInstances.set(clientId, new Set([instanceId]))
|
|
595
|
+
if (!this._sharedInstances.has(clientId)) {
|
|
598
596
|
this._mqttSubscribe(`${clientId}/__clientInfo__`)
|
|
599
597
|
}
|
|
600
598
|
this._log.info(`Tracking lifetime of client: ${clientId}`)
|
|
601
599
|
}
|
|
602
600
|
}
|
|
603
601
|
|
|
604
|
-
|
|
605
|
-
const entry = this.
|
|
602
|
+
_registerSharedInstance (instanceId, clientId) {
|
|
603
|
+
const entry = this._sharedInstances.get(clientId)
|
|
606
604
|
if (entry) {
|
|
607
605
|
// already seen
|
|
608
606
|
entry.add(instanceId)
|
|
609
607
|
} else {
|
|
610
608
|
// new instance
|
|
611
|
-
this.
|
|
612
|
-
if (!this.
|
|
609
|
+
this._sharedInstances.set(clientId, new Set([instanceId]))
|
|
610
|
+
if (!this._isolatedInstances.has(clientId)) {
|
|
613
611
|
this._mqttSubscribe(`${clientId}/__clientInfo__`)
|
|
614
612
|
}
|
|
615
613
|
this._log.debug(`Tracking lifetime of client: ${clientId}`)
|
|
616
614
|
}
|
|
617
615
|
}
|
|
618
616
|
|
|
619
|
-
|
|
620
|
-
for (const [, instances] of this.
|
|
617
|
+
_hasSharedInstance (instanceId) {
|
|
618
|
+
for (const [, instances] of this._sharedInstances) {
|
|
621
619
|
if (instances.has(instanceId)) return true
|
|
622
620
|
}
|
|
623
621
|
return false
|
|
624
622
|
}
|
|
625
623
|
|
|
626
624
|
_unregisterInstance (instanceId, clientId) {
|
|
627
|
-
const
|
|
628
|
-
if (
|
|
629
|
-
|
|
630
|
-
if (
|
|
631
|
-
this.
|
|
625
|
+
const entryIsolated = this._isolatedInstances.get(clientId)
|
|
626
|
+
if (entryIsolated && entryIsolated.has(instanceId)) {
|
|
627
|
+
entryIsolated.delete(instanceId)
|
|
628
|
+
if (entryIsolated.length === 0) {
|
|
629
|
+
this._isolatedInstances.delete(clientId)
|
|
632
630
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
633
631
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
634
632
|
}
|
|
635
633
|
return false
|
|
636
634
|
}
|
|
637
635
|
let found = false
|
|
638
|
-
this.
|
|
636
|
+
this._sharedInstances.forEach(async v => {
|
|
639
637
|
if (v.has(instanceId)) {
|
|
640
638
|
found = true
|
|
641
639
|
v.delete(instanceId)
|
|
642
640
|
if (v.length === 0) {
|
|
643
|
-
this.
|
|
641
|
+
this._sharedInstances.delete(clientId)
|
|
644
642
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
645
643
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
646
644
|
}
|
|
@@ -749,4 +747,13 @@ class VrpcAgent extends EventEmitter {
|
|
|
749
747
|
* @event VrpcAgent#end
|
|
750
748
|
*/
|
|
751
749
|
|
|
750
|
+
/**
|
|
751
|
+
* Event 'clientGone'
|
|
752
|
+
*
|
|
753
|
+
* Emitted when a tracked VRPC client exited.
|
|
754
|
+
*
|
|
755
|
+
* @event VrpcAgent#clientGone
|
|
756
|
+
* @type {String} clientId
|
|
757
|
+
*/
|
|
758
|
+
|
|
752
759
|
module.exports = VrpcAgent
|