iosignal 4.8.0 → 5.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/dist/io.d.ts +3 -1
- package/dist/io.js +3 -3
- package/dist/io.js.map +1 -1
- package/dist/io.min.js +4 -4
- package/dist/io.min.js.map +1 -1
- package/dist/iosignal.js +120 -524
- package/dist/types/client/IOCore.d.ts +1 -0
- package/dist/types/client/browser/IOWebSocket.d.ts +1 -1
- package/dist/types/common/constants.d.ts +7 -6
- package/package.json +2 -2
- package/src/auth/BohoAuth.js +36 -41
- package/src/client/IOCore.js +27 -26
- package/src/common/constants.js +10 -17
- package/src/server/Manager.js +7 -7
- package/src/server/Metrics.js +155 -0
- package/src/server/RemoteCore.js +6 -19
- package/src/server/Server.js +15 -15
- package/src/server/serverOption.js +1 -2
- package/src/services/sudoService.js +5 -5
|
@@ -74,6 +74,6 @@ export default class IO extends IOCore {
|
|
|
74
74
|
socket_send(data: BufferSource): void;
|
|
75
75
|
}
|
|
76
76
|
export type Boho = import("boho").Boho;
|
|
77
|
-
export type MBP =
|
|
77
|
+
export type MBP = typeof import("meta-buffer-pack");
|
|
78
78
|
export type Buffer = import("boho").Buffer;
|
|
79
79
|
import { IOCore } from "../IOCore.js";
|
|
@@ -2,11 +2,14 @@ export type STATE = {
|
|
|
2
2
|
CONNECTING: number;
|
|
3
3
|
OPEN: number;
|
|
4
4
|
SERVER_READY: number;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
READY: number;
|
|
5
|
+
AUTH_REQ: number;
|
|
6
|
+
AUTH_RES: number;
|
|
8
7
|
AUTH_FAIL: number;
|
|
9
8
|
AUTH_CLEAR: number;
|
|
9
|
+
CID_REQ: number;
|
|
10
|
+
CID_RES: number;
|
|
11
|
+
READY: number;
|
|
12
|
+
CLOSING: number;
|
|
10
13
|
CLOSED: number;
|
|
11
14
|
STOP: number;
|
|
12
15
|
REDIRECTING: number;
|
|
@@ -16,9 +19,7 @@ export namespace STATE {
|
|
|
16
19
|
let OPEN: number;
|
|
17
20
|
let SERVER_READY: number;
|
|
18
21
|
let AUTH_REQ: number;
|
|
19
|
-
let
|
|
20
|
-
let AUTH_HMAC: number;
|
|
21
|
-
let AUTH_ACK: number;
|
|
22
|
+
let AUTH_RES: number;
|
|
22
23
|
let AUTH_FAIL: number;
|
|
23
24
|
let AUTH_CLEAR: number;
|
|
24
25
|
let CID_REQ: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iosignal",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "iosignal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"typescript": "^5.8.3"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"boho": "^
|
|
40
|
+
"boho": "^2.0.0",
|
|
41
41
|
"eventemitter3": "^5.0.1",
|
|
42
42
|
"meta-buffer-pack": "^2.1.0",
|
|
43
43
|
"ws": "^8.18.3"
|
package/src/auth/BohoAuth.js
CHANGED
|
@@ -12,7 +12,7 @@ export class BohoAuth {
|
|
|
12
12
|
throw new Error('BohoAuth must be initialized with a keyProvider.');
|
|
13
13
|
}
|
|
14
14
|
this.keyProvider = keyProvider;
|
|
15
|
-
|
|
15
|
+
this.keepOldConnection = true;
|
|
16
16
|
this.authLogger;
|
|
17
17
|
if (serverOption.fileLogger.auth.use) {
|
|
18
18
|
this.authLogger = new FileLogger(serverOption.fileLogger.auth.path)
|
|
@@ -27,42 +27,40 @@ export class BohoAuth {
|
|
|
27
27
|
}
|
|
28
28
|
// console.log('##### AUTH_FAIL reason: ', reason)
|
|
29
29
|
peer.setState(STATE.AUTH_FAIL)
|
|
30
|
-
peer.send(Buffer.from([Boho.BohoMsg.AUTH_FAIL]))
|
|
31
30
|
// add some delay time.
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
setTimeout(e => {
|
|
32
|
+
peer.send(Buffer.from([Boho.BohoMsg.AUTH_FAIL]))
|
|
33
|
+
}, serverOption.auth.delay_auth_fail)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
async verify_auth_req(auth_req, peer) {
|
|
37
38
|
try {
|
|
38
39
|
//1. unpack
|
|
39
|
-
let infoPack = MBP.unpack(
|
|
40
|
-
|
|
40
|
+
let infoPack = MBP.unpack(auth_req, Boho.Meta.AUTH_REQ)
|
|
41
41
|
if (!infoPack) {
|
|
42
42
|
this.send_auth_fail(peer, 'unpack auth_pack fail');
|
|
43
43
|
return
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
let id = ""
|
|
47
47
|
if (infoPack.id8.includes(0)) {
|
|
48
48
|
id = decoder.decode(infoPack.id8.subarray(0, infoPack.id8.indexOf(0)));
|
|
49
49
|
} else {
|
|
50
50
|
id = decoder.decode(infoPack.id8);
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
//2. get key of id from DB
|
|
54
54
|
let authInfo = await this.keyProvider.getAuth(id)
|
|
55
|
-
|
|
56
55
|
if (!authInfo) {
|
|
57
56
|
this.send_auth_fail(peer, 'NO ID:' + id);
|
|
58
57
|
return
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
if (serverOption.debug.showAuthInfo) {
|
|
62
|
-
console.log('[debug]showAuthInfo',authInfo
|
|
61
|
+
console.log('[debug]showAuthInfo', authInfo)
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
// console.log('db authInfo.key: ', authInfo.key)
|
|
66
64
|
peer.boho.copy_id8(infoPack.id8)
|
|
67
65
|
// type of key
|
|
68
66
|
let authKey;
|
|
@@ -77,46 +75,47 @@ export class BohoAuth {
|
|
|
77
75
|
}
|
|
78
76
|
|
|
79
77
|
//3. check hmac
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// console.log('auth_ack', auth_ack )
|
|
84
|
-
if (!auth_ack) {
|
|
78
|
+
let auth_res = peer.boho.verify_auth_req(infoPack)
|
|
79
|
+
if (!auth_res) {
|
|
85
80
|
this.send_auth_fail(peer, 'hmac dismatched');
|
|
86
81
|
return
|
|
87
82
|
}
|
|
88
83
|
|
|
89
84
|
//4. get info
|
|
90
|
-
// console.log('#### auth success' )
|
|
91
85
|
|
|
92
86
|
//5. check duplicate login.
|
|
93
|
-
// current policy. Deny duplicate login
|
|
94
|
-
|
|
95
|
-
// duplicate login policy.
|
|
96
|
-
// 1. send clear_auth signal to the old connection.
|
|
97
|
-
// 2. close old connection.
|
|
98
|
-
// 3. close new connection. retry from begining.
|
|
99
87
|
if (peer.manager.cid2remote.has(authInfo.cid)) {
|
|
100
88
|
let old = peer.manager.cid2remote.get(authInfo.cid)
|
|
89
|
+
console.log('[WARN]DUPLICATE_LOGIN detected.', old.cid)
|
|
90
|
+
old.ping(); // check the connection.
|
|
101
91
|
if (old == peer) {
|
|
102
|
-
console.log('## trying RELOGIN
|
|
92
|
+
console.log('## trying to RELOGIN after login.')
|
|
103
93
|
return
|
|
104
94
|
}
|
|
105
|
-
|
|
106
|
-
let
|
|
95
|
+
|
|
96
|
+
let authClearSignal = MBP.pack(
|
|
107
97
|
MBP.MB('#MsgType', '8', IOMsg.AUTH_CLEAR),
|
|
108
98
|
MBP.MB('#reason', 'duplicate login.')
|
|
109
99
|
)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
100
|
+
if (this.keepOldConnection) { // default true
|
|
101
|
+
// keep old connection. reject new connection by sending auth_fail signal.
|
|
102
|
+
// peer.send(authClearSignal)
|
|
103
|
+
if( peer.socketType == 'websocket'){
|
|
104
|
+
this.send_auth_fail(peer, 'duplicate login')
|
|
105
|
+
}else{
|
|
106
|
+
peer.close(); //arduino
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
// accept new connection. stop the old connection by sending auth_clear signal.
|
|
110
|
+
old.send(authClearSignal)
|
|
111
|
+
peer.close()
|
|
112
|
+
}
|
|
113
113
|
return
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
//6. delete current (temp rand)cid if exist.
|
|
116
|
+
//6. delete current (or anonymouse)cid if exist.
|
|
118
117
|
if (peer.cid) {
|
|
119
|
-
console.log('
|
|
118
|
+
// console.log('if peer has cid', peer.cid)
|
|
120
119
|
peer.manager.cid2remote.delete(peer.cid)
|
|
121
120
|
}
|
|
122
121
|
|
|
@@ -124,7 +123,7 @@ export class BohoAuth {
|
|
|
124
123
|
peer.did = id
|
|
125
124
|
peer.cid = authInfo.cid
|
|
126
125
|
peer.nick = authInfo.cid // temporary: nick as cid
|
|
127
|
-
if(
|
|
126
|
+
if (authInfo.uid) peer.uid = authInfo.uid
|
|
128
127
|
|
|
129
128
|
//8. setting quota level.
|
|
130
129
|
let quotaLevel = serverOption.defaultQuotaIndex;
|
|
@@ -133,7 +132,6 @@ export class BohoAuth {
|
|
|
133
132
|
let newQuota = quotaTable[quotaLevel];
|
|
134
133
|
if (!newQuota) {
|
|
135
134
|
let err = 'no index quotaTable for auth.level: ' + quotaLevel
|
|
136
|
-
console.log('##AUTH:DATA ERROR##', err)
|
|
137
135
|
this.send_auth_fail(peer, err);
|
|
138
136
|
return
|
|
139
137
|
} else {
|
|
@@ -145,17 +143,14 @@ export class BohoAuth {
|
|
|
145
143
|
console.log('## LOGIN ADMIN CID:', peer.cid)
|
|
146
144
|
peer.isAdmin = true;
|
|
147
145
|
}
|
|
148
|
-
// console.log('auth: peer.quota', peer.quota )
|
|
149
146
|
// send quota.level
|
|
150
147
|
peer.send(Buffer.from([IOMsg.QUOTA_LEVEL, quotaLevel]))
|
|
151
148
|
|
|
152
149
|
//9. set cid2remote
|
|
153
150
|
peer.manager.cid2remote.set(peer.cid, peer)
|
|
154
|
-
// console.log( 'done: cid2remote:', peer.manager.cid2remote.keys() )
|
|
155
|
-
// console.log("LOGIN: ", `id: ${ peer.did}(${peer.cid})` )
|
|
156
151
|
//10. send ack.
|
|
157
|
-
peer.send(
|
|
158
|
-
peer.setState(STATE.
|
|
152
|
+
peer.send(auth_res)
|
|
153
|
+
peer.setState(STATE.AUTH_RES)
|
|
159
154
|
if (this.authLogger) {
|
|
160
155
|
let peerInfo = `OK #${peer.ssid} cid: ${peer.cid} did:${peer.did}`
|
|
161
156
|
if (peer.isAdmin) peerInfo = "#ADMIN# " + peerInfo
|
|
@@ -163,9 +158,9 @@ export class BohoAuth {
|
|
|
163
158
|
}
|
|
164
159
|
return authInfo
|
|
165
160
|
} catch (error) {
|
|
161
|
+
console.log('verify_auth_req error', error)
|
|
166
162
|
this.send_auth_fail(peer, '[BohoAuth]caught: unknown error:' + error);
|
|
167
163
|
}
|
|
168
|
-
|
|
169
164
|
}
|
|
170
165
|
|
|
171
166
|
async getAuth(id) {
|
package/src/client/IOCore.js
CHANGED
|
@@ -114,6 +114,8 @@ export class IOCore extends EventEmitter {
|
|
|
114
114
|
* @type {Boho}
|
|
115
115
|
*/
|
|
116
116
|
this.boho = new Boho()
|
|
117
|
+
|
|
118
|
+
this.serverTimeNonce = Buffer.alloc( Boho.MetaSize.SERVER_TIME_NONCE );
|
|
117
119
|
/**
|
|
118
120
|
* Indicates if the connection is TLS (wss).
|
|
119
121
|
* @type {boolean}
|
|
@@ -246,6 +248,7 @@ export class IOCore extends EventEmitter {
|
|
|
246
248
|
stop() {
|
|
247
249
|
this.autoReconnect = false;
|
|
248
250
|
this.close();
|
|
251
|
+
this.cid = ''
|
|
249
252
|
this.stateChange('stop','stop');
|
|
250
253
|
}
|
|
251
254
|
|
|
@@ -344,10 +347,13 @@ export class IOCore extends EventEmitter {
|
|
|
344
347
|
* @returns {this}
|
|
345
348
|
*/
|
|
346
349
|
login(id, key) {
|
|
347
|
-
this.
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
350
|
+
if( this.serverTimeNonce ){
|
|
351
|
+
console.log('iosignal.login serverTimeNonce', this.serverTimeNonce)
|
|
352
|
+
this.auth(id, key)
|
|
353
|
+
this.useAuth = true
|
|
354
|
+
let auth_pack = this.boho.auth_req(this.serverTimeNonce )
|
|
355
|
+
this.send(auth_pack)
|
|
356
|
+
}
|
|
351
357
|
return this
|
|
352
358
|
}
|
|
353
359
|
|
|
@@ -496,12 +502,15 @@ export class IOCore extends EventEmitter {
|
|
|
496
502
|
this.redirect(url)
|
|
497
503
|
break;
|
|
498
504
|
|
|
499
|
-
case
|
|
505
|
+
case Boho.BohoMsg.SERVER_TIME_NONCE: // SERVER_READY
|
|
500
506
|
this.stateChange('server_ready', 'server_ready')
|
|
501
507
|
if (this.useAuth) {
|
|
502
|
-
this.send(this.boho.auth_req())
|
|
503
|
-
|
|
508
|
+
this.send(this.boho.auth_req(buffer))
|
|
509
|
+
this.stateChange('auth_req','auth_req')
|
|
510
|
+
// CID_REQ will be called, after auth_res.
|
|
504
511
|
} else {
|
|
512
|
+
// keep server_time_nonce for manual login()
|
|
513
|
+
buffer.copy( this.serverTimeNonce);
|
|
505
514
|
// CID_REQ here, if not using auth.
|
|
506
515
|
this.send(Buffer.from([IOMsg.CID_REQ]))
|
|
507
516
|
}
|
|
@@ -548,6 +557,7 @@ export class IOCore extends EventEmitter {
|
|
|
548
557
|
> cid_sub message: tag includes cid and @ both : 'cid@*'
|
|
549
558
|
> ch_sub message: else.
|
|
550
559
|
*/
|
|
560
|
+
// console.log('payloadType', payloadType )
|
|
551
561
|
switch (payloadType) {
|
|
552
562
|
|
|
553
563
|
case PAYLOAD_TYPE.EMPTY:
|
|
@@ -567,7 +577,7 @@ export class IOCore extends EventEmitter {
|
|
|
567
577
|
}
|
|
568
578
|
let oneString = decoder.decode(payloadStringWithoutNull)
|
|
569
579
|
if (tag.indexOf('@') === 0) this.emit('@', tag, oneString)
|
|
570
|
-
|
|
580
|
+
else {
|
|
571
581
|
this.emit(tag, tag, oneString)
|
|
572
582
|
this.emit('message', tag, oneString)
|
|
573
583
|
}
|
|
@@ -575,7 +585,7 @@ export class IOCore extends EventEmitter {
|
|
|
575
585
|
|
|
576
586
|
case PAYLOAD_TYPE.BINARY:
|
|
577
587
|
if (tag.indexOf('@') === 0) this.emit('@', tag, payloadBuffer)
|
|
578
|
-
|
|
588
|
+
else {
|
|
579
589
|
this.emit(tag, tag, payloadBuffer)
|
|
580
590
|
this.emit('message', tag, payloadBuffer)
|
|
581
591
|
}
|
|
@@ -585,7 +595,7 @@ export class IOCore extends EventEmitter {
|
|
|
585
595
|
let oneObjectBuffer = decoder.decode(payloadBuffer)
|
|
586
596
|
let oneJSONObject = JSON.parse(oneObjectBuffer)
|
|
587
597
|
if (tag.indexOf('@') === 0) this.emit('@', tag, oneJSONObject)
|
|
588
|
-
|
|
598
|
+
else {
|
|
589
599
|
this.emit(tag, tag, oneJSONObject)
|
|
590
600
|
this.emit('message', tag, oneJSONObject)
|
|
591
601
|
}
|
|
@@ -595,7 +605,7 @@ export class IOCore extends EventEmitter {
|
|
|
595
605
|
let mjsonBuffer = decoder.decode(payloadBuffer)
|
|
596
606
|
let mjson = JSON.parse(mjsonBuffer)
|
|
597
607
|
if (tag.indexOf('@') === 0) this.emit('@', tag, ...mjson)
|
|
598
|
-
|
|
608
|
+
else {
|
|
599
609
|
this.emit(tag, tag, ...mjson)
|
|
600
610
|
this.emit('message', tag, ...mjson)
|
|
601
611
|
}
|
|
@@ -604,7 +614,7 @@ export class IOCore extends EventEmitter {
|
|
|
604
614
|
case PAYLOAD_TYPE.MBA:
|
|
605
615
|
let mbaObject = MBP.unpack(payloadBuffer)
|
|
606
616
|
if (tag.indexOf('@') === 0) this.emit('@', tag, ...mbaObject.args)
|
|
607
|
-
|
|
617
|
+
else {
|
|
608
618
|
this.emit(tag, tag, ...mbaObject.args)
|
|
609
619
|
this.emit('message', tag, ...mbaObject.args)
|
|
610
620
|
}
|
|
@@ -624,26 +634,16 @@ export class IOCore extends EventEmitter {
|
|
|
624
634
|
this.testPromise(buffer)
|
|
625
635
|
break;
|
|
626
636
|
|
|
627
|
-
case Boho.BohoMsg.AUTH_NONCE:
|
|
628
|
-
this.stateChange('auth_nonce','server sent auth_nonce.')
|
|
629
|
-
let auth_hmac = this.boho.auth_hmac(buffer)
|
|
630
|
-
if (auth_hmac) {
|
|
631
|
-
this.send(auth_hmac)
|
|
632
|
-
} else {
|
|
633
|
-
this.stateChange('auth_fail', 'boho.auth_hmac() invalid auth_nonce.')
|
|
634
|
-
}
|
|
635
|
-
break;
|
|
636
|
-
|
|
637
637
|
case Boho.BohoMsg.AUTH_FAIL:
|
|
638
638
|
this.stateChange('auth_fail', 'auth_fail from server.')
|
|
639
639
|
break;
|
|
640
640
|
|
|
641
|
-
case Boho.BohoMsg.
|
|
642
|
-
if (this.boho.
|
|
643
|
-
this.stateChange('
|
|
641
|
+
case Boho.BohoMsg.AUTH_RES:
|
|
642
|
+
if (this.boho.verify_auth_res(buffer)) {
|
|
643
|
+
this.stateChange('auth_res', 'server sent auth_res')
|
|
644
644
|
this.send(Buffer.from([IOMsg.CID_REQ]))
|
|
645
645
|
} else {
|
|
646
|
-
this.stateChange('auth_fail', '
|
|
646
|
+
this.stateChange('auth_fail', 'verify_auth_res() invalid server_hmac')
|
|
647
647
|
}
|
|
648
648
|
break;
|
|
649
649
|
|
|
@@ -871,6 +871,7 @@ export class IOCore extends EventEmitter {
|
|
|
871
871
|
*/
|
|
872
872
|
signal_e2e(tag, data, key) {
|
|
873
873
|
|
|
874
|
+
if( !this.boho.isAuthorized ) return;
|
|
874
875
|
if (typeof tag !== 'string') throw TypeError('tag should be string.')
|
|
875
876
|
let tagEncoded = encoder.encode(tag)
|
|
876
877
|
let dataPack = MBP.B8(data)
|
package/src/common/constants.js
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
* @property {number} CONNECTING
|
|
5
5
|
* @property {number} OPEN
|
|
6
6
|
* @property {number} SERVER_READY
|
|
7
|
-
* @property {number}
|
|
8
|
-
* @property {number}
|
|
9
|
-
* @property {number} READY
|
|
7
|
+
* @property {number} AUTH_REQ
|
|
8
|
+
* @property {number} AUTH_RES
|
|
10
9
|
* @property {number} AUTH_FAIL
|
|
11
10
|
* @property {number} AUTH_CLEAR
|
|
11
|
+
* @property {number} CID_REQ
|
|
12
|
+
* @property {number} CID_RES
|
|
13
|
+
* @property {number} READY
|
|
12
14
|
* @property {number} CLOSING
|
|
13
15
|
* @property {number} CLOSED
|
|
14
16
|
* @property {number} STOP
|
|
@@ -20,11 +22,10 @@ export const STATE = {
|
|
|
20
22
|
OPEN: 1,
|
|
21
23
|
SERVER_READY: 10,
|
|
22
24
|
AUTH_REQ: 11,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
AUTH_CLEAR: 16,
|
|
25
|
+
AUTH_RES: 12,
|
|
26
|
+
AUTH_FAIL: 13,
|
|
27
|
+
AUTH_CLEAR: 14,
|
|
28
|
+
|
|
28
29
|
CID_REQ: 17,
|
|
29
30
|
CID_RES: 18,
|
|
30
31
|
READY: 19,
|
|
@@ -139,15 +140,7 @@ export let IOMsg = {
|
|
|
139
140
|
|
|
140
141
|
// DO NOT USE: 0xB0~ 0xBF
|
|
141
142
|
// Boho module using this numbers.
|
|
142
|
-
|
|
143
|
-
// AUTH_NONCE: 0xB1,
|
|
144
|
-
// AUTH_HMAC: 0xB2,
|
|
145
|
-
// AUTH_ACK: 0xB3,
|
|
146
|
-
// AUTH_FAIL: 0xB4,
|
|
147
|
-
// AUTH_EXT: 0xB5,
|
|
148
|
-
// ENC_PACK : 0xB6,
|
|
149
|
-
// ENC_E2E : 0xB7,
|
|
150
|
-
// ENC_488 : 0xB8
|
|
143
|
+
|
|
151
144
|
// reserved ~0xBF
|
|
152
145
|
|
|
153
146
|
// C. IO status contorl.
|
package/src/server/Manager.js
CHANGED
|
@@ -4,7 +4,7 @@ import { serverOption } from './serverOption.js'
|
|
|
4
4
|
import { IOMsg, STATE } from '../common/constants.js'
|
|
5
5
|
import { getSignalPack } from '../common/payload.js';
|
|
6
6
|
import { FileLogger } from './FileLogger.js'
|
|
7
|
-
import {
|
|
7
|
+
import { Metrics } from './Metrics.js'
|
|
8
8
|
|
|
9
9
|
const encoder = new TextEncoder()
|
|
10
10
|
const decoder = new TextDecoder()
|
|
@@ -36,7 +36,7 @@ export class Manager {
|
|
|
36
36
|
this.channel_map = new Map() // key: channelName -> value: < remote : Set >
|
|
37
37
|
this.CHANNEL_PREFIX = 'CH:'
|
|
38
38
|
this.CID_PREFIX = 'CID:'
|
|
39
|
-
this.
|
|
39
|
+
this.metrics = new Metrics(this)
|
|
40
40
|
this.lastSSID = 0;
|
|
41
41
|
|
|
42
42
|
this.pingIntervalID = setInterval(e => {
|
|
@@ -72,7 +72,7 @@ export class Manager {
|
|
|
72
72
|
socket.isAlive = true;
|
|
73
73
|
let remote = new Remote(socket, req, this)
|
|
74
74
|
this.remotes.add(remote)
|
|
75
|
-
remote.send(
|
|
75
|
+
remote.send( remote.boho.server_time_nonce() )
|
|
76
76
|
remote.setState(STATE.SERVER_READY)
|
|
77
77
|
this.lastSSID = remote.ssid;
|
|
78
78
|
let connectionInfo = `+ IP:${remote.ip} #${remote.ssid} ${socket.socketType === 'websocket' ? "WS" : "CS"} `;
|
|
@@ -402,18 +402,18 @@ export class Manager {
|
|
|
402
402
|
}
|
|
403
403
|
|
|
404
404
|
if (serverOption.showChannel > 0) {
|
|
405
|
-
this.
|
|
405
|
+
this.metrics.channels(serverOption.showChannel);
|
|
406
406
|
}
|
|
407
407
|
let mode = parseInt(serverOption.showMetric)
|
|
408
408
|
switch (mode) {
|
|
409
409
|
case 1:
|
|
410
|
-
this.
|
|
410
|
+
this.metrics.oneline(true);
|
|
411
411
|
break;
|
|
412
412
|
case 2:
|
|
413
|
-
this.
|
|
413
|
+
this.metrics.getRemotes(true);
|
|
414
414
|
break;
|
|
415
415
|
case 3:
|
|
416
|
-
this.
|
|
416
|
+
this.metrics.getChannelList(true);
|
|
417
417
|
break;
|
|
418
418
|
default:
|
|
419
419
|
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { memoryUsage } from 'process';
|
|
2
|
+
import { ENC_MODE } from '../common/constants.js'
|
|
3
|
+
|
|
4
|
+
const MAX = 10
|
|
5
|
+
let period = 10000
|
|
6
|
+
|
|
7
|
+
export class Metrics {
|
|
8
|
+
constructor(manager) {
|
|
9
|
+
this.manager = manager;
|
|
10
|
+
|
|
11
|
+
this.metricsPack = {
|
|
12
|
+
remotes: [0],
|
|
13
|
+
channels: [0],
|
|
14
|
+
txBytes: [0],
|
|
15
|
+
rxBytes: [0],
|
|
16
|
+
unixTime: [Math.floor(Date.now() / 1000)],
|
|
17
|
+
period: 0
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
this.tickId = setInterval(e => {
|
|
21
|
+
|
|
22
|
+
this.metricsPack.remotes.push(this.manager.remotes.size)
|
|
23
|
+
if (this.metricsPack.remotes.length > MAX) this.metricsPack.remotes.shift()
|
|
24
|
+
|
|
25
|
+
this.metricsPack.channels.push(this.manager.channel_map.size)
|
|
26
|
+
if (this.metricsPack.channels.length > MAX) this.metricsPack.channels.shift()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
this.metricsPack.txBytes.push(this.manager.txBytes)
|
|
30
|
+
if (this.metricsPack.txBytes.length > MAX) this.metricsPack.txBytes.shift()
|
|
31
|
+
|
|
32
|
+
this.metricsPack.rxBytes.push(this.manager.rxBytes)
|
|
33
|
+
if (this.metricsPack.rxBytes.length > MAX) this.metricsPack.rxBytes.shift()
|
|
34
|
+
|
|
35
|
+
this.metricsPack.unixTime = Math.floor(Date.now() / 1000)
|
|
36
|
+
this.metricsPack.period = period;
|
|
37
|
+
this.manager.txBytes = 0
|
|
38
|
+
this.manager.rxBytes = 0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
}, 10000)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
oneline(prn) {
|
|
47
|
+
let memoryUse = memoryUsage();
|
|
48
|
+
let memoryInfo = [{
|
|
49
|
+
...memoryUse
|
|
50
|
+
}]
|
|
51
|
+
|
|
52
|
+
if (prn) console.table(memoryInfo, ['rss', 'heapTotal', 'heapUsed', 'external', 'arrayBuffers']);
|
|
53
|
+
|
|
54
|
+
let metric = [{
|
|
55
|
+
lastSSID: this.manager.lastSSID,
|
|
56
|
+
remotes: this.manager.remotes.size,
|
|
57
|
+
channels: this.manager.channel_map.size,
|
|
58
|
+
txBytes: this.manager.txBytes,
|
|
59
|
+
rxBytes: this.manager.rxBytes,
|
|
60
|
+
}]
|
|
61
|
+
|
|
62
|
+
if (prn) console.table(metric, ['lastSSID', 'remotes', 'channels', 'txBytes', 'rxBytes']);
|
|
63
|
+
|
|
64
|
+
return metric[0]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getRemotes(prn) {
|
|
68
|
+
let remoteList = Array.from(this.manager.remotes.values())
|
|
69
|
+
let remoteStates = remoteList.map(v => {
|
|
70
|
+
return "#" + v.ssid + ":" + v.cid + "(" + v.state + ")"
|
|
71
|
+
})
|
|
72
|
+
if (prn) console.table(remoteStates)
|
|
73
|
+
return remoteStates
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getCIdList(prn) {
|
|
77
|
+
let CIdList = Array.from(this.manager.cid2remote.keys())
|
|
78
|
+
if (prn) console.table(CIdList)
|
|
79
|
+
return CIdList
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getChannelList(prn) {
|
|
83
|
+
let chList = Array.from(this.manager.channel_map.keys())
|
|
84
|
+
if (prn) console.table(chList)
|
|
85
|
+
return chList
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getSubscribers(ch) {
|
|
89
|
+
let list = [];
|
|
90
|
+
if (this.manager.channel_map.has(ch)) {
|
|
91
|
+
let subscribers = this.manager.channel_map.get(ch) //set
|
|
92
|
+
subscribers.forEach(c => {
|
|
93
|
+
list.push(c.cid)
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
// console.log(list)
|
|
97
|
+
return list
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getRemoteByCId(cid, mode = 1) {
|
|
101
|
+
|
|
102
|
+
if (this.manager.cid2remote.has(cid)) {
|
|
103
|
+
let remote = this.manager.cid2remote.get(cid)
|
|
104
|
+
if (mode == 1) {
|
|
105
|
+
return {
|
|
106
|
+
ip: remote.ip,
|
|
107
|
+
id: remote.cid,
|
|
108
|
+
ssid: remote.ssid,
|
|
109
|
+
cid: remote.cid,
|
|
110
|
+
uid: remote.uid,
|
|
111
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
112
|
+
tx: remote.socket.txCounter,
|
|
113
|
+
rx: remote.socket.rxCounter,
|
|
114
|
+
txBytes: remote.socket.bytesWritten || remote.socket._socket?.bytesWritten,
|
|
115
|
+
rxBytes: remote.socket.bytesRead || remote.socket._socket?.bytesRead
|
|
116
|
+
}
|
|
117
|
+
} else if (mode == 2) {
|
|
118
|
+
return {
|
|
119
|
+
ip: remote.ip,
|
|
120
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
121
|
+
nick: remote.nick,
|
|
122
|
+
ssid: remote.ssid
|
|
123
|
+
}
|
|
124
|
+
} else if (mode == 3) {
|
|
125
|
+
return {
|
|
126
|
+
ip: remote.ip,
|
|
127
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
128
|
+
nick: remote.nick,
|
|
129
|
+
ssid: remote.ssid,
|
|
130
|
+
isSecure: remote.TLS,
|
|
131
|
+
isAuth: remote.boho.isAuthorized,
|
|
132
|
+
encMode: ENC_MODE[remote.encMode]
|
|
133
|
+
}
|
|
134
|
+
} else if (mode == 4) {
|
|
135
|
+
let channels = Array.from(remote.channels.keys())
|
|
136
|
+
let set_memory = Array.from(remote.memory.keys())
|
|
137
|
+
let retain_signal = Array.from(remote.retain_signal.keys())
|
|
138
|
+
return {
|
|
139
|
+
ip: remote.ip,
|
|
140
|
+
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
141
|
+
channels: channels,
|
|
142
|
+
set: set_memory,
|
|
143
|
+
retain: retain_signal
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
} else {
|
|
148
|
+
return { result: "nop" }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
}
|
|
155
|
+
|