iosignal 4.6.1 → 4.8.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/.vscode/settings.json +1 -1
- package/README.md +30 -7
- package/dist/io.d.ts +7 -13
- package/dist/io.js +4 -4
- package/dist/io.js.map +1 -1
- package/dist/io.min.js +3 -3
- package/dist/io.min.js.map +1 -1
- package/dist/iosignal.js +74 -110
- package/dist/types/client/IOCore.d.ts +7 -13
- package/dist/types/common/constants.d.ts +2 -12
- package/index.js +4 -4
- package/package.json +1 -1
- package/src/auth/key_providers/RedisKeyProvider.js +2 -2
- package/src/client/IOCore.js +20 -47
- package/src/common/constants.js +5 -15
- package/src/server/Manager.js +1 -1
- package/src/server/Remote.js +1 -0
- package/src/server/RemoteCore.js +3 -3
- package/src/server/Server.js +27 -27
- package/src/{api/RedisAPI.js → services/RedisService.js} +6 -6
- package/src/{api/api_reply.js → services/replyService.js} +5 -5
- package/src/{api/api_sudo.js → services/sudoService.js} +3 -3
- package/tsconfig.json +1 -1
- /package/src/{api/api_constant.js → services/constant.js} +0 -0
package/src/client/IOCore.js
CHANGED
|
@@ -460,7 +460,7 @@ export class IOCore extends EventEmitter {
|
|
|
460
460
|
|
|
461
461
|
// **IMPORTANT** change state before subscribe.
|
|
462
462
|
this.stateChange('ready', 'cid_ready')
|
|
463
|
-
this.
|
|
463
|
+
this.subscribe_channels()
|
|
464
464
|
break;
|
|
465
465
|
|
|
466
466
|
case IOMsg.QUOTA_LEVEL:
|
|
@@ -732,6 +732,7 @@ export class IOCore extends EventEmitter {
|
|
|
732
732
|
console.log('## your maximum signalSize(bytes) is:', this.quota.signalSize)
|
|
733
733
|
return
|
|
734
734
|
}
|
|
735
|
+
// console.log(`C->[${IOMsg[ data[0]]}]`)
|
|
735
736
|
this.socket_send(data);
|
|
736
737
|
}
|
|
737
738
|
|
|
@@ -900,7 +901,7 @@ export class IOCore extends EventEmitter {
|
|
|
900
901
|
if (!storeName || args.length == 0) {
|
|
901
902
|
return Promise.reject(new Error('set need storeName and value)'))
|
|
902
903
|
}
|
|
903
|
-
return this.
|
|
904
|
+
return this.call('store', 'set', storeName, ...args)
|
|
904
905
|
}
|
|
905
906
|
|
|
906
907
|
/**
|
|
@@ -912,26 +913,26 @@ export class IOCore extends EventEmitter {
|
|
|
912
913
|
if (!storeName) {
|
|
913
914
|
return Promise.reject(new Error('store get need storeName)'))
|
|
914
915
|
}
|
|
915
|
-
let pack = await this.
|
|
916
|
+
let pack = await this.call('store', 'get', storeName)
|
|
916
917
|
let { $ } = MBP.unpack(pack.body)
|
|
917
918
|
return $
|
|
918
919
|
}
|
|
919
920
|
|
|
920
921
|
|
|
921
922
|
/**
|
|
922
|
-
* Sends a request to a target and topic.(remote
|
|
923
|
-
* @param {string} target - The target(
|
|
924
|
-
* @param {string} topic - The topic(
|
|
923
|
+
* Sends a request to a target and topic.(remote service call)
|
|
924
|
+
* @param {string} target - The target(service name) of the request.
|
|
925
|
+
* @param {string} topic - The topic(service function name) of the request.
|
|
925
926
|
* @param {...any} args - Optional arguments for the request.
|
|
926
927
|
* @returns {Promise<any>}
|
|
927
928
|
*/
|
|
928
|
-
|
|
929
|
+
call(target, topic, ...args) {
|
|
929
930
|
if (!target || !topic)
|
|
930
931
|
return Promise.reject(new Error('request need target and topic)'))
|
|
931
932
|
let sigPack;
|
|
932
933
|
if (args.length > 0) {
|
|
933
934
|
sigPack = MBP.pack(
|
|
934
|
-
MBP.MB('#MsgType', '8', IOMsg.
|
|
935
|
+
MBP.MB('#MsgType', '8', IOMsg.CALL),
|
|
935
936
|
MBP.MB('mid', '16', ++this.mid),
|
|
936
937
|
MBP.MB('target', target),
|
|
937
938
|
MBP.MB('topic', topic),
|
|
@@ -939,7 +940,7 @@ export class IOCore extends EventEmitter {
|
|
|
939
940
|
)
|
|
940
941
|
} else {
|
|
941
942
|
sigPack = MBP.pack(
|
|
942
|
-
MBP.MB('#MsgType', '8', IOMsg.
|
|
943
|
+
MBP.MB('#MsgType', '8', IOMsg.CALL),
|
|
943
944
|
MBP.MB('mid', '16', ++this.mid),
|
|
944
945
|
MBP.MB('target', target),
|
|
945
946
|
MBP.MB('topic', topic)
|
|
@@ -958,14 +959,6 @@ export class IOCore extends EventEmitter {
|
|
|
958
959
|
subscribe(tag) {
|
|
959
960
|
if (typeof tag !== 'string') throw TypeError('tag should be string.')
|
|
960
961
|
if (tag.length > SIZE_LIMIT.TAG_LEN1) throw TypeError('please check tag string length limit:' + SIZE_LIMIT.TAG_LEN1)
|
|
961
|
-
if (this.state !== STATE.READY) return
|
|
962
|
-
|
|
963
|
-
// subscribe 사용시, 사용 'ready' 이벤트시 메뉴얼 등록되므로 여기에 등록하면 2중 호출된다.
|
|
964
|
-
// 즉, channels 등록은 listen 같은 자동화 구독시만 사용.
|
|
965
|
-
// let tagList = tag.split(',')
|
|
966
|
-
// tagList.forEach(tag => {
|
|
967
|
-
// this.channels.add(tag)
|
|
968
|
-
// })
|
|
969
962
|
|
|
970
963
|
try {
|
|
971
964
|
let tagEncoded = encoder.encode(tag)
|
|
@@ -975,47 +968,27 @@ export class IOCore extends EventEmitter {
|
|
|
975
968
|
MBP.NB('8', tagEncoded.byteLength),
|
|
976
969
|
tagEncoded]))
|
|
977
970
|
} catch (error) { }
|
|
971
|
+
|
|
978
972
|
}
|
|
979
973
|
|
|
974
|
+
|
|
980
975
|
/**
|
|
981
|
-
* Subscribes
|
|
982
|
-
*
|
|
983
|
-
* @returns {Promise<any>}
|
|
984
|
-
* @throws {TypeError} If tag is not a string or exceeds length limit.
|
|
976
|
+
* Subscribes stored channels.
|
|
977
|
+
* called client state become 'ready'
|
|
985
978
|
*/
|
|
986
|
-
|
|
987
|
-
if (
|
|
988
|
-
if (
|
|
989
|
-
|
|
990
|
-
if (this.state !== STATE.READY) {
|
|
991
|
-
return Promise.reject('subscribe_promise:: connection is not ready')
|
|
992
|
-
}
|
|
979
|
+
subscribe_channels() {
|
|
980
|
+
if (this.state !== STATE.READY) return
|
|
981
|
+
if (this.channels.size == 0) return
|
|
982
|
+
let tag = Array.from(this.channels).join(',')
|
|
993
983
|
|
|
994
984
|
try {
|
|
995
985
|
let tagEncoded = encoder.encode(tag)
|
|
996
986
|
this.send_enc_mode(
|
|
997
987
|
Buffer.concat([
|
|
998
|
-
MBP.NB('8', IOMsg.
|
|
999
|
-
MBP.NB('
|
|
1000
|
-
MBP.NB('16', tagEncoded.byteLength),
|
|
988
|
+
MBP.NB('8', IOMsg.SUBSCRIBE),
|
|
989
|
+
MBP.NB('8', tagEncoded.byteLength),
|
|
1001
990
|
tagEncoded]))
|
|
1002
|
-
return this.setMsgPromise(this.mid)
|
|
1003
991
|
} catch (error) { }
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
/**
|
|
1007
|
-
* Subscribes to channels stored in memory (local cache).
|
|
1008
|
-
*/
|
|
1009
|
-
subscribe_memory_channels() { //local cache . auto_resubscribe
|
|
1010
|
-
if (this.channels.size == 0) return
|
|
1011
|
-
let chList = Array.from(this.channels).join(',')
|
|
1012
|
-
|
|
1013
|
-
this.subscribe_promise(chList)
|
|
1014
|
-
.then((res) => {
|
|
1015
|
-
// console.log('>> SUBSCRIBE_REQ result', res ) // return code == map.size
|
|
1016
|
-
}).catch((e) => {
|
|
1017
|
-
console.log('>> SUBSCRIBE FAIL:', e)
|
|
1018
|
-
})
|
|
1019
992
|
|
|
1020
993
|
}
|
|
1021
994
|
|
package/src/common/constants.js
CHANGED
|
@@ -116,7 +116,7 @@ for (let c in PAYLOAD_TYPE) { PAYLOAD_TYPE[PAYLOAD_TYPE[c]] = c }
|
|
|
116
116
|
* @property {number} SET
|
|
117
117
|
* @property {number} RESPONSE_CODE
|
|
118
118
|
* @property {number} RESPONSE_MBP
|
|
119
|
-
* @property {number}
|
|
119
|
+
* @property {number} CALL
|
|
120
120
|
* @property {number} RESPONSE
|
|
121
121
|
* @property {number} FLOW_MODE
|
|
122
122
|
* @property {number} WAIT
|
|
@@ -181,11 +181,10 @@ export let IOMsg = {
|
|
|
181
181
|
|
|
182
182
|
//..
|
|
183
183
|
SET: 0xDB, //
|
|
184
|
-
RESPONSE_CODE: 0xDC,
|
|
184
|
+
// RESPONSE_CODE: 0xDC,
|
|
185
185
|
RESPONSE_MBP: 0xDD,
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
RESPONSE: 0xDF,
|
|
186
|
+
CALL: 0xDE, // rpc call
|
|
187
|
+
// RESPONSE: 0xDF,
|
|
189
188
|
// ~DF
|
|
190
189
|
|
|
191
190
|
|
|
@@ -203,22 +202,13 @@ export let IOMsg = {
|
|
|
203
202
|
for (let c in IOMsg) { IOMsg[IOMsg[c]] = c }
|
|
204
203
|
// console.log( IOMsg );
|
|
205
204
|
|
|
206
|
-
/**
|
|
207
|
-
* @typedef {object} API_TYPE
|
|
208
|
-
* @property {string} REQUEST_RESPONSE
|
|
209
|
-
* @property {string} ONE_WAY
|
|
210
|
-
*/
|
|
211
|
-
export const API_TYPE = {
|
|
212
|
-
'REQUEST_RESPONSE': 'requet_response',
|
|
213
|
-
'ONE_WAY': 'one_way'
|
|
214
|
-
}
|
|
215
205
|
|
|
216
206
|
/**
|
|
217
207
|
* @typedef {object} STATUS
|
|
218
208
|
* @property {number} OK
|
|
219
209
|
* @property {number} ERROR
|
|
220
210
|
*/
|
|
221
|
-
//
|
|
211
|
+
// service response status code
|
|
222
212
|
export const STATUS = {
|
|
223
213
|
OK: 0,
|
|
224
214
|
//0~127: success
|
package/src/server/Manager.js
CHANGED
|
@@ -119,7 +119,7 @@ export class Manager {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
|
|
122
|
-
if (remote.uid && this.server.
|
|
122
|
+
if (remote.uid && this.server.serviceNames.has('account')) {
|
|
123
123
|
let req = { topic: 'detachUserRemote' }
|
|
124
124
|
req.args = ['caller:manager.removeRemote']
|
|
125
125
|
this.server.emit('account', remote, req)
|
package/src/server/Remote.js
CHANGED
|
@@ -187,6 +187,7 @@ export class Remote extends RemoteCore {
|
|
|
187
187
|
|
|
188
188
|
// node.js server side remote.
|
|
189
189
|
send(message, isBinary) {
|
|
190
|
+
// console.log(`<-S [${IOMsg[ message[0]]}]`)
|
|
190
191
|
this.manager.txBytes += message.byteLength;
|
|
191
192
|
this.socket.txCounter++;
|
|
192
193
|
if (this.socketType === 'websocket') {
|
package/src/server/RemoteCore.js
CHANGED
|
@@ -265,15 +265,15 @@ export class RemoteCore {
|
|
|
265
265
|
break;
|
|
266
266
|
|
|
267
267
|
|
|
268
|
-
case IOMsg.
|
|
268
|
+
case IOMsg.CALL: //service request
|
|
269
269
|
try {
|
|
270
270
|
let req = MBP.unpack(message)
|
|
271
271
|
if (req) {
|
|
272
272
|
if (!req.target || !req.topic) return
|
|
273
|
-
if (this.manager.server.
|
|
273
|
+
if (this.manager.server.serviceNames.has(req.target)) {
|
|
274
274
|
this.manager.server.emit(req.target, this, req)
|
|
275
275
|
} else {
|
|
276
|
-
// console.log('UnKnown
|
|
276
|
+
// console.log('UnKnown Service call: ', req.target)
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
} catch (e) {
|
package/src/server/Server.js
CHANGED
|
@@ -3,13 +3,13 @@ import EventEmitter from 'events'
|
|
|
3
3
|
import { WebSocketServer } from 'ws'
|
|
4
4
|
import { Manager } from './Manager.js'
|
|
5
5
|
import { serverOption } from './serverOption.js'
|
|
6
|
-
import { STATUS } from '../
|
|
6
|
+
import { STATUS } from '../services/constant.js'
|
|
7
7
|
|
|
8
8
|
export class Server extends EventEmitter {
|
|
9
9
|
|
|
10
10
|
constructor(options, authManager) {
|
|
11
11
|
super();
|
|
12
|
-
this.
|
|
12
|
+
this.serviceNames = new Set()
|
|
13
13
|
this.wss = {};
|
|
14
14
|
this.port = null;
|
|
15
15
|
this.congPort = null;
|
|
@@ -130,63 +130,63 @@ export class Server extends EventEmitter {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
|
-
*
|
|
134
|
-
* target:
|
|
135
|
-
*
|
|
133
|
+
* RPC service register.
|
|
134
|
+
* target: service_name <string>
|
|
135
|
+
* service: service_module <function module|class instance>
|
|
136
136
|
* return this
|
|
137
137
|
*/
|
|
138
|
-
|
|
138
|
+
attach(target, service_module) {
|
|
139
139
|
|
|
140
140
|
if( typeof target !== 'string'){
|
|
141
|
-
throw new Error(`
|
|
141
|
+
throw new Error(`service( target ,service ) target name is not a string.`)
|
|
142
142
|
}
|
|
143
|
-
if (!
|
|
144
|
-
throw new Error(`
|
|
143
|
+
if (!service_module.checkPermission || typeof service_module.checkPermission != 'function') {
|
|
144
|
+
throw new Error(`Service ${target} : no checkPermission or not a function.`)
|
|
145
145
|
}
|
|
146
|
-
if(!
|
|
147
|
-
throw new Error(`
|
|
146
|
+
if(!service_module.commands || !Array.isArray(service_module.commands) ){
|
|
147
|
+
throw new Error(`Service ${target} : no commands or !Array.`)
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
//
|
|
151
|
-
if (
|
|
150
|
+
// Service TYPE 1. single call() function.
|
|
151
|
+
if ( service_module.call && typeof service_module.call == 'function' ) {
|
|
152
152
|
this.on(target, async (remote, req) => {
|
|
153
153
|
try {
|
|
154
|
-
if (!
|
|
154
|
+
if (!service_module.checkPermission(remote, req)) {
|
|
155
155
|
remote.response(req.mid, STATUS.ERROR, "NO_PERMISSION.")
|
|
156
156
|
return
|
|
157
157
|
}
|
|
158
|
-
if(
|
|
159
|
-
// console.log('server
|
|
160
|
-
await
|
|
158
|
+
if( service_module.commands.includes( req.topic )){
|
|
159
|
+
// console.log('server service_module req', req )
|
|
160
|
+
await service_module.call(remote, req)
|
|
161
161
|
}else{
|
|
162
162
|
remote.response(req.mid, STATUS.ERROR, "UNKNOWN_COMMAND");
|
|
163
163
|
}
|
|
164
164
|
} catch (error) {
|
|
165
|
-
console.error(`Unhandled
|
|
165
|
+
console.error(`Unhandled Service Error in target [${target}]:`, error);
|
|
166
166
|
remote.response(req.mid, STATUS.ERROR, "INTERNAL_SERVER_ERROR");
|
|
167
167
|
}
|
|
168
168
|
})
|
|
169
169
|
} else {
|
|
170
|
-
//
|
|
170
|
+
// Service TYPE 2. multiple functions.
|
|
171
171
|
this.on(target, async (remote, req) => {
|
|
172
172
|
try {
|
|
173
|
-
if (!
|
|
173
|
+
if (!service_module.checkPermission(remote, req)) {
|
|
174
174
|
remote.response(req.mid, STATUS.ERROR, "NO_PERMISSION.")
|
|
175
175
|
return
|
|
176
176
|
}
|
|
177
|
-
if(
|
|
178
|
-
await
|
|
177
|
+
if( service_module.commands.includes( req.topic )){
|
|
178
|
+
await service_module[req.topic](remote, req)
|
|
179
179
|
}else{
|
|
180
180
|
remote.response(req.mid, STATUS.ERROR, "UNKNOWN_COMMAND");
|
|
181
181
|
}
|
|
182
182
|
} catch (error) {
|
|
183
|
-
console.error(`Unhandled
|
|
183
|
+
console.error(`Unhandled Service Error in target [${target}]:`, error);
|
|
184
184
|
remote.response(req.mid, STATUS.ERROR, "INTERNAL_SERVER_ERROR");
|
|
185
185
|
}
|
|
186
186
|
})
|
|
187
187
|
}
|
|
188
|
-
this.
|
|
189
|
-
// console.log(`[
|
|
188
|
+
this.serviceNames.add(target)
|
|
189
|
+
// console.log(`[Service attached: ${target} ] accept commands: ${service_module.commands}`)
|
|
190
190
|
return this
|
|
191
191
|
}
|
|
192
192
|
|
|
@@ -196,8 +196,8 @@ export class Server extends EventEmitter {
|
|
|
196
196
|
this.manager.close()
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
this.
|
|
200
|
-
this.
|
|
199
|
+
this.serviceNames.forEach(v => this.removeAllListeners(v))
|
|
200
|
+
this.serviceNames.clear()
|
|
201
201
|
|
|
202
202
|
let serverCount = 0;
|
|
203
203
|
if (this.wss) serverCount++;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* RedisService <IOSignal RPC Service>
|
|
3
3
|
*
|
|
4
4
|
* iosignal 클라이언트의 redis 질의 요청을 받으면,
|
|
5
5
|
* redisClient 로 연결된 redis-server 에게 질의 후 결과를 되돌려 준다.
|
|
6
6
|
*
|
|
7
7
|
* client ex:
|
|
8
|
-
* await io.
|
|
8
|
+
* await io.call('redis','hget','user:id' )
|
|
9
9
|
*
|
|
10
10
|
*/
|
|
11
|
-
import { STATUS } from './
|
|
11
|
+
import { STATUS } from './constant.js'
|
|
12
12
|
|
|
13
13
|
const MIN_LEVEL = 200;
|
|
14
14
|
const COMMANDS = [
|
|
@@ -18,10 +18,10 @@ const COMMANDS = [
|
|
|
18
18
|
'sMembers', 'exists', 'sRem', 'del', 'keys', 'save'
|
|
19
19
|
]
|
|
20
20
|
|
|
21
|
-
export class
|
|
21
|
+
export class RedisService {
|
|
22
22
|
constructor(redisClient, _minLevel) {
|
|
23
23
|
if (!redisClient) {
|
|
24
|
-
throw new Error("
|
|
24
|
+
throw new Error("RedisService constructor: no redisClient")
|
|
25
25
|
}
|
|
26
26
|
this.redisClient = redisClient
|
|
27
27
|
this.minLevel = _minLevel ? _minLevel : MIN_LEVEL
|
|
@@ -32,7 +32,7 @@ export class RedisAPI {
|
|
|
32
32
|
return (remote.level >= this.minLevel) ? true : false;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
async
|
|
35
|
+
async call(remote, req) {
|
|
36
36
|
let result;
|
|
37
37
|
try {
|
|
38
38
|
let cmd = req.topic
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
/**
|
|
3
|
-
* simple IOSignal
|
|
3
|
+
* simple IOSignal RPC Service example.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { STATUS } from './
|
|
6
|
+
import { STATUS } from './constant.js'
|
|
7
7
|
export const commands = ['echo', 'date', 'unixtime'];
|
|
8
8
|
|
|
9
9
|
const MIN_LEVEL = 0;
|
|
@@ -12,7 +12,7 @@ export function checkPermission(remote) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* client api call example: await io.
|
|
15
|
+
* client api call example: await io.call('reply','echo','hello')
|
|
16
16
|
*/
|
|
17
17
|
export async function echo(remote, req) {
|
|
18
18
|
if (!req.args)
|
|
@@ -22,7 +22,7 @@ export async function echo(remote, req) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* client api call example: await io.
|
|
25
|
+
* client api call example: await io.call('reply','date')
|
|
26
26
|
*/
|
|
27
27
|
export async function date(remote, req) {
|
|
28
28
|
let r = new Date().toUTCString()
|
|
@@ -30,7 +30,7 @@ export async function date(remote, req) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* client api call example: await io.
|
|
33
|
+
* client api call example: await io.call('reply','unixtime')
|
|
34
34
|
*/
|
|
35
35
|
export async function unixtime(remote, req) {
|
|
36
36
|
let r = Math.floor(Date.now() / 1000)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { STATUS } from './
|
|
1
|
+
import { STATUS } from './constant.js'
|
|
2
2
|
|
|
3
3
|
export const commands = [
|
|
4
4
|
'cid', 'remotes', 'clients',
|
|
@@ -14,7 +14,7 @@ export function checkPermission(remote) {
|
|
|
14
14
|
return (remote.level >= MIN_LEVEL) ? true : false;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export async function
|
|
17
|
+
export async function call(remote, req) {
|
|
18
18
|
let result;
|
|
19
19
|
let status = 0;
|
|
20
20
|
try {
|
|
@@ -70,7 +70,7 @@ export async function request(remote, req) {
|
|
|
70
70
|
}
|
|
71
71
|
} else {
|
|
72
72
|
status = STATUS.ERROR;
|
|
73
|
-
result = "
|
|
73
|
+
result = "service sudo: no such a cmd: " + cmd
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
remote.response(req.mid, status, result)
|
package/tsconfig.json
CHANGED
|
File without changes
|