vrpc 2.5.0 → 3.0.0-alpha.2
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 +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 +275 -233
- package/vrpc/VrpcAgent.js +204 -185
- package/vrpc/{VrpcRemote.js → VrpcClient.js} +295 -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, 20)
|
|
202
173
|
let username = this._username
|
|
203
174
|
let password = this._password
|
|
204
175
|
if (this._token) {
|
|
@@ -210,7 +181,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
210
181
|
password,
|
|
211
182
|
keepalive: 30,
|
|
212
183
|
connectTimeout: 10 * 1000,
|
|
213
|
-
clientId: `
|
|
184
|
+
clientId: `va3${md5}`,
|
|
214
185
|
rejectUnauthorized: false,
|
|
215
186
|
will: {
|
|
216
187
|
topic: `${this._baseTopic}/__agentInfo__`,
|
|
@@ -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
|
|
@@ -448,7 +456,8 @@ class VrpcAgent extends EventEmitter {
|
|
|
448
456
|
instances: this._getInstances(className),
|
|
449
457
|
memberFunctions: this._getMemberFunctions(className),
|
|
450
458
|
staticFunctions: this._getStaticFunctions(className),
|
|
451
|
-
meta: this._getMetaData(className)
|
|
459
|
+
meta: this._getMetaData(className),
|
|
460
|
+
v: VRPC_PROTOCOL_VERSION
|
|
452
461
|
}
|
|
453
462
|
try {
|
|
454
463
|
this._mqttPublish(
|
|
@@ -486,7 +495,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
486
495
|
const json = JSON.parse(data.toString())
|
|
487
496
|
this._log.debug(`Message arrived with topic: ${topic} and payload:`, json)
|
|
488
497
|
const tokens = topic.split('/')
|
|
489
|
-
const [
|
|
498
|
+
const [, , className, instance, method] = tokens
|
|
490
499
|
|
|
491
500
|
// Special case: clientInfo message
|
|
492
501
|
if (tokens.length === 4 && tokens[3] === '__clientInfo__') {
|
|
@@ -501,142 +510,142 @@ class VrpcAgent extends EventEmitter {
|
|
|
501
510
|
}
|
|
502
511
|
|
|
503
512
|
// Prepare RPC json
|
|
504
|
-
|
|
505
|
-
|
|
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
|
|
506
517
|
|
|
507
518
|
// Mutates json and adds return value
|
|
508
|
-
VrpcAdapter._call(json)
|
|
519
|
+
const mustTrack = VrpcAdapter._call(json)
|
|
520
|
+
|
|
521
|
+
if (mustTrack) {
|
|
522
|
+
this._mqttSubscribe(`${json.s}/__clientInfo__`)
|
|
523
|
+
}
|
|
509
524
|
|
|
510
525
|
// Intersecting life-cycle functions
|
|
511
|
-
let publishClassInfo = false
|
|
512
526
|
switch (method) {
|
|
513
|
-
case '
|
|
527
|
+
case '__createIsolated__': {
|
|
514
528
|
// TODO handle instantiation errors
|
|
515
|
-
const instanceId = json.
|
|
529
|
+
const instanceId = json.r
|
|
516
530
|
// TODO await this
|
|
517
531
|
this._subscribeToMethodsOfNewInstance(className, instanceId)
|
|
518
|
-
this.
|
|
532
|
+
this._registerIsolatedInstance(instanceId, json.s)
|
|
519
533
|
break
|
|
520
534
|
}
|
|
521
|
-
case '
|
|
535
|
+
case '__createShared__': {
|
|
522
536
|
// TODO handle instantiation errors
|
|
523
|
-
const instanceId = json.
|
|
524
|
-
if (!this.
|
|
525
|
-
publishClassInfo = true
|
|
537
|
+
const instanceId = json.r
|
|
538
|
+
if (!this._hasSharedInstance(instanceId)) {
|
|
526
539
|
this._subscribeToMethodsOfNewInstance(className, instanceId)
|
|
540
|
+
this._publishClassInfoMessage(className)
|
|
527
541
|
}
|
|
528
|
-
this.
|
|
529
|
-
break
|
|
530
|
-
}
|
|
531
|
-
case '__getNamed__': {
|
|
532
|
-
const { data: { _1, e }, sender } = json
|
|
533
|
-
if (!e) this._registerNamedInstance(_1, sender)
|
|
542
|
+
this._registerSharedInstance(instanceId, json.s)
|
|
534
543
|
break
|
|
535
544
|
}
|
|
536
545
|
case '__delete__': {
|
|
537
|
-
const { data: { _1 }, sender } = json
|
|
538
546
|
this._unsubscribeMethodsOfDeletedInstance(className, instance)
|
|
539
|
-
const
|
|
540
|
-
if (
|
|
547
|
+
const wasShared = this._unregisterInstance(json.a[0], json.s)
|
|
548
|
+
if (wasShared) this._publishClassInfoMessage(className)
|
|
541
549
|
break
|
|
542
550
|
}
|
|
543
551
|
}
|
|
544
|
-
|
|
545
|
-
try {
|
|
546
|
-
jsonString = JSON.stringify(json)
|
|
547
|
-
} catch (err) {
|
|
548
|
-
this._log.debug(`Failed serialization of return value for: ${json.context}::${json.method}, because: ${err.message}`)
|
|
549
|
-
json.data.r = '__vrpc::not-serializable__'
|
|
550
|
-
jsonString = JSON.stringify(json)
|
|
551
|
-
}
|
|
552
|
-
if (publishClassInfo && method === '__delete__') {
|
|
553
|
-
this._publishClassInfoMessage(className)
|
|
554
|
-
}
|
|
555
|
-
this._mqttPublish(json.sender, jsonString)
|
|
556
|
-
if (publishClassInfo && method === '__createNamed__') {
|
|
557
|
-
this._publishClassInfoMessage(className)
|
|
558
|
-
}
|
|
552
|
+
this._mqttPublish(json.s, VrpcAgent._stringifySafely(json))
|
|
559
553
|
} catch (err) {
|
|
560
|
-
this._log.error(
|
|
554
|
+
this._log.error(
|
|
555
|
+
err,
|
|
556
|
+
`Problem while handling incoming message: ${err.message}`
|
|
557
|
+
)
|
|
561
558
|
}
|
|
562
559
|
}
|
|
563
560
|
|
|
564
561
|
_handleClientInfoMessage (topic, json) {
|
|
565
562
|
// Client went offline
|
|
566
|
-
const clientId = topic.slice(0, -
|
|
563
|
+
const clientId = topic.slice(0, -15) // /__clientInfo__ = 15
|
|
567
564
|
if (json.status === 'offline') {
|
|
568
|
-
const entry = this.
|
|
569
|
-
if (entry) {
|
|
565
|
+
const entry = this._isolatedInstances.get(clientId)
|
|
566
|
+
if (entry) {
|
|
567
|
+
// anonymous
|
|
570
568
|
entry.forEach(instanceId => {
|
|
571
|
-
const json = {
|
|
569
|
+
const json = { f: '__delete__', a: [instanceId], r: null }
|
|
570
|
+
// TODO Do not mutate!
|
|
572
571
|
VrpcAdapter._call(json)
|
|
573
|
-
|
|
574
|
-
|
|
572
|
+
if (json.r) {
|
|
573
|
+
this._log.debug(`Auto-deleted isolated instance: ${instanceId}`)
|
|
574
|
+
}
|
|
575
575
|
})
|
|
576
576
|
}
|
|
577
577
|
VrpcAdapter._unregisterEventListeners(clientId)
|
|
578
578
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
579
|
+
this.emit('clientGone', clientId)
|
|
579
580
|
}
|
|
580
581
|
}
|
|
581
582
|
|
|
582
|
-
|
|
583
|
-
const entry = this.
|
|
584
|
-
if (entry) {
|
|
583
|
+
_registerIsolatedInstance (instanceId, clientId) {
|
|
584
|
+
const entry = this._isolatedInstances.get(clientId)
|
|
585
|
+
if (entry) {
|
|
586
|
+
// already seen
|
|
585
587
|
entry.add(instanceId)
|
|
586
|
-
} else {
|
|
587
|
-
|
|
588
|
-
|
|
588
|
+
} else {
|
|
589
|
+
// new instance
|
|
590
|
+
this._isolatedInstances.set(clientId, new Set([instanceId]))
|
|
591
|
+
if (!this._sharedInstances.has(clientId)) {
|
|
589
592
|
this._mqttSubscribe(`${clientId}/__clientInfo__`)
|
|
590
593
|
}
|
|
591
594
|
this._log.info(`Tracking lifetime of client: ${clientId}`)
|
|
592
595
|
}
|
|
593
596
|
}
|
|
594
597
|
|
|
595
|
-
|
|
596
|
-
const entry = this.
|
|
597
|
-
if (entry) {
|
|
598
|
+
_registerSharedInstance (instanceId, clientId) {
|
|
599
|
+
const entry = this._sharedInstances.get(clientId)
|
|
600
|
+
if (entry) {
|
|
601
|
+
// already seen
|
|
598
602
|
entry.add(instanceId)
|
|
599
|
-
} else {
|
|
600
|
-
|
|
601
|
-
|
|
603
|
+
} else {
|
|
604
|
+
// new instance
|
|
605
|
+
this._sharedInstances.set(clientId, new Set([instanceId]))
|
|
606
|
+
if (!this._isolatedInstances.has(clientId)) {
|
|
602
607
|
this._mqttSubscribe(`${clientId}/__clientInfo__`)
|
|
603
608
|
}
|
|
604
609
|
this._log.debug(`Tracking lifetime of client: ${clientId}`)
|
|
605
610
|
}
|
|
606
611
|
}
|
|
607
612
|
|
|
608
|
-
|
|
609
|
-
for (const [, instances] of this.
|
|
613
|
+
_hasSharedInstance (instanceId) {
|
|
614
|
+
for (const [, instances] of this._sharedInstances) {
|
|
610
615
|
if (instances.has(instanceId)) return true
|
|
611
616
|
}
|
|
612
617
|
return false
|
|
613
618
|
}
|
|
614
619
|
|
|
615
620
|
_unregisterInstance (instanceId, clientId) {
|
|
616
|
-
const
|
|
617
|
-
if (
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
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)
|
|
621
627
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
622
628
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
623
629
|
}
|
|
624
630
|
return false
|
|
625
631
|
}
|
|
626
632
|
let found = false
|
|
627
|
-
this.
|
|
633
|
+
this._sharedInstances.forEach(async v => {
|
|
628
634
|
if (v.has(instanceId)) {
|
|
629
635
|
found = true
|
|
636
|
+
VrpcAdapter._unregisterEventListenersOfInstance(instanceId)
|
|
630
637
|
v.delete(instanceId)
|
|
631
638
|
if (v.length === 0) {
|
|
632
|
-
this.
|
|
639
|
+
this._sharedInstances.delete(clientId)
|
|
633
640
|
this._mqttUnsubscribe(`${clientId}/__clientInfo__`)
|
|
634
641
|
this._log.debug(`Stopped tracking lifetime of client: ${clientId}`)
|
|
635
642
|
}
|
|
636
643
|
}
|
|
637
644
|
})
|
|
638
645
|
if (!found) {
|
|
639
|
-
this._log.warn(
|
|
646
|
+
this._log.warn(
|
|
647
|
+
`Failed un-registering not registered instance: ${instanceId}`
|
|
648
|
+
)
|
|
640
649
|
return false
|
|
641
650
|
}
|
|
642
651
|
return true
|
|
@@ -685,7 +694,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
685
694
|
* @event VrpcAgent#connect
|
|
686
695
|
* @type {Object}
|
|
687
696
|
* @property {Boolean} sessionPresent - A session from a previous connection is already present
|
|
688
|
-
*/
|
|
697
|
+
*/
|
|
689
698
|
|
|
690
699
|
/**
|
|
691
700
|
* Event 'reconnect'
|
|
@@ -693,7 +702,7 @@ class VrpcAgent extends EventEmitter {
|
|
|
693
702
|
* Emitted when a reconnect starts.
|
|
694
703
|
*
|
|
695
704
|
* @event VrpcAgent#reconnect
|
|
696
|
-
*/
|
|
705
|
+
*/
|
|
697
706
|
|
|
698
707
|
/**
|
|
699
708
|
* Event 'close'
|
|
@@ -736,4 +745,14 @@ class VrpcAgent extends EventEmitter {
|
|
|
736
745
|
* @event VrpcAgent#end
|
|
737
746
|
*/
|
|
738
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
|
+
|
|
739
758
|
module.exports = VrpcAgent
|