iosignal 1.4.2 → 1.6.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.
Files changed (52) hide show
  1. package/README.md +247 -240
  2. package/auth_file.json +13 -13
  3. package/auth_file.mjs +23 -23
  4. package/dist/iosignal.cjs +1 -1
  5. package/dist/iosignal.esm.js +2 -2
  6. package/dist/iosignal.esm.js.map +1 -1
  7. package/dist/iosignal.min.js +2 -2
  8. package/dist/iosignal.min.js.map +1 -1
  9. package/index.js +19 -19
  10. package/indexWebBrowser.js +13 -13
  11. package/package.json +59 -60
  12. package/rollup.config.js +69 -69
  13. package/src/api/RedisAPI.js +68 -68
  14. package/src/api/api_constant.js +7 -7
  15. package/src/api/api_reply.js +34 -34
  16. package/src/api/api_sudo.js +96 -96
  17. package/src/auth/Auth_Env.js +78 -78
  18. package/src/auth/Auth_File.js +75 -75
  19. package/src/auth/Auth_Redis.js +50 -50
  20. package/src/auth/BohoAuth.js +168 -168
  21. package/src/client/CongPacket.js +150 -150
  22. package/src/client/IOCongSocket.js +88 -88
  23. package/src/client/IOCore.js +851 -851
  24. package/src/client/IOWS.js +81 -81
  25. package/src/client/IOWebSocket.js +107 -95
  26. package/src/common/constants.js +159 -159
  27. package/src/common/payload.js +82 -82
  28. package/src/common/quotaTable.js +68 -68
  29. package/src/common/util.js +61 -61
  30. package/src/server/FileLogger.js +30 -30
  31. package/src/server/Manager.js +449 -449
  32. package/src/server/Metric.js +154 -154
  33. package/src/server/Server.js +166 -148
  34. package/src/server/ServerRemote.js +227 -227
  35. package/src/server/ServerRemoteCore.js +467 -467
  36. package/src/server/serverOption.js +56 -55
  37. package/test-browser/favicon.ico +0 -0
  38. package/test-browser/index.html +92 -92
  39. package/test-browser/server.js +11 -11
  40. package/test-client-nodejs/client.cjs +17 -17
  41. package/test-client-nodejs/client.mjs +17 -17
  42. package/test-client-nodejs/client_api_reply.mjs +15 -15
  43. package/test-client-nodejs/stateChange.mjs +19 -19
  44. package/test-server/server-Auth_Env.js +19 -19
  45. package/test-server/server-Auth_File.js +12 -12
  46. package/test-server/server-api_reply.js +6 -6
  47. package/test-server/server-api_sudo.js +7 -7
  48. package/test-server/server-commonjs.cjs +8 -8
  49. package/test-server/server-esm.mjs +19 -19
  50. package/test-server/simple-server.mjs +11 -11
  51. package/test-signal-payloadType/signalPack.js +34 -34
  52. package/test-signal-payloadType/signaling.js +60 -60
