vrpc 2.4.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 +69 -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 +13 -17
- package/vrpc/VrpcAdapter.js +337 -216
- package/vrpc/VrpcAgent.js +203 -183
- package/vrpc/{VrpcRemote.js → VrpcClient.js} +304 -288
- 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,13 +6,14 @@ 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
|
*
|
|
12
14
|
* @extends EventEmitter
|
|
13
15
|
*/
|
|
14
16
|
class VrpcAgent extends EventEmitter {
|
|
15
|
-
|
|
16
17
|
/**
|
|
17
18
|
* Constructs an agent by parsing command line arguments
|
|
18
19
|
*
|
|
@@ -41,79 +42,47 @@ class VrpcAgent extends EventEmitter {
|
|
|
41
42
|
add_help: true,
|
|
42
43
|
description: 'VRPC Node.js Agent'
|
|
43
44
|
})
|
|
44
|
-
parser.add_argument(
|
|
45
|
-
'
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
)
|
|
52
|
-
parser.add_argument(
|
|
53
|
-
'
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)
|
|
60
|
-
parser.add_argument(
|
|
61
|
-
'
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
)
|
|
68
|
-
parser.add_argument(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
'
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
'-P',
|
|
86
|
-
'--password',
|
|
87
|
-
{
|
|
88
|
-
help: 'Password',
|
|
89
|
-
default: password
|
|
90
|
-
}
|
|
91
|
-
)
|
|
92
|
-
parser.add_argument(
|
|
93
|
-
'--bestEffort',
|
|
94
|
-
{
|
|
95
|
-
help: 'Calls function on best-effort. Improves performance but may fail under unstable network connections.',
|
|
96
|
-
action: 'store_true'
|
|
97
|
-
}
|
|
98
|
-
)
|
|
99
|
-
parser.add_argument(
|
|
100
|
-
'-V', '-v',
|
|
101
|
-
'--userVersion',
|
|
102
|
-
{
|
|
103
|
-
help: 'User defined agent version. May be checked on the remote side for compatibility checks.',
|
|
104
|
-
required: false,
|
|
105
|
-
default: version,
|
|
106
|
-
dest: 'version'
|
|
107
|
-
}
|
|
108
|
-
)
|
|
109
|
-
parser.add_argument(
|
|
110
|
-
'--version',
|
|
111
|
-
{
|
|
112
|
-
help: 'Returns the VRPC version this client was built with.',
|
|
113
|
-
action: 'version',
|
|
114
|
-
version: 'v2.3.2'
|
|
115
|
-
}
|
|
116
|
-
)
|
|
45
|
+
parser.add_argument('-a', '--agent', {
|
|
46
|
+
help: 'Agent name',
|
|
47
|
+
default: agent || VrpcAgent._generateAgentName()
|
|
48
|
+
})
|
|
49
|
+
parser.add_argument('-d', '--domain', {
|
|
50
|
+
help: 'Domain name',
|
|
51
|
+
default: domain || 'public.vrpc'
|
|
52
|
+
})
|
|
53
|
+
parser.add_argument('-t', '--token', {
|
|
54
|
+
help: 'access token',
|
|
55
|
+
default: token
|
|
56
|
+
})
|
|
57
|
+
parser.add_argument('-b', '--broker', {
|
|
58
|
+
help: 'Broker url',
|
|
59
|
+
default: broker || 'mqtts://vrpc.io:8883'
|
|
60
|
+
})
|
|
61
|
+
parser.add_argument('-u', '--username', {
|
|
62
|
+
help: 'Username',
|
|
63
|
+
default: username
|
|
64
|
+
})
|
|
65
|
+
parser.add_argument('-P', '--password', {
|
|
66
|
+
help: 'Password',
|
|
67
|
+
default: password
|
|
68
|
+
})
|
|
69
|
+
parser.add_argument('--bestEffort', {
|
|
70
|
+
help:
|
|
71
|
+
'Calls function on best-effort. Improves performance but may fail under unstable network connections.',
|
|
72
|
+
action: 'store_true'
|
|
73
|
+
})
|
|
74
|
+
parser.add_argument('-V', '-v', '--userVersion', {
|
|
75
|
+
help:
|
|
76
|
+
'User defined agent version. May be checked on the remote side for compatibility checks.',
|
|
77
|
+
required: false,
|
|
78
|
+
default: version,
|
|
79
|
+
dest: 'version'
|
|
80
|
+
})
|
|
81
|
+
parser.add_argument('--version', {
|
|
82
|
+
help: 'Returns the VRPC version this client was built with.',
|
|
83
|
+
action: 'version',
|
|
84
|
+
version: 'v2.3.2'
|
|
85
|
+
})
|
|
117
86
|
const args = parser.parse_args()
|
|
118
87
|
return new VrpcAgent(args)
|
|
119
88
|
}
|
|
@@ -149,8 +118,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
149
118
|
log = 'console',
|
|
150
119
|
bestEffort = false,
|
|
151
120
|
version = ''
|
|
152
|
-
} = {}
|
|
153
|
-
) {
|
|
121
|
+
} = {}) {
|
|
154
122
|
super()
|
|
155
123
|
this._validateDomain(domain)
|
|
156
124
|
this._validateAgent(agent)
|
|
@@ -172,11 +140,11 @@ class VrpcAgent extends EventEmitter {
|
|
|
172
140
|
this._baseTopic = `${this._domain}/${this._agent}`
|
|
173
141
|
VrpcAdapter.onCallback(this._handleVrpcCallback.bind(this))
|
|
174
142
|
// maps clientId to instanceId
|
|
175
|
-
this.
|
|
176
|
-
this.
|
|
143
|
+
this._isolatedInstances = new Map()
|
|
144
|
+
this._sharedInstances = new Map()
|
|
177
145
|
|
|
178
146
|
// Handle the internal error event in case the user forgot to implement it
|
|
179
|
-
this.on('error',
|
|
147
|
+
this.on('error', err => {
|
|
180
148
|
this._log.debug(`Encountered an error: ${err.message}`)
|
|
181
149
|
})
|
|
182
150
|
}
|
|
@@ -197,8 +165,11 @@ class VrpcAgent extends EventEmitter {
|
|
|
197
165
|
* rejects
|
|
198
166
|
*/
|
|
199
167
|
async serve () {
|
|
200
|
-
const md5 = crypto
|
|
201
|
-
.
|
|
168
|
+
const md5 = crypto
|
|
169
|
+
.createHash('md5')
|
|
170
|
+
.update(this._domain + this._agent)
|
|
171
|
+
.digest('hex')
|
|
172
|
+
.substr(0, 18)
|
|
202
173
|
let username = this._username
|
|
203
174
|
let password = this._password
|
|
204
175
|
if (this._token) {
|
|
@@ -225,10 +196,10 @@ class VrpcAgent extends EventEmitter {
|
|
|
225
196
|
this._log.info('Connecting to MQTT server...')
|
|
226
197
|
const wasEnded = await this._clearPersistedSession()
|
|
227
198
|
if (wasEnded) return
|
|
228
|
-
this._client = mqtt.connect(
|
|
229
|
-
this.
|
|
230
|
-
|
|
231
|
-
)
|
|
199
|
+
this._client = mqtt.connect(this._broker, {
|
|
200
|
+
...this._options,
|
|
201
|
+
clean: false
|
|
202
|
+
})
|
|
232
203
|
this._client.on('connect', this._handleConnect.bind(this))
|
|
233
204
|
this._client.on('reconnect', this._handleReconnect.bind(this))
|
|
234
205
|
this._client.on('error', this._handleError.bind(this))
|
|
@@ -269,30 +240,35 @@ class VrpcAgent extends EventEmitter {
|
|
|
269
240
|
await new Promise(resolve => this._client.end(false, {}, resolve))
|
|
270
241
|
await this._clearPersistedSession()
|
|
271
242
|
} catch (err) {
|
|
272
|
-
this._log.error(
|
|
273
|
-
err,
|
|
274
|
-
`Problem during disconnecting agent: ${err.message}`
|
|
275
|
-
)
|
|
243
|
+
this._log.error(err, `Problem during disconnecting agent: ${err.message}`)
|
|
276
244
|
}
|
|
277
245
|
}
|
|
278
246
|
|
|
279
247
|
static _generateAgentName () {
|
|
280
248
|
const { username } = os.userInfo()
|
|
281
|
-
const pathId = crypto
|
|
249
|
+
const pathId = crypto
|
|
250
|
+
.createHash('md5')
|
|
251
|
+
.update(path.resolve())
|
|
252
|
+
.digest('hex')
|
|
253
|
+
.substr(0, 4)
|
|
282
254
|
return `${username}-${pathId}@${os.hostname()}-${os.platform()}-js`
|
|
283
255
|
}
|
|
284
256
|
|
|
285
257
|
_validateDomain (domain) {
|
|
286
258
|
if (!domain) throw new Error('The domain must be specified')
|
|
287
259
|
if (domain.match(/[+/#*]/)) {
|
|
288
|
-
throw new Error(
|
|
260
|
+
throw new Error(
|
|
261
|
+
'The domain must NOT contain any of those characters: "+", "/", "#", "*"'
|
|
262
|
+
)
|
|
289
263
|
}
|
|
290
264
|
}
|
|
291
265
|
|
|
292
266
|
_validateAgent (agent) {
|
|
293
267
|
if (!agent) throw new Error('The agent must be specified')
|
|
294
268
|
if (agent.match(/[+/#*]/)) {
|
|
295
|
-
throw new Error(
|
|
269
|
+
throw new Error(
|
|
270
|
+
'The agent must NOT contain any of those characters: "+", "/", "#", "*"'
|
|
271
|
+
)
|
|
296
272
|
}
|
|
297
273
|
}
|
|
298
274
|
|
|
@@ -300,7 +276,8 @@ class VrpcAgent extends EventEmitter {
|
|
|
300
276
|
return JSON.stringify({
|
|
301
277
|
status,
|
|
302
278
|
hostname: os.hostname(),
|
|
303
|
-
version: this._version
|
|
279
|
+
version: this._version,
|
|
280
|
+
v: VRPC_PROTOCOL_VERSION
|
|
304
281
|
})
|
|
305
282
|
}
|
|
306
283
|
|
|
@@ -309,11 +286,8 @@ class VrpcAgent extends EventEmitter {
|
|
|
309
286
|
*/
|
|
310
287
|
async _clearPersistedSession () {
|
|
311
288
|
// Clear potentially existing persisted sessions
|
|
312
|
-
const client = mqtt.connect(
|
|
313
|
-
|
|
314
|
-
{ ...this._options, clean: true }
|
|
315
|
-
)
|
|
316
|
-
client.on('error', (err) => this.emit('error', err))
|
|
289
|
+
const client = mqtt.connect(this._broker, { ...this._options, clean: true })
|
|
290
|
+
client.on('error', err => this.emit('error', err))
|
|
317
291
|
client.on('offline', () => this.emit('offline'))
|
|
318
292
|
client.on('reconnect', () => this.emit('reconnect'))
|
|
319
293
|
return new Promise(resolve => {
|
|
@@ -329,29 +303,44 @@ class VrpcAgent extends EventEmitter {
|
|
|
329
303
|
}
|
|
330
304
|
|
|
331
305
|
_mqttPublish (topic, message, options) {
|
|
332
|
-
this._client.publish(
|
|
333
|
-
|
|
334
|
-
|
|
306
|
+
this._client.publish(
|
|
307
|
+
topic,
|
|
308
|
+
message,
|
|
309
|
+
{ qos: this._qos, ...options },
|
|
310
|
+
err => {
|
|
311
|
+
if (err) {
|
|
312
|
+
this._log.warn(
|
|
313
|
+
`Could not publish MQTT message because: ${err.message}`
|
|
314
|
+
)
|
|
315
|
+
}
|
|
335
316
|
}
|
|
336
|
-
|
|
317
|
+
)
|
|
337
318
|
}
|
|
338
319
|
|
|
339
320
|
_mqttSubscribe (topic, options) {
|
|
340
|
-
this._client.subscribe(
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (
|
|
345
|
-
this._log.
|
|
321
|
+
this._client.subscribe(
|
|
322
|
+
topic,
|
|
323
|
+
{ qos: this._qos, ...options },
|
|
324
|
+
(err, granted) => {
|
|
325
|
+
if (err) {
|
|
326
|
+
this._log.warn(
|
|
327
|
+
`Could not subscribe to topic '${topic}', because: ${err.message}`
|
|
328
|
+
)
|
|
329
|
+
} else {
|
|
330
|
+
if (granted.length === 0) {
|
|
331
|
+
this._log.debug(`Already subscribed to topic '${topic}'`)
|
|
332
|
+
}
|
|
346
333
|
}
|
|
347
334
|
}
|
|
348
|
-
|
|
335
|
+
)
|
|
349
336
|
}
|
|
350
337
|
|
|
351
338
|
_mqttUnsubscribe (topic, options) {
|
|
352
|
-
this._client.unsubscribe(topic, options,
|
|
339
|
+
this._client.unsubscribe(topic, options, err => {
|
|
353
340
|
if (err) {
|
|
354
|
-
this._log.warn(
|
|
341
|
+
this._log.warn(
|
|
342
|
+
`Could not unsubscribe from topic: ${topic} because: ${err.message}`
|
|
343
|
+
)
|
|
355
344
|
}
|
|
356
345
|
})
|
|
357
346
|
}
|
|
@@ -377,7 +366,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
377
366
|
}
|
|
378
367
|
|
|
379
368
|
async _ensureConnected () {
|
|
380
|
-
return new Promise(
|
|
369
|
+
return new Promise(resolve => {
|
|
381
370
|
if (this._client.connected) {
|
|
382
371
|
resolve()
|
|
383
372
|
} else {
|
|
@@ -386,11 +375,11 @@ class VrpcAgent extends EventEmitter {
|
|
|
386
375
|
})
|
|
387
376
|
}
|
|
388
377
|
|
|
389
|
-
_handleVrpcCallback (
|
|
390
|
-
const {
|
|
378
|
+
_handleVrpcCallback (json) {
|
|
379
|
+
const { s } = json
|
|
391
380
|
try {
|
|
392
|
-
this._log.debug(`Forwarding callback to: ${
|
|
393
|
-
this._mqttPublish(
|
|
381
|
+
this._log.debug(`Forwarding callback to: ${s} with payload:`, json)
|
|
382
|
+
this._mqttPublish(s, VrpcAgent._stringifySafely(json))
|
|
394
383
|
} catch (err) {
|
|
395
384
|
this._log.warn(
|
|
396
385
|
err,
|
|
@@ -399,10 +388,29 @@ class VrpcAgent extends EventEmitter {
|
|
|
399
388
|
}
|
|
400
389
|
}
|
|
401
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
|
+
|
|
402
406
|
_handleConnect () {
|
|
403
407
|
this._log.info('[OK]')
|
|
404
408
|
if (this._isReconnect) {
|
|
405
409
|
this._publishAgentInfoMessage()
|
|
410
|
+
const classes = this._getClasses()
|
|
411
|
+
classes.forEach(className => {
|
|
412
|
+
this._publishClassInfoMessage(className)
|
|
413
|
+
})
|
|
406
414
|
this.emit('connect')
|
|
407
415
|
this._isReconnect = false
|
|
408
416
|
return
|
|
@@ -424,6 +432,13 @@ class VrpcAgent extends EventEmitter {
|
|
|
424
432
|
classes.forEach(className => {
|
|
425
433
|
this._publishClassInfoMessage(className)
|
|
426
434
|
})
|
|
435
|
+
// Register all pre-existing instances
|
|
436
|
+
for (const [
|
|
437
|
+
instanceId,
|
|
438
|
+
{ className }
|
|
439
|
+
] of VrpcAdapter._instances.entries()) {
|
|
440
|
+
this._subscribeToMethodsOfNewInstance(className, instanceId)
|
|
441
|
+
}
|
|
427
442
|
this.emit('connect')
|
|
428
443
|
}
|
|
429
444
|
|
|
@@ -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(
|
|
@@ -479,7 +495,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
479
495
|
const json = JSON.parse(data.toString())
|
|
480
496
|
this._log.debug(`Message arrived with topic: ${topic} and payload:`, json)
|
|
481
497
|
const tokens = topic.split('/')
|
|
482
|
-
const [
|
|
498
|
+
const [, , className, instance, method] = tokens
|
|
483
499
|
|
|
484
500
|
// Special case: clientInfo message
|
|
485
501
|
if (tokens.length === 4 && tokens[3] === '__clientInfo__') {
|
|
@@ -494,142 +510,136 @@ 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 { data: { _1, e }, sender } = json
|
|
526
|
-
if (!e) this._registerNamedInstance(_1, sender)
|
|
538
|
+
this._registerSharedInstance(instanceId, json.s)
|
|
527
539
|
break
|
|
528
540
|
}
|
|
529
541
|
case '__delete__': {
|
|
530
|
-
const { data: { _1 }, sender } = json
|
|
531
542
|
this._unsubscribeMethodsOfDeletedInstance(className, instance)
|
|
532
|
-
const
|
|
533
|
-
if (
|
|
543
|
+
const wasShared = this._unregisterInstance(json.a[0], json.s)
|
|
544
|
+
if (wasShared) this._publishClassInfoMessage(className)
|
|
534
545
|
break
|
|
535
546
|
}
|
|
536
547
|
}
|
|
537
|
-
|
|
538
|
-
try {
|
|
539
|
-
jsonString = JSON.stringify(json)
|
|
540
|
-
} catch (err) {
|
|
541
|
-
this._log.debug(`Failed serialization of return value for: ${json.context}::${json.method}, because: ${err.message}`)
|
|
542
|
-
json.data.r = '__vrpc::not-serializable__'
|
|
543
|
-
jsonString = JSON.stringify(json)
|
|
544
|
-
}
|
|
545
|
-
if (publishClassInfo && method === '__delete__') {
|
|
546
|
-
this._publishClassInfoMessage(className)
|
|
547
|
-
}
|
|
548
|
-
this._mqttPublish(json.sender, jsonString)
|
|
549
|
-
if (publishClassInfo && method === '__createNamed__') {
|
|
550
|
-
this._publishClassInfoMessage(className)
|
|
551
|
-
}
|
|
548
|
+
this._mqttPublish(json.s, VrpcAgent._stringifySafely(json))
|
|
552
549
|
} catch (err) {
|
|
553
|
-
this._log.error(
|
|
550
|
+
this._log.error(
|
|
551
|
+
err,
|
|
552
|
+
`Problem while handling incoming message: ${err.message}`
|
|
553
|
+
)
|
|
554
554
|
}
|
|
555
555
|
}
|
|
556
556
|
|
|
557
557
|
_handleClientInfoMessage (topic, json) {
|
|
558
558
|
// Client went offline
|
|
559
|
-
const clientId = topic.slice(0, -
|
|
559
|
+
const clientId = topic.slice(0, -15) // /__clientInfo__ = 15
|
|
560
560
|
if (json.status === 'offline') {
|
|
561
|
-
const entry = this.
|
|
562
|
-
if (entry) {
|
|
561
|
+
const entry = this._isolatedInstances.get(clientId)
|
|
562
|
+
if (entry) {
|
|
563
|
+
// anonymous
|
|
563
564
|
entry.forEach(instanceId => {
|
|
564
|
-
const json = {
|
|
565
|
+
const json = { f: '__delete__', a: [instanceId], r: null }
|
|
566
|
+
// TODO Do not mutate!
|
|
565
567
|
VrpcAdapter._call(json)
|
|
566
|
-
|
|
567
|
-
|
|
568
|
+
if (json.r) {
|
|
569
|
+
this._log.debug(`Auto-deleted isolated instance: ${instanceId}`)
|
|
570
|
+
}
|
|
568
571
|
})
|
|
569
572
|
}
|
|
570
573
|
VrpcAdapter._unregisterEventListeners(clientId)
|
|
571
574
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
575
|
+
this.emit('clientGone', clientId)
|
|
572
576
|
}
|
|
573
577
|
}
|
|
574
578
|
|
|
575
|
-
|
|
576
|
-
const entry = this.
|
|
577
|
-
if (entry) {
|
|
579
|
+
_registerIsolatedInstance (instanceId, clientId) {
|
|
580
|
+
const entry = this._isolatedInstances.get(clientId)
|
|
581
|
+
if (entry) {
|
|
582
|
+
// already seen
|
|
578
583
|
entry.add(instanceId)
|
|
579
|
-
} else {
|
|
580
|
-
|
|
581
|
-
|
|
584
|
+
} else {
|
|
585
|
+
// new instance
|
|
586
|
+
this._isolatedInstances.set(clientId, new Set([instanceId]))
|
|
587
|
+
if (!this._sharedInstances.has(clientId)) {
|
|
582
588
|
this._mqttSubscribe(`${clientId}/__clientInfo__`)
|
|
583
589
|
}
|
|
584
590
|
this._log.info(`Tracking lifetime of client: ${clientId}`)
|
|
585
591
|
}
|
|
586
592
|
}
|
|
587
593
|
|
|
588
|
-
|
|
589
|
-
const entry = this.
|
|
590
|
-
if (entry) {
|
|
594
|
+
_registerSharedInstance (instanceId, clientId) {
|
|
595
|
+
const entry = this._sharedInstances.get(clientId)
|
|
596
|
+
if (entry) {
|
|
597
|
+
// already seen
|
|
591
598
|
entry.add(instanceId)
|
|
592
|
-
} else {
|
|
593
|
-
|
|
594
|
-
|
|
599
|
+
} else {
|
|
600
|
+
// new instance
|
|
601
|
+
this._sharedInstances.set(clientId, new Set([instanceId]))
|
|
602
|
+
if (!this._isolatedInstances.has(clientId)) {
|
|
595
603
|
this._mqttSubscribe(`${clientId}/__clientInfo__`)
|
|
596
604
|
}
|
|
597
605
|
this._log.debug(`Tracking lifetime of client: ${clientId}`)
|
|
598
606
|
}
|
|
599
607
|
}
|
|
600
608
|
|
|
601
|
-
|
|
602
|
-
for (const [, instances] of this.
|
|
609
|
+
_hasSharedInstance (instanceId) {
|
|
610
|
+
for (const [, instances] of this._sharedInstances) {
|
|
603
611
|
if (instances.has(instanceId)) return true
|
|
604
612
|
}
|
|
605
613
|
return false
|
|
606
614
|
}
|
|
607
615
|
|
|
608
616
|
_unregisterInstance (instanceId, clientId) {
|
|
609
|
-
const
|
|
610
|
-
if (
|
|
611
|
-
|
|
612
|
-
if (
|
|
613
|
-
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)
|
|
614
622
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
615
623
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
616
624
|
}
|
|
617
625
|
return false
|
|
618
626
|
}
|
|
619
627
|
let found = false
|
|
620
|
-
this.
|
|
628
|
+
this._sharedInstances.forEach(async v => {
|
|
621
629
|
if (v.has(instanceId)) {
|
|
622
630
|
found = true
|
|
623
631
|
v.delete(instanceId)
|
|
624
632
|
if (v.length === 0) {
|
|
625
|
-
this.
|
|
633
|
+
this._sharedInstances.delete(clientId)
|
|
626
634
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
627
635
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
628
636
|
}
|
|
629
637
|
}
|
|
630
638
|
})
|
|
631
639
|
if (!found) {
|
|
632
|
-
this._log.warn(
|
|
640
|
+
this._log.warn(
|
|
641
|
+
`Failed un-registering not registered instance: ${instanceId}`
|
|
642
|
+
)
|
|
633
643
|
return false
|
|
634
644
|
}
|
|
635
645
|
return true
|
|
@@ -678,7 +688,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
678
688
|
* @event VrpcAgent#connect
|
|
679
689
|
* @type {Object}
|
|
680
690
|
* @property {Boolean} sessionPresent - A session from a previous connection is already present
|
|
681
|
-
*/
|
|
691
|
+
*/
|
|
682
692
|
|
|
683
693
|
/**
|
|
684
694
|
* Event 'reconnect'
|
|
@@ -686,7 +696,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
686
696
|
* Emitted when a reconnect starts.
|
|
687
697
|
*
|
|
688
698
|
* @event VrpcAgent#reconnect
|
|
689
|
-
*/
|
|
699
|
+
*/
|
|
690
700
|
|
|
691
701
|
/**
|
|
692
702
|
* Event 'close'
|
|
@@ -729,4 +739,14 @@ class VrpcAgent extends EventEmitter {
|
|
|
729
739
|
* @event VrpcAgent#end
|
|
730
740
|
*/
|
|
731
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
|
+
|
|
732
752
|
module.exports = VrpcAgent
|