@@ -1,467 +1,467 @@
1
- import { webcrypto } from 'crypto';
2
- import { MBP } from 'meta-buffer-pack'
3
- import { Boho, BohoMsg, MetaSize } from 'boho'
4
- import { serverOption } from './serverOption.js';
5
- import { IOMsg, ENC_MODE, CLIENT_STATE } from '../common/constants.js'
6
- import { quotaTable } from '../common/quotaTable.js'
7
-
8
- const decoder = new TextDecoder()
9
-
10
- const NB = MBP.NB
11
- const MB = MBP.MB
12
-
13
-
14
- export class ServerRemoteCore {
15
- constructor(socket, manager) {
16
-
17
- this.socket = socket;
18
- socket.isAlive = true;
19
- socket.txCounter = 0;
20
- socket.rxCounter = 0;
21
- socket.openTime = Date.now()
22
-
23
- this.boho = new Boho()
24
- this.encMode = ENC_MODE.AUTO;
25
- this.channels = new Set(); // subscribed tags
26
- this.retain_signal = new Map();
27
-
28
- this.ssid = ServerRemoteCore.ssid++; // ordered index number
29
- this.manager = manager;
30
-
31
- this.cid; // Communication Id
32
- this.did; // Device Id
33
- this.uid;
34
- this.nick = ""
35
-
36
-
37
- this.lastEchoMessage = "N"
38
- this.privateNode = false
39
- this.HOME_CHANNEL = ""
40
- this.level = serverOption.defaultQuotaIndex;
41
- this.quota = quotaTable[this.level];
42
- this.isAdmin = false;
43
-
44
- this.state;
45
- this.stateLog = [];
46
- this.setState(CLIENT_STATE.INIT)
47
-
48
- }
49
-
50
- static ssid = 1;
51
-
52
-
53
-
54
- setState(state) {
55
- this.state = state
56
- if (serverOption.debug.showAuthInfo) {
57
- this.stateLog.push(state)
58
- console.log(this.stateLog.join('>'))
59
- }
60
- }
61
-
62
- getState(){
63
- return this.state
64
- }
65
-
66
- getStateName(){
67
- //state <number>
68
- //value of constant STATES.NAME < number >
69
- //type of constant STATES.NAME name < string uppercase >
70
- //stateName,eventName <string lowercase>
71
- return ( CLIENT_STATE[ this.state ]).toLowerCase()
72
- }
73
-
74
-
75
- showMessageLog(message, isBinary) {
76
- let from = this.boho.isAuthorized ? `did: ${this.did} ${this.cid}@` : `${this.cid}@`
77
- if (isBinary) {
78
- let msgTypeName = IOMsg[message[0]]
79
- if (!msgTypeName) msgTypeName = BohoMsg[message[0]]
80
- msgTypeName =' [' + msgTypeName + ']';
81
- if (message.byteLength > 40) {
82
- console.log(from + msgTypeName + ' LEN:', message.length);
83
- } else {
84
- console.log(from + msgTypeName , message);
85
- }
86
-
87
- } else {
88
- console.log(from + ' [TEXT] %s', message);
89
- }
90
- }
91
-
92
- rxQuotaChecker(message) {
93
- let rxBytes = message.byteLength
94
- this.manager.rxBytes += rxBytes;
95
-
96
- if (serverOption.useQuota.signalSize && (rxBytes > this.quota.signalSize)) {
97
- console.log('## quota: size over')
98
- this.send(Buffer.from([IOMsg.OVER_SIZE]))
99
- if (serverOption.useQuota.disconnect) this.close();
100
- return false;
101
- }
102
-
103
- // todo:
104
- // traffic rate :
105
-
106
- return true;
107
- }
108
-
109
- //
110
- onTimeDelayMessage(message, isBinary = true) {
111
- setTimeout(() => {
112
- this.onSocketMessage(message, isBinary)
113
- }
114
- , serverOption.debug.delay)
115
- }
116
- // CongSocket or WebSocket
117
- onSocketMessage(message, isBinary = true) {
118
-
119
- this.receiveMonitor() // rx data and ping pong : timeout check
120
- if (!this.rxQuotaChecker(message)) return
121
-
122
- if (serverOption.showMessage === 'message') this.showMessageLog(message, isBinary)
123
-
124
- let msgType, decoded
125
-
126
- if (isBinary) {
127
- msgType = message[0]
128
-
129
- if (msgType === BohoMsg.ENC_488) {
130
- // let from = this.boho.isAuthorized ? this.cid : "anonymous"
131
- // console.log(`>> [E488] from: ${from} LEN ${message.byteLength}`)
132
- // console.log( "ENC buffer", message)
133
- try {
134
- decoded = this.boho.decrypt_488(message)
135
- } catch (err) {
136
- console.log('-- E488 DEC_FAIL', err)
137
- return
138
- }
139
-
140
- if (decoded) {
141
- msgType = decoded[0]
142
- message = decoded // signal message
143
- // console.log('[D]', IOMsg[ msgType] )
144
- } else {
145
- return
146
- }
147
-
148
- } else if (msgType === BohoMsg.ENC_E2E) { // End to End Signal
149
- try {
150
- decoded = this.boho.decrypt_488(message)
151
- } catch (err) {
152
- console.log('-- E2E DEC_FAIL', err)
153
- return
154
- }
155
- // console.log('e2e unpack:', decoded )
156
- if (decoded) {
157
- msgType = decoded[0]
158
- message.set(decoded, MetaSize.ENC_488) // set decoded signal_e2e headaer.
159
- message = message.subarray(MetaSize.ENC_488) // reset offset.
160
- // console.log('[D]', IOMsg[ msgType] )
161
- } else {
162
- return
163
- }
164
-
165
- } else {
166
- // console.log( `[P] ${ IOMsg[ msgType] || BohoMsg[cmd]} LEN: ${message.byteLength}` )
167
- }
168
-
169
- switch (msgType) {
170
- case IOMsg.PING:
171
- // console.log('-- ping from:' , this.cid )
172
- this.pong();
173
- break;
174
-
175
- case IOMsg.PONG:
176
- // console.log('<-pong')
177
- break;
178
-
179
- case IOMsg.CID_REQ:
180
- if (this.state < CLIENT_STATE.SENT_SERVER_READY) {
181
- // console.log('protocol error. cid_req before server_ready')
182
- this.close()
183
- }
184
- if (!this.cid) {
185
- this.cid = '?' + webcrypto.getRandomValues(Buffer.alloc(3)).toString('base64url')
186
- this.manager.cid2remote.set(this.cid, this)
187
- }
188
-
189
- // console.log('<< SENDING CID_RES:', this.cid)
190
- this.send_enc_mode(MBP.pack(
191
- MB('#cid_ack', '8', IOMsg.CID_RES),
192
- MB('#cid', this.cid)
193
- ))
194
-
195
- this.setState(CLIENT_STATE.CID_READY)
196
- break;
197
-
198
-
199
- case IOMsg.ECHO: // TEXT only
200
- try {
201
- let msg = decoder.decode(message.subarray(1))
202
- this.lastEchoMessage = msg
203
- } catch (error) {
204
- // console.log('ECHO message is not a text')
205
- }
206
- this.send(message, isBinary)
207
- break;
208
-
209
- case IOMsg.IAM: // iam
210
- if(message.byteLength > 1){
211
- let iamInfo = message.subarray(1)
212
- this.nick = decoder.decode(iamInfo)
213
- console.log('iam nick reset', this.nick)
214
- }
215
- this.iamResponse()
216
- break;
217
-
218
- case IOMsg.SIGNAL_E2E:
219
- case IOMsg.SIGNAL:
220
- if (message.byteLength >= 3) {
221
- let tagLen = message.readUInt8(1)
222
- if (message.byteLength >= tagLen + 2) {
223
- let tag = message.subarray(2, 2 + tagLen)
224
- tag = decoder.decode(tag)
225
- this.manager.sender(tag, this, message )
226
- }
227
- }
228
- break;
229
-
230
-
231
-
232
- case IOMsg.UNSUBSCRIBE:
233
- if (message.byteLength == 2) {
234
- this.manager.unsubscribe([""], this)
235
- } else if (message.byteLength >= 3) {
236
- let tagLen = message.readUInt8(1)
237
-
238
- if (message.byteLength == tagLen + 2) {
239
- let tag = message.subarray(2, 2 + tagLen)
240
- tag = decoder.decode(tag)
241
- // console.log('unsub: tag', tag)
242
- let tagList = tag.split(',')
243
- this.manager.unsubscribe(tagList, this)
244
- }
245
- }
246
- break;
247
-
248
-
249
- case IOMsg.SUBSCRIBE: // 1byte tagLen
250
- if (message.byteLength >= 3) {
251
- let tagLen = message.readUInt8(1)
252
- if (message.byteLength == tagLen + 2) {
253
- let tag = message.subarray(2, 2 + tagLen)
254
- tag = decoder.decode(tag)
255
- let tagList = tag.split(',')
256
- // console.log('-- SUBSCRIBE req splittagList', tagList)
257
- this.manager.subscribe(tagList, this)
258
- }
259
- }
260
- break;
261
-
262
- case IOMsg.SUBSCRIBE_REQ: // 2bytes tagLen
263
- // console.log('#####recv sub_req ', message, message.byteLength )
264
- if (message.byteLength >= 6) {
265
- let msgID = message.readUInt16BE(1)
266
- let tagLen = message.readUInt16BE(3)
267
- if (message.byteLength == tagLen + 5) {
268
- let tag = message.subarray(5, 5 + tagLen)
269
- tag = decoder.decode(tag)
270
- let tagList = tag.split(',')
271
- // console.log('>> SUBSCRIBE_REQ from:', this.cid, tagList)
272
- let size = this.manager.subscribe(tagList, this)
273
-
274
- // this.response(msgID, 0, { size: size })
275
- this.response(msgID, 0)
276
- } else {
277
- this.response(msgID, 255 )
278
- }
279
- }
280
- break;
281
-
282
-
283
- case IOMsg.REQUEST: //api request
284
- try {
285
- let req = MBP.unpack( message )
286
- // console.log('request unpack req', req)
287
- if(req){
288
- // console.log('request target:', req.target, 'topic:', req.topic)
289
- if( !req.target || !req.topic ) return
290
- if(this.manager.server.apiNames.has(req.target)){
291
- this.manager.server.emit( req.target, this , req )
292
- }else{
293
- console.log('UnKnown API req: ', req.target)
294
- }
295
- }
296
- } catch (e) {
297
- console.log('request catch error', e)
298
- }
299
- break;
300
-
301
-
302
- case IOMsg.CLOSE:
303
- if (message.byteLength > 1) {
304
- let reason = decoder.decode(message.subarray(1))
305
- console.log('>> CLOSE reason:', reason)
306
- this.close();
307
- }
308
- break;
309
-
310
-
311
- // Auth
312
- case BohoMsg.AUTH_REQ:
313
- if (!this.manager.authManager) return
314
- if (this.state < CLIENT_STATE.SENT_SERVER_READY) {
315
- // console.log('protocol error. auth_req before server_ready')
316
- this.close();
317
- }
318
- this.setState(CLIENT_STATE.RECV_AUTH_REQ)
319
- let auth_nonce_pack = this.boho.auth_nonce()
320
- // console.log('## auth_nonce_pack', auth_nonce_pack )
321
- this.send(auth_nonce_pack)
322
- this.setState(CLIENT_STATE.SENT_SERVER_NONCE)
323
- break;
324
-
325
- case BohoMsg.AUTH_HMAC:
326
- if (!this.manager.authManager) return
327
- if (this.state < CLIENT_STATE.SENT_SERVER_NONCE) {
328
- // console.log('protocol error. auth_hmac before server_nonce')
329
- this.close();
330
- }
331
- this.setState(CLIENT_STATE.RECV_AUTH_HMAC)
332
- //async
333
- this.manager.authManager.verify_auth_hmac(message, this)
334
- .then(authInfo=>{
335
- if(authInfo){
336
- this.manager.deligateSignal(remote, '@$name', authInfo.name)
337
- // console.log('device login success authInfo', authInfo)
338
- // this.manager.sender('@$name', authInfo.name )
339
- }
340
-
341
- })
342
- .catch(e=>{
343
-
344
- })
345
-
346
- return;
347
-
348
- default:
349
- // console.log('unknown MsgType:',msgType,' from:', this.ip, this.ssid, this.cid )
350
-
351
- }
352
-
353
- } else {
354
- // TEXT FRAME
355
- try {
356
- let textMessage = decoder.decode(message)
357
- // console.log('text buffer:',textMessage )
358
- // console.log('text buffer:',message.toString('hex') )
359
- this.manager.server.emit('text_message', textMessage, this)
360
- } catch (error) {
361
-
362
- }
363
- }
364
-
365
- }
366
-
367
-
368
- response(msgId, statusCode, body) {
369
- // console.log('response body', body)
370
- if( body ){
371
-
372
- this.send_enc_mode( MBP.pack(
373
- MB('#type', '8', IOMsg.RESPONSE_MBP),
374
- MB('status','8', statusCode),
375
- MB('mid','16', msgId),
376
- MB('body',body)
377
- ))
378
-
379
- }else{
380
- this.send_enc_mode( MBP.pack(
381
- MB('#type','8', IOMsg.RESPONSE_MBP),
382
- MB('status','8', statusCode),
383
- MB('mid','16', msgId)
384
- ))
385
-
386
- }
387
-
388
- }
389
-
390
-
391
- send_enc_mode(data, useEncryption = false) {
392
-
393
- if (this.encMode === ENC_MODE.YES ||
394
- this.encMode === ENC_MODE.AUTO &&
395
- !this.TLS && this.boho.isAuthorized
396
- ) useEncryption = true;
397
-
398
- // console.log('svr useEnc', useEncryption, data )
399
-
400
- if (useEncryption && data[0] == IOMsg.SIGNAL_E2E) {
401
- // E2E는,
402
- // 서버는 수신자와 암호통신하는경우, 헤더부분만 암호화 하고, 데이타 부분은 그대로 전달해야한다.
403
- let tagLen = data[1];
404
-
405
- // let tagInfo = decoder.decode( data.subarray( 2, 2 + tagLen) )
406
- // console.log('server bypass E2E signal taginfo:', tagInfo )
407
-
408
- let encHeader = this.boho.encrypt_488(data.subarray(0, 3 + tagLen))
409
- // console.log('size check:', encHeader.byteLength == (tagLen + 3 + 21 ) )
410
- encHeader[0] = BohoMsg.ENC_E2E
411
- // encHeader 크기가 21(enc_488)크기 만큼 커짐. tagLen+3 + 21
412
- let newEncBuffer = Buffer.concat([encHeader, data.subarray(3 + tagLen)])
413
-
414
- // console.log('ENC_E2E', newEncBuffer)
415
- this.send(newEncBuffer)
416
-
417
- } else if (useEncryption) {
418
- let encPack = this.boho.encrypt_488(data)
419
- if (encPack) {
420
- // console.log('send *S* LEN:', encPack.byteLength)
421
- this.send(encPack)
422
- } else {
423
- console.log('encryption FAIL: NO DATA TRANSIT')
424
- }
425
- } else {
426
- // console.log('send -N-')
427
- this.send(data)
428
-
429
- }
430
-
431
- }
432
-
433
-
434
-
435
- iamResponse(info = "") {
436
-
437
- if (info == "") {
438
- //send general info
439
- let channels = []
440
- for (let tag of this.channels.keys()) {
441
- channels.push(tag)
442
- }
443
-
444
- info = {
445
- "ssid": this.ssid,
446
- "uid": this.uid,
447
- "cid": this.cid,
448
- "did": this.did,
449
- "nick": this.nick,
450
- "ip": this.ip
451
- , 'tag': channels
452
- }
453
- }
454
-
455
- let pack = MBP.pack(
456
- MB('#MsgType', '8', IOMsg.IAM_RES),
457
- MB('#info', info)
458
- )
459
- this.send_enc_mode(pack)
460
- }
461
-
462
- // storeSet(topic, ...args){
463
- // this.manager.set( this, topic, ...args )
464
- // }
465
-
466
- }
467
-
1
+ import { webcrypto } from 'crypto';
2
+ import { MBP } from 'meta-buffer-pack'
3
+ import { Boho, BohoMsg, MetaSize } from 'boho'
4
+ import { serverOption } from './serverOption.js';
5
+ import { IOMsg, ENC_MODE, CLIENT_STATE } from '../common/constants.js'
6
+ import { quotaTable } from '../common/quotaTable.js'
7
+
8
+ const decoder = new TextDecoder()
9
+
10
+ const NB = MBP.NB
11
+ const MB = MBP.MB
12
+
13
+
14
+ export class ServerRemoteCore {
15
+ constructor(socket, manager) {
16
+
17
+ this.socket = socket;
18
+ socket.isAlive = true;
19
+ socket.txCounter = 0;
20
+ socket.rxCounter = 0;
21
+ socket.openTime = Date.now()
22
+
23
+ this.boho = new Boho()
24
+ this.encMode = ENC_MODE.AUTO;
25
+ this.channels = new Set(); // subscribed tags
26
+ this.retain_signal = new Map();
27
+
28
+ this.ssid = ServerRemoteCore.ssid++; // ordered index number
29
+ this.manager = manager;
30
+
31
+ this.cid; // Communication Id
32
+ this.did; // Device Id
33
+ this.uid;
34
+ this.nick = ""
35
+
36
+
37
+ this.lastEchoMessage = "N"
38
+ this.privateNode = false
39
+ this.HOME_CHANNEL = ""
40
+ this.level = serverOption.defaultQuotaIndex;
41
+ this.quota = quotaTable[this.level];
42
+ this.isAdmin = false;
43
+
44
+ this.state;
45
+ this.stateLog = [];
46
+ this.setState(CLIENT_STATE.INIT)
47
+
48
+ }
49
+
50
+ static ssid = 1;
51
+
52
+
53
+
54
+ setState(state) {
55
+ this.state = state
56
+ if (serverOption.debug.showAuthInfo) {
57
+ this.stateLog.push(state)
58
+ console.log(this.stateLog.join('>'))
59
+ }
60
+ }
61
+
62
+ getState(){
63
+ return this.state
64
+ }
65
+
66
+ getStateName(){
67
+ //state <number>
68
+ //value of constant STATES.NAME < number >
69
+ //type of constant STATES.NAME name < string uppercase >
70
+ //stateName,eventName <string lowercase>
71
+ return ( CLIENT_STATE[ this.state ]).toLowerCase()
72
+ }
73
+
74
+
75
+ showMessageLog(message, isBinary) {
76
+ let from = this.boho.isAuthorized ? `did: ${this.did} ${this.cid}@` : `${this.cid}@`
77
+ if (isBinary) {
78
+ let msgTypeName = IOMsg[message[0]]
79
+ if (!msgTypeName) msgTypeName = BohoMsg[message[0]]
80
+ msgTypeName =' [' + msgTypeName + ']';
81
+ if (message.byteLength > 40) {
82
+ console.log(from + msgTypeName + ' LEN:', message.length);
83
+ } else {
84
+ console.log(from + msgTypeName , message);
85
+ }
86
+
87
+ } else {
88
+ console.log(from + ' [TEXT] %s', message);
89
+ }
90
+ }
91
+
92
+ rxQuotaChecker(message) {
93
+ let rxBytes = message.byteLength
94
+ this.manager.rxBytes += rxBytes;
95
+
96
+ if (serverOption.useQuota.signalSize && (rxBytes > this.quota.signalSize)) {
97
+ console.log('## quota: size over')
98
+ this.send(Buffer.from([IOMsg.OVER_SIZE]))
99
+ if (serverOption.useQuota.disconnect) this.close();
100
+ return false;
101
+ }
102
+
103
+ // todo:
104
+ // traffic rate :
105
+
106
+ return true;
107
+ }
108
+
109
+ //
110
+ onTimeDelayMessage(message, isBinary = true) {
111
+ setTimeout(() => {
112
+ this.onSocketMessage(message, isBinary)
113
+ }
114
+ , serverOption.debug.delay)
115
+ }
116
+ // CongSocket or WebSocket
117
+ onSocketMessage(message, isBinary = true) {
118
+
119
+ this.receiveMonitor() // rx data and ping pong : timeout check
120
+ if (!this.rxQuotaChecker(message)) return
121
+
122
+ if (serverOption.showMessage === 'message') this.showMessageLog(message, isBinary)
123
+
124
+ let msgType, decoded
125
+
126
+ if (isBinary) {
127
+ msgType = message[0]
128
+
129
+ if (msgType === BohoMsg.ENC_488) {
130
+ // let from = this.boho.isAuthorized ? this.cid : "anonymous"
131
+ // console.log(`>> [E488] from: ${from} LEN ${message.byteLength}`)
132
+ // console.log( "ENC buffer", message)
133
+ try {
134
+ decoded = this.boho.decrypt_488(message)
135
+ } catch (err) {
136
+ console.log('-- E488 DEC_FAIL', err)
137
+ return
138
+ }
139
+
140
+ if (decoded) {
141
+ msgType = decoded[0]
142
+ message = decoded // signal message
143
+ // console.log('[D]', IOMsg[ msgType] )
144
+ } else {
145
+ return
146
+ }
147
+
148
+ } else if (msgType === BohoMsg.ENC_E2E) { // End to End Signal
149
+ try {
150
+ decoded = this.boho.decrypt_488(message)
151
+ } catch (err) {
152
+ console.log('-- E2E DEC_FAIL', err)
153
+ return
154
+ }
155
+ // console.log('e2e unpack:', decoded )
156
+ if (decoded) {
157
+ msgType = decoded[0]
158
+ message.set(decoded, MetaSize.ENC_488) // set decoded signal_e2e headaer.
159
+ message = message.subarray(MetaSize.ENC_488) // reset offset.
160
+ // console.log('[D]', IOMsg[ msgType] )
161
+ } else {
162
+ return
163
+ }
164
+
165
+ } else {
166
+ // console.log( `[P] ${ IOMsg[ msgType] || BohoMsg[cmd]} LEN: ${message.byteLength}` )
167
+ }
168
+
169
+ switch (msgType) {
170
+ case IOMsg.PING:
171
+ // console.log('-- ping from:' , this.cid )
172
+ this.pong();
173
+ break;
174
+
175
+ case IOMsg.PONG:
176
+ // console.log('<-pong')
177
+ break;
178
+
179
+ case IOMsg.CID_REQ:
180
+ if (this.state < CLIENT_STATE.SENT_SERVER_READY) {
181
+ // console.log('protocol error. cid_req before server_ready')
182
+ this.close()
183
+ }
184
+ if (!this.cid) {
185
+ this.cid = '?' + webcrypto.getRandomValues(Buffer.alloc(3)).toString('base64url')
186
+ this.manager.cid2remote.set(this.cid, this)
187
+ }
188
+
189
+ // console.log('<< SENDING CID_RES:', this.cid)
190
+ this.send_enc_mode(MBP.pack(
191
+ MB('#cid_ack', '8', IOMsg.CID_RES),
192
+ MB('#cid', this.cid)
193
+ ))
194
+
195
+ this.setState(CLIENT_STATE.CID_READY)
196
+ break;
197
+
198
+
199
+ case IOMsg.ECHO: // TEXT only
200
+ try {
201
+ let msg = decoder.decode(message.subarray(1))
202
+ this.lastEchoMessage = msg
203
+ } catch (error) {
204
+ // console.log('ECHO message is not a text')
205
+ }
206
+ this.send(message, isBinary)
207
+ break;
208
+
209
+ case IOMsg.IAM: // iam
210
+ if(message.byteLength > 1){
211
+ let iamInfo = message.subarray(1)
212
+ this.nick = decoder.decode(iamInfo)
213
+ console.log('iam nick reset', this.nick)
214
+ }
215
+ this.iamResponse()
216
+ break;
217
+
218
+ case IOMsg.SIGNAL_E2E:
219
+ case IOMsg.SIGNAL:
220
+ if (message.byteLength >= 3) {
221
+ let tagLen = message.readUInt8(1)
222
+ if (message.byteLength >= tagLen + 2) {
223
+ let tag = message.subarray(2, 2 + tagLen)
224
+ tag = decoder.decode(tag)
225
+ this.manager.sender(tag, this, message )
226
+ }
227
+ }
228
+ break;
229
+
230
+
231
+
232
+ case IOMsg.UNSUBSCRIBE:
233
+ if (message.byteLength == 2) {
234
+ this.manager.unsubscribe([""], this)
235
+ } else if (message.byteLength >= 3) {
236
+ let tagLen = message.readUInt8(1)
237
+
238
+ if (message.byteLength == tagLen + 2) {
239
+ let tag = message.subarray(2, 2 + tagLen)
240
+ tag = decoder.decode(tag)
241
+ // console.log('unsub: tag', tag)
242
+ let tagList = tag.split(',')
243
+ this.manager.unsubscribe(tagList, this)
244
+ }
245
+ }
246
+ break;
247
+
248
+
249
+ case IOMsg.SUBSCRIBE: // 1byte tagLen
250
+ if (message.byteLength >= 3) {
251
+ let tagLen = message.readUInt8(1)
252
+ if (message.byteLength == tagLen + 2) {
253
+ let tag = message.subarray(2, 2 + tagLen)
254
+ tag = decoder.decode(tag)
255
+ let tagList = tag.split(',')
256
+ // console.log('-- SUBSCRIBE req splittagList', tagList)
257
+ this.manager.subscribe(tagList, this)
258
+ }
259
+ }
260
+ break;
261
+
262
+ case IOMsg.SUBSCRIBE_REQ: // 2bytes tagLen
263
+ // console.log('#####recv sub_req ', message, message.byteLength )
264
+ if (message.byteLength >= 6) {
265
+ let msgID = message.readUInt16BE(1)
266
+ let tagLen = message.readUInt16BE(3)
267
+ if (message.byteLength == tagLen + 5) {
268
+ let tag = message.subarray(5, 5 + tagLen)
269
+ tag = decoder.decode(tag)
270
+ let tagList = tag.split(',')
271
+ // console.log('>> SUBSCRIBE_REQ from:', this.cid, tagList)
272
+ let size = this.manager.subscribe(tagList, this)
273
+
274
+ // this.response(msgID, 0, { size: size })
275
+ this.response(msgID, 0)
276
+ } else {
277
+ this.response(msgID, 255 )
278
+ }
279
+ }
280
+ break;
281
+
282
+
283
+ case IOMsg.REQUEST: //api request
284
+ try {
285
+ let req = MBP.unpack( message )
286
+ // console.log('request unpack req', req)
287
+ if(req){
288
+ // console.log('request target:', req.target, 'topic:', req.topic)
289
+ if( !req.target || !req.topic ) return
290
+ if(this.manager.server.apiNames.has(req.target)){
291
+ this.manager.server.emit( req.target, this , req )
292
+ }else{
293
+ console.log('UnKnown API req: ', req.target)
294
+ }
295
+ }
296
+ } catch (e) {
297
+ console.log('request catch error', e)
298
+ }
299
+ break;
300
+
301
+
302
+ case IOMsg.CLOSE:
303
+ if (message.byteLength > 1) {
304
+ let reason = decoder.decode(message.subarray(1))
305
+ console.log('>> CLOSE reason:', reason)
306
+ this.close();
307
+ }
308
+ break;
309
+
310
+
311
+ // Auth
312
+ case BohoMsg.AUTH_REQ:
313
+ if (!this.manager.authManager) return
314
+ if (this.state < CLIENT_STATE.SENT_SERVER_READY) {
315
+ // console.log('protocol error. auth_req before server_ready')
316
+ this.close();
317
+ }
318
+ this.setState(CLIENT_STATE.RECV_AUTH_REQ)
319
+ let auth_nonce_pack = this.boho.auth_nonce()
320
+ // console.log('## auth_nonce_pack', auth_nonce_pack )
321
+ this.send(auth_nonce_pack)
322
+ this.setState(CLIENT_STATE.SENT_SERVER_NONCE)
323
+ break;
324
+
325
+ case BohoMsg.AUTH_HMAC:
326
+ if (!this.manager.authManager) return
327
+ if (this.state < CLIENT_STATE.SENT_SERVER_NONCE) {
328
+ // console.log('protocol error. auth_hmac before server_nonce')
329
+ this.close();
330
+ }
331
+ this.setState(CLIENT_STATE.RECV_AUTH_HMAC)
332
+ //async
333
+ this.manager.authManager.verify_auth_hmac(message, this)
334
+ .then(authInfo=>{
335
+ if(authInfo){
336
+ this.manager.deligateSignal(remote, '@$name', authInfo.name)
337
+ // console.log('device login success authInfo', authInfo)
338
+ // this.manager.sender('@$name', authInfo.name )
339
+ }
340
+
341
+ })
342
+ .catch(e=>{
343
+
344
+ })
345
+
346
+ return;
347
+
348
+ default:
349
+ // console.log('unknown MsgType:',msgType,' from:', this.ip, this.ssid, this.cid )
350
+
351
+ }
352
+
353
+ } else {
354
+ // TEXT FRAME
355
+ try {
356
+ let textMessage = decoder.decode(message)
357
+ // console.log('text buffer:',textMessage )
358
+ // console.log('text buffer:',message.toString('hex') )
359
+ this.manager.server.emit('text_message', textMessage, this)
360
+ } catch (error) {
361
+
362
+ }
363
+ }
364
+
365
+ }
366
+
367
+
368
+ response(msgId, statusCode, body) {
369
+ // console.log('response body', body)
370
+ if( body ){
371
+
372
+ this.send_enc_mode( MBP.pack(
373
+ MB('#type', '8', IOMsg.RESPONSE_MBP),
374
+ MB('status','8', statusCode),
375
+ MB('mid','16', msgId),
376
+ MB('body',body)
377
+ ))
378
+
379
+ }else{
380
+ this.send_enc_mode( MBP.pack(
381
+ MB('#type','8', IOMsg.RESPONSE_MBP),
382
+ MB('status','8', statusCode),
383
+ MB('mid','16', msgId)
384
+ ))
385
+
386
+ }
387
+
388
+ }
389
+
390
+
391
+ send_enc_mode(data, useEncryption = false) {
392
+
393
+ if (this.encMode === ENC_MODE.YES ||
394
+ this.encMode === ENC_MODE.AUTO &&
395
+ !this.TLS && this.boho.isAuthorized
396
+ ) useEncryption = true;
397
+
398
+ // console.log('svr useEnc', useEncryption, data )
399
+
400
+ if (useEncryption && data[0] == IOMsg.SIGNAL_E2E) {
401
+ // E2E는,
402
+ // 서버는 수신자와 암호통신하는경우, 헤더부분만 암호화 하고, 데이타 부분은 그대로 전달해야한다.
403
+ let tagLen = data[1];
404
+
405
+ // let tagInfo = decoder.decode( data.subarray( 2, 2 + tagLen) )
406
+ // console.log('server bypass E2E signal taginfo:', tagInfo )
407
+
408
+ let encHeader = this.boho.encrypt_488(data.subarray(0, 3 + tagLen))
409
+ // console.log('size check:', encHeader.byteLength == (tagLen + 3 + 21 ) )
410
+ encHeader[0] = BohoMsg.ENC_E2E
411
+ // encHeader 크기가 21(enc_488)크기 만큼 커짐. tagLen+3 + 21
412
+ let newEncBuffer = Buffer.concat([encHeader, data.subarray(3 + tagLen)])
413
+
414
+ // console.log('ENC_E2E', newEncBuffer)
415
+ this.send(newEncBuffer)
416
+
417
+ } else if (useEncryption) {
418
+ let encPack = this.boho.encrypt_488(data)
419
+ if (encPack) {
420
+ // console.log('send *S* LEN:', encPack.byteLength)
421
+ this.send(encPack)
422
+ } else {
423
+ console.log('encryption FAIL: NO DATA TRANSIT')
424
+ }
425
+ } else {
426
+ // console.log('send -N-')
427
+ this.send(data)
428
+
429
+ }
430
+
431
+ }
432
+
433
+
434
+
435
+ iamResponse(info = "") {
436
+
437
+ if (info == "") {
438
+ //send general info
439
+ let channels = []
440
+ for (let tag of this.channels.keys()) {
441
+ channels.push(tag)
442
+ }
443
+
444
+ info = {
445
+ "ssid": this.ssid,
446
+ "uid": this.uid,
447
+ "cid": this.cid,
448
+ "did": this.did,
449
+ "nick": this.nick,
450
+ "ip": this.ip
451
+ , 'tag': channels
452
+ }
453
+ }
454
+
455
+ let pack = MBP.pack(
456
+ MB('#MsgType', '8', IOMsg.IAM_RES),
457
+ MB('#info', info)
458
+ )
459
+ this.send_enc_mode(pack)
460
+ }
461
+
462
+ // storeSet(topic, ...args){
463
+ // this.manager.set( this, topic, ...args )
464
+ // }
465
+
466
+ }
467
+