Browse Source

fix listener bugs

master
peter 3 years ago
parent
commit
8352ac1bf7
9 changed files with 278 additions and 144 deletions
  1. 1
    1
      package-lock.json
  2. 12
    10
      src/Backend.ts
  3. 13
    9
      src/Frontend.ts
  4. 1
    1
      src/Interfaces.ts
  5. 8
    3
      src/PromiseIO/Client.ts
  6. 15
    6
      src/PromiseIO/Server.ts
  7. 4
    4
      src/Types.ts
  8. 54
    57
      src/Utils.ts
  9. 170
    53
      test/Test.ts

+ 1
- 1
package-lock.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "rpclibrary",
3
-  "version": "2.0.0",
3
+  "version": "2.2.1",
4 4
   "lockfileVersion": 1,
5 5
   "requires": true,
6 6
   "dependencies": {

+ 12
- 10
src/Backend.ts View File

@@ -10,7 +10,7 @@ export class RPCServer<
10 10
     InterfaceT extends T.RPCInterface = T.RPCInterface,
11 11
 > {
12 12
 
13
-    private pio = PromiseIO.createServer()
13
+    private pio = new PromiseIO()
14 14
     private closeHandler: T.CloseHandler
15 15
     private errorHandler: T.ErrorHandler
16 16
     private connectionHandler: T.ConnectionHandler
@@ -37,7 +37,6 @@ export class RPCServer<
37 37
             return this.sesame!(sesame!)
38 38
         })
39 39
 
40
-
41 40
         this.errorHandler = (socket: I.Socket | PromiseIO) => (error: any, rpcName: string, args: any[]) => {
42 41
             if (conf.errorHandler) conf.errorHandler(socket, error, rpcName, args)
43 42
             else throw error
@@ -65,11 +64,13 @@ export class RPCServer<
65 64
         }
66 65
         
67 66
         try {
68
-            this.pio.on('socket', (socket: I.Socket) => {
69
-                socket.on('error', (err) => this.errorHandler(socket, err, "system", []))
70
-                socket.on('close', () => this.closeHandler(socket))
71
-                this.connectionHandler(socket)
72
-                this.initRPCs(socket)
67
+
68
+            this.pio.on('socket', (clientSocket: I.Socket) => {
69
+                const sock:any = clientSocket;
70
+                clientSocket.on('disconnect', () => this.closeHandler(clientSocket))
71
+                this.connectionHandler(clientSocket)
72
+                this.initRPCs(clientSocket)
73
+                
73 74
             })
74 75
         } catch (e) {
75 76
             this.errorHandler(this.pio, e, 'system', [])
@@ -88,12 +89,13 @@ export class RPCServer<
88 89
         return this
89 90
     }
90 91
 
91
-    protected initRPCs(socket: I.Socket) {
92
-        socket.hook('info', async (sesame?: string) => {
92
+    protected initRPCs(clientSocket: I.Socket) {
93
+        clientSocket.hook('info', async (sesame?: string) => {
93 94
             const rpcs = await Promise.all(this.exporters.map(async exp => {
94 95
                 const allowed = await this.accessFilter(sesame, exp)
95 96
                 if (!allowed) return []
96
-                return U.rpcHooker(socket, exp, this.errorHandler, this.sesame)
97
+                const infos = U.rpcHooker(clientSocket, exp, this.errorHandler, this.sesame)
98
+                return infos
97 99
             }))
98 100
             return rpcs.flat()
99 101
         })

+ 13
- 9
src/Frontend.ts View File

@@ -16,7 +16,7 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
16 16
         return await socket.connect(sesame)
17 17
     }
18 18
 
19
-    private protocol: 'http:' | 'https:'
19
+    private protocol: 'http' | 'https'
20 20
     private socket: I.Socket
21 21
     private handlers : {
22 22
         [name in string]: T.AnyFunction[]
@@ -32,9 +32,9 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
32 32
      * @param server Server address
33 33
      * @param tls @default false use TLS
34 34
      */
35
-    constructor(public port:number, private server: string, conf:T.SocketConf = { tls: false }){
35
+    constructor(public port:number, public address: string, conf:T.SocketConf = { tls: false }){
36 36
         Object.defineProperty(this, 'socket', {value: undefined, writable: true})
37
-        this.protocol = conf.tls ? "https:" : "http:"
37
+        this.protocol = conf.tls ? "https" : "http"
38 38
     }
39 39
 
40 40
     /**
@@ -141,7 +141,7 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
141 141
     public async connect( sesame?: string ) : Promise<T.ConnectedSocket<Ifc>> {
142 142
 
143 143
         try{
144
-            this.socket = await PromiseIOClient.connect(this.port, this.server, this.protocol)
144
+            this.socket = await PromiseIOClient.connect(this.port, this.address, this.protocol)
145 145
         }catch(e){
146 146
             this.handlers['error'].forEach(h => h(e))
147 147
             throw e
@@ -215,14 +215,17 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
215 215
         sesame = appendComma(sesame, true)
216 216
         headerArgs = fnArgs.length>0?headerArgs+",":headerArgs 
217 217
 
218
-        return eval( `
219
-        async (${headerArgs} callback) => {
218
+        const frontendHookStr = `
219
+        async (${headerArgs} $__callback__$) => {
220 220
             const r = await this.call("${fnName}", ${sesame} ${argParams})
221 221
             try{
222 222
                 if(r){
223 223
                     if(r.uuid){
224
-                        callback['destroy'] = () => { this.socket.unhook(r.uuid) }
225
-                        this.socket.hook(r.uuid, callback)
224
+                        $__callback__$['destroy'] = () => {
225
+                            this.socket.fire(r.uuid)
226
+                            this.socket.unhook(r.uuid) 
227
+                        }
228
+                        this.socket.hook(r.uuid, $__callback__$)
226 229
                     }
227 230
                     return r.return
228 231
                 }else{
@@ -231,6 +234,7 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
231 234
             }catch(e){
232 235
                 throw e
233 236
             }
234
-        }`)
237
+        }`
238
+        return eval(frontendHookStr)
235 239
     }
236 240
 }

+ 1
- 1
src/Interfaces.ts View File

@@ -20,6 +20,6 @@ export interface Socket {
20 20
     call: (rpcname: string, ...args: any[]) => Promise<any>
21 21
     fire: (rpcname: string, ...args: any[]) => Promise<any>
22 22
     on: (type: string, f: T.AnyFunction)=>any
23
-    emit: (eventName: string, data: any) => void
23
+    emit: (eventName: string, ...args: any[]) => void
24 24
     close(): void
25 25
   }

+ 8
- 3
src/PromiseIO/Client.ts View File

@@ -5,21 +5,26 @@ import * as socketio from 'socket.io-client'
5 5
 
6 6
 export class PromiseIOClient {
7 7
     
8
-    static connect = (port: number, host = "localhost", protocol : 'http:' | 'https:' = "http:"): Promise<I.Socket> => new Promise((res, rej) => {
8
+    static connect = (port: number, host = "localhost", protocol : 'http' | 'https' = "http"): Promise<I.Socket> => new Promise((res, rej) => {
9 9
         try {
10
-            const socket = socketio(`${protocol}//${host}:${port}`, {
10
+            const address = `${host}:${port}`
11
+            const socket = socketio(`${protocol}://${address}`, {
11 12
                 reconnectionAttempts: 2,
12 13
                 reconnectionDelay: 200,
13 14
                 timeout: 450,
14 15
                 reconnection: false,
15 16
             })
17
+
16 18
             socket.on('connect_error', e => {
17 19
                 sock.emit('error', e)
18 20
                 rej(e) 
19 21
             })
20 22
 
23
+            socket['address'] = address
21 24
             const sock = U.makePioSocket(socket)
22
-            socket.on('connect', ()=>{ res(sock) })
25
+            socket.on('connect', ()=>{
26
+                res(sock) 
27
+            })
23 28
 
24 29
 
25 30
             /*

+ 15
- 6
src/PromiseIO/Server.ts View File

@@ -4,6 +4,7 @@ import * as U from '../Utils'
4 4
 import * as T from '../Types'
5 5
 import socketio = require('socket.io')
6 6
 
7
+
7 8
 export class PromiseIO {
8 9
     io?: Server
9 10
     httpServer: httpServer
@@ -12,15 +13,23 @@ export class PromiseIO {
12 13
         connect: []
13 14
     }
14 15
 
15
-    static createServer(): PromiseIO {
16
-        return new PromiseIO();
17
-    }
18
-
19 16
     attach(httpServer: httpServer) {
20 17
         this.httpServer = httpServer
21 18
         this.io = socketio(httpServer, { cookie:false })
22
-        this.io!.on('connection', (sock: Socket) => {
23
-            const pioSock = U.makePioSocket(sock)
19
+
20
+        this.io!.on('connection', (clientSocket: Socket) => {
21
+            console.log(this.listeners);
22
+
23
+            clientSocket.use((packet, next) => {
24
+                console.log(this.listeners[packet[0]]);
25
+                
26
+                console.log(packet[0]);
27
+                next()
28
+            })
29
+    
30
+
31
+            clientSocket['address'] = clientSocket.handshake.headers["x-real-ip"] || clientSocket.handshake.address
32
+            const pioSock = U.makePioSocket(clientSocket)
24 33
             this.listeners['socket'].forEach(listener => listener(pioSock))
25 34
             this.listeners['connect'].forEach(listener => listener(pioSock))
26 35
             /*

+ 4
- 4
src/Types.ts View File

@@ -5,8 +5,6 @@ import { PromiseIO } from "./PromiseIO/Server";
5 5
 export type PioBindListener = (...args: any) => void
6 6
 export type PioHookListener = AnyFunction
7 7
 
8
-
9
-
10 8
 export type AnyFunction = (...args:any) => any
11 9
 export type HookFunction = AnyFunction
12 10
 export type AccessFilter<InterfaceT extends RPCInterface = RPCInterface> = (sesame:string|undefined, exporter: I.RPCExporter<InterfaceT, keyof InterfaceT>) => Promise<boolean> | boolean
@@ -57,7 +55,7 @@ export type HookRPC<Name, Func extends AnyFunction> = {
57 55
     name: Name
58 56
     hook: Func
59 57
     onCallback?: AnyFunction
60
-    onClose?: HookCloseFunction<ReturnType<Func> extends Promise<infer T> ? T : ReturnType<Func>>
58
+    onDestroy?: HookCloseFunction<ReturnType<Func> extends Promise<infer T> ? T : ReturnType<Func>>
61 59
 }
62 60
 
63 61
 export type RPC<Name, Func extends AnyFunction> = HookRPC<Name, Func> | CallRPC<Name,Func> | Func
@@ -101,4 +99,6 @@ export type HookCloseFunction<T> = (res: T, rpc:HookRPC<any, any>) => any
101 99
 
102 100
 export type AsyncIfc<Ifc extends RPCInterface> = { [grp in keyof Ifc]: {[rpcname in keyof Ifc[grp]] : AsyncAnyFunction<Ifc[grp][rpcname]> } }
103 101
 
104
-export type AsyncAnyFunction<F extends AnyFunction = AnyFunction> = F extends (...args: Parameters<F>) => infer R ? ((...args: Parameters<F>) => R extends Promise<any> ? R : Promise<R> ) : Promise<any>
102
+export type AsyncAnyFunction<F extends AnyFunction = AnyFunction> = F extends (...args: Parameters<F>) => infer R 
103
+                                                                    ? ((...args: Parameters<F>) => R extends Promise<any> ? R : Promise<R> ) 
104
+                                                                    : Promise<any>

+ 54
- 57
src/Utils.ts View File

@@ -2,8 +2,7 @@ import * as uuidv4 from "uuid/v4"
2 2
 
3 3
 import * as T from "./Types";
4 4
 import * as I from "./Interfaces";
5
-import { Server as ioServer, Socket as ioSocket, Socket } from "socket.io"
6
-import { Socket as ioClientSocket } from "socket.io-client"
5
+import { Socket } from "socket.io"
7 6
 
8 7
 /**
9 8
  * Translate an RPC to RPCInfo for serialization.
@@ -17,20 +16,22 @@ export const rpcToRpcinfo = (socket: I.Socket, rpc: T.RPC<any, any>, owner: stri
17 16
     switch (typeof rpc) {
18 17
         case "object":
19 18
             if (rpc['call']) {
19
+                const _rpc: T.CallRPC<any, any> = rpc
20 20
                 return {
21 21
                     owner: owner,
22 22
                     argNames: extractArgs(rpc['call']),
23 23
                     type: "Call",
24 24
                     name: rpc.name,
25
-                    call: sesame ? async (_sesame, ...args) => { if (sesame(_sesame)) return await rpc['call'].apply({}, args); socket.close() } : rpc['call'], // check & remove sesame 
25
+                    call: sesame ? async ($__sesame__$, ...args) => { if (sesame($__sesame__$)) return await rpc['call'].apply({}, args); socket.close() } : rpc['call'], // check & remove sesame 
26 26
                 }
27 27
             } else {
28
-                const generator = hookGenerator(<T.HookRPC<any, any>>rpc, errorHandler, sesame)
28
+                const _rpc: T.HookRPC<any, any> = rpc
29
+                const generator = hookGenerator(_rpc, errorHandler, sesame)
29 30
                 return {
30 31
                     owner: owner,
31 32
                     argNames: extractArgs(generator(undefined)),
32 33
                     type: "Hook",
33
-                    name: rpc.name,
34
+                    name: _rpc.name,
34 35
                     generator: generator,
35 36
                 }
36 37
             }
@@ -47,7 +48,7 @@ RPC did not provide a name.
47 48
                 argNames: extractArgs(rpc),
48 49
                 type: "Call",
49 50
                 name: rpc.name,
50
-                call: sesame ? async (_sesame, ...args) => { if (sesame(_sesame)) return await rpc.apply({}, args); throw makeError(rpc.name) } : rpc, // check & remove sesame 
51
+                call: sesame ? async ($__sesame__$, ...args) => { if (sesame($__sesame__$)) return await rpc.apply({}, args); throw makeError(rpc.name) } : rpc, // check & remove sesame 
51 52
             }
52 53
     }
53 54
     throw new Error("Bad socketIORPC type " + typeof rpc)
@@ -55,23 +56,22 @@ RPC did not provide a name.
55 56
 
56 57
 /**
57 58
  * Utility function to apply the RPCs of an {@link RPCExporter}.
58
- * @param socket The websocket (implementation: bsock) to hook on
59
+ * @param serverSocket The websocket (implementation: socket.io) to hook on
59 60
  * @param exporter The exporter
60 61
  * @param makeUnique @default true Attach a suffix to RPC names
61 62
  */
62
-export function rpcHooker(socket: I.Socket, exporter: I.RPCExporter<any, any>, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction, makeUnique = true): T.ExtendedRpcInfo[] {
63
+export function rpcHooker(serverSocket: I.Socket, exporter: I.RPCExporter<any, any>, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction, makeUnique = true): T.ExtendedRpcInfo[] {
63 64
     const owner = exporter.name
64 65
     const RPCs = typeof exporter.RPCs === "function" ? exporter.RPCs() : exporter.RPCs
65 66
 
66
-    return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame))
67
+    return RPCs
68
+        .map(rpc => rpcToRpcinfo(serverSocket, rpc, owner, errorHandler, sesame))
67 69
         .map(info => {
68 70
             const suffix = makeUnique ? "-" + uuidv4().substr(0, 4) : ""
69 71
             const ret: any = info
70 72
             ret.uniqueName = info.name + suffix
71
-            let rpcFunction = info.type === 'Hook' ? info.generator(socket)
72
-                : info.call
73
-
74
-            socket.hook(ret.uniqueName, callGenerator(info.name, socket, rpcFunction, errorHandler))
73
+            let rpcFunction = info.type === 'Hook' ? info.generator(serverSocket) : info.call
74
+            serverSocket.hook(ret.uniqueName, callGenerator(info.name, serverSocket, rpcFunction, errorHandler))
75 75
             return ret
76 76
         })
77 77
 }
@@ -80,18 +80,20 @@ export function rpcHooker(socket: I.Socket, exporter: I.RPCExporter<any, any>, e
80 80
  * Decorate an RPC with the error handler
81 81
  * @param rpcFunction the function to decorate
82 82
  */
83
-const callGenerator = (rpcName: string, socket: I.Socket, rpcFunction: T.AnyFunction, errorHandler: T.ErrorHandler): T.AnyFunction => {
83
+const callGenerator = (rpcName: string, $__socket__$: I.Socket, rpcFunction: T.AnyFunction, errorHandler: T.ErrorHandler): T.AnyFunction => {
84 84
     const argsArr = extractArgs(rpcFunction)
85 85
     const args = argsArr.join(',')
86 86
     const argsStr = argsArr.map(stripAfterEquals).join(',')
87 87
 
88
-    return eval(`async (` + args + `) => {
88
+    const callStr = `async (${args}) => {
89 89
         try{
90
-            return await rpcFunction(`+ argsStr + `)
90
+            return await rpcFunction(${argsStr})
91 91
         }catch(e){
92
-            errorHandler(socket)(e, rpcName, [`+ args + `])
92
+            errorHandler($__socket__$)(e, rpcName, [${args}])
93 93
         }
94
-    }`)
94
+    }`
95
+
96
+    return eval(callStr);
95 97
 }
96 98
 
97 99
 /**
@@ -107,44 +109,40 @@ export function stripAfterEquals(str: string): string {
107 109
  * @param rpc The RPC to transform
108 110
  * @returns A {@link HookFunction}
109 111
  */
110
-const hookGenerator = (rpc: T.HookRPC<any, any>, /*not unused!*/ errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => {
111
-
112
+const hookGenerator = (rpc: T.HookRPC<any, any>, errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction, injectSocket?: boolean): T.HookInfo['generator'] => {
112 113
     let argsArr = extractArgs(rpc.hook)
113
-    argsArr.pop() //remove 'callback' from the end
114
-    let callArgs = argsArr.join(',')
114
+    argsArr.pop() //remove callback param
115 115
 
116
+    let callArgs = argsArr.join(',')
116 117
     const args = sesameFn ? (['sesame', ...argsArr].join(','))
117
-        : callArgs
118
+                          : callArgs
118 119
 
119 120
     callArgs = appendComma(callArgs, false)
120 121
 
121
-    //note rpc.hook is the associated RPC, not a socket.hook
122
-    return eval(`
123
-    (clientSocket) => async (${args}) => {
122
+    const hookStr = `
123
+    ($__socket__$) => async (${args}) => {
124 124
         try{
125 125
             if(sesameFn && !sesameFn(sesame)) return
126 126
             const uuid = uuidv4()
127 127
             const res = await rpc.hook(${callArgs} (...cbargs) => {
128
-                if(rpc.onCallback){
129
-                    rpc.onCallback.apply({}, cbargs)
130
-                }
131
-                clientSocket.call.apply(clientSocket, [uuid, ...cbargs])
128
+                ${rpc.onCallback ? `rpc.onCallback.apply({}, cbargs)` : ``}
129
+                $__socket__$.call.apply($__socket__$, [uuid, ...cbargs])
132 130
             })
133
-            if(rpc.onClose){
134
-                clientSocket.on('close', () => rpc.onClose(res, rpc))
135
-            }
131
+            ${rpc.onDestroy ? `$__socket__$.bind(uuid, () => {
132
+                rpc.onDestroy(res, rpc)
133
+            })` : ``}
136 134
             return {'uuid': uuid, 'return': res}
137 135
         }catch(e){
138 136
             //can throw to pass exception to client or swallow to keep it local
139
-            errorHandler(clientSocket)(e, ${rpc.name}, [${args}])
137
+            errorHandler($__socket__$)(e, ${rpc.name}, [${args}])
140 138
         }
141
-    }`)
142
-}
139
+    }`
143 140
 
144
-const makeError = (callName: string) => {
145
-    return new Error("Call not found: " + callName + ". ; Zone: <root> ; Task: Promise.then ; Value: Error: Call not found: " + callName)
141
+    return eval(hookStr)
146 142
 }
147 143
 
144
+const makeError = (callName: string) =>  new Error(`Call not found: ${callName}. ; Zone: <root> ; Task: Promise.then ; Value: Error: Call not found: ${callName}`)
145
+
148 146
 /**
149 147
  * Extract a string list of parameters from a function
150 148
  * @param f The source function
@@ -189,24 +187,25 @@ export function fixNames(o: Object): void {
189 187
     })
190 188
 }
191 189
 
190
+/**
191
+ * Transforms a socket.io instance into one conforming to I.Socket
192
+ * @param socket A socket.io socket
193
+ */
192 194
 export const makePioSocket = (socket: any): I.Socket => {
193
-    return {
194
-        bind: (name: string, listener: T.PioBindListener) => socket.on(name, (...args: any) => {
195
-            const ack = args.pop()
196
-            listener.apply(null, args)
197
-            ack()
198
-        }),
195
+    return <I.Socket>{
196
+        id: socket.id,
197
+        bind: (name: string, listener: T.PioBindListener) => socket.on(name, (...args: any) => listener.apply(null, args)),
199 198
 
200 199
         hook: (name: string, listener: T.PioHookListener) => {
201 200
             const args = extractArgs(listener)
202 201
             let argNames
203 202
             let restParam = args.find(e => e.includes('...'))
204
-            if(!restParam){
205
-                argNames = [...args, '...__args__'].join(',')
206
-                restParam = '__args__'
207
-            }else{
203
+            if (!restParam) {
204
+                argNames = [...args, '...$__args__$'].join(',')
205
+                restParam = '$__args__$'
206
+            } else {
208 207
                 argNames = [...args].join(',')
209
-                restParam = restParam.replace('...','')
208
+                restParam = restParam.replace('...', '')
210 209
             }
211 210
 
212 211
             const decoratedListener = eval(`(() => async (${argNames}) => {
@@ -229,7 +228,7 @@ export const makePioSocket = (socket: any): I.Socket => {
229 228
         call: (name: string, ...args: any) => {
230 229
             return new Promise((res, rej) => {
231 230
                 const params: any = [name, ...args, (resp) => {
232
-                    if(isError(resp)){
231
+                    if (isError(resp)) {
233 232
                         const err = new Error()
234 233
                         err.stack = resp.stack
235 234
                         err.name = resp.name
@@ -238,7 +237,7 @@ export const makePioSocket = (socket: any): I.Socket => {
238 237
                     }
239 238
                     res(resp)
240 239
                 }]
241
-                socket.emit.apply(socket, params)                    
240
+                socket.emit.apply(socket, params)
242 241
             })
243 242
         },
244 243
 
@@ -256,17 +255,15 @@ export const makePioSocket = (socket: any): I.Socket => {
256 255
             }
257 256
         },
258 257
 
259
-        id: socket.id,
260 258
         on: (...args) => socket.on.apply(socket, args),
261 259
         emit: (...args) => socket.emit.apply(socket, args),
262 260
         close: () => {
263
-            socket
264 261
             socket.disconnect(true)
265 262
         }
266 263
     }
267 264
 }
268 265
 
269
-export const isError = function(e){
270
-    return e && e.stack && e.message && typeof e.stack === 'string' 
271
-           && typeof e.message === 'string';
272
-   }
266
+export const isError = function (e) {
267
+    return e && e.stack && e.message && typeof e.stack === 'string'
268
+        && typeof e.message === 'string';
269
+}

+ 170
- 53
test/Test.ts View File

@@ -1,16 +1,20 @@
1 1
 import { describe, it } from "mocha";
2 2
 import { RPCServer, RPCSocket } from '../Index'
3
-import { RPCExporter } from "../src/Interfaces";
3
+import { RPCExporter, Socket } from "../src/Interfaces";
4 4
 import { ConnectedSocket } from "../src/Types";
5 5
 import * as log from 'why-is-node-running';
6 6
 import * as http from 'http';
7 7
 import * as express from 'express';
8 8
 import * as fetch from 'node-fetch';
9
+import { PromiseIO } from "../src/PromiseIO/Server";
10
+import { PromiseIOClient } from "../src/PromiseIO/Client";
11
+
12
+const noop = (...args) => { }
9 13
 
10 14
 const add = (...args: number[]) => { return args.reduce((a, b) => a + b, 0) }
11
-function makeServer() {
15
+function makeServer(onCallback = noop, connectionHandler = noop, hookCloseHandler = noop, closeHandler = noop, errorHandler = (socket, err) => { throw err }) {
12 16
     let subcallback
13
-    const sv = new RPCServer([{
17
+    const serv = new RPCServer([{
14 18
         name: 'test',
15 19
         RPCs: [
16 20
             {
@@ -22,36 +26,92 @@ function makeServer() {
22 26
                     subcallback = callback
23 27
                     return { topic: "test" }
24 28
                 },
25
-                onClose: (res) => { }
29
+                onDestroy: hookCloseHandler
26 30
             }, {
27 31
                 name: 'subscribe',
28 32
                 hook: async (callback) => {
29 33
                     subcallback = callback
30 34
                     return { topic: "test" }
31 35
                 },
32
-                onClose: (res, rpc) => {
33
-                    console.log("onClose", rpc.name === 'subscribe' && res ? "OK" : "")
34
-                    subcallback = null
35
-                },
36
-                onCallback: (...args: any) => {
37
-                    console.log("onCallback", args[0] === "test" && args[1] === "callback" ? "OK" : "")
38
-                }
36
+                onDestroy: hookCloseHandler,
37
+                onCallback: onCallback
39 38
             },
40 39
             add,
41 40
             function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) },
41
+            function brokenRPC(){ throw new Error("Intended error") }
42 42
         ]
43
-    }], 
44
-    {
45
-        connectionHandler: (socket) => {
46
-        },
47
-        closeHandler: (socket) => { },
48
-        errorHandler: (socket, err) => { throw err }
49
-    })
50
-    sv.listen(21010)
51
-    return sv
43
+    }],
44
+        {
45
+            connectionHandler: connectionHandler,
46
+            closeHandler: closeHandler,
47
+            errorHandler: errorHandler
48
+        })
49
+    serv.listen(21010)
50
+    return serv
52 51
 }
53 52
 
54 53
 
54
+describe('PromiseIO', () => {
55
+
56
+    it("bind + fire", (done) => {
57
+        const server = new PromiseIO()
58
+        server.attach(new http.Server())
59
+        server.on("socket", clientSocket => {
60
+            clientSocket.bind("test123", (p1,p2) => {
61
+                server.close()
62
+                if(p1 === "p1" && p2 === "p2")
63
+                    done()
64
+            })
65
+        });
66
+
67
+        server.listen(21003)
68
+        PromiseIOClient.connect(21003, "localhost", "http").then(cli => {
69
+            cli.fire("test123", "p1", "p2")
70
+            cli.close()
71
+        })
72
+    })
73
+
74
+    it("hook + call", (done) => {
75
+        const server = new PromiseIO()
76
+        server.attach(new http.Server())
77
+        server.on("socket", clientSocket => {
78
+            clientSocket.hook("test123", (p1,p2) => {
79
+                if(p1 === "p1" && p2 === "p2")
80
+                    return "OK"
81
+            })
82
+        });
83
+
84
+        server.listen(21003)
85
+        PromiseIOClient.connect(21003, "localhost", "http").then(cli => {
86
+            cli.call("test123", "p1", "p2").then(resp => {
87
+                cli.close()
88
+                server.close()
89
+
90
+                if(resp === "OK")
91
+                    done()
92
+            })
93
+        })
94
+    })
95
+
96
+    it("on + emit", (done) => {
97
+        const server = new PromiseIO()
98
+        server.attach(new http.Server())
99
+        server.on("socket", clientSocket => {
100
+            clientSocket.on("test123", (p1,p2) => {
101
+                server.close()
102
+                if(p1 === "p1" && p2 === "p2")
103
+                    done()
104
+            })
105
+        });
106
+
107
+        server.listen(21003)
108
+        PromiseIOClient.connect(21003, "localhost", "http").then(cli => {
109
+            cli.emit("test123", "p1", "p2")
110
+            cli.close()
111
+        })
112
+    })
113
+})
114
+
55 115
 describe('RPCServer', () => {
56 116
     let client, server
57 117
     const echo = (x) => x
@@ -133,21 +193,21 @@ describe('RPCServer with premade http server', () => {
133 193
         {
134 194
             name: 'Grp2',
135 195
             RPCs: [
136
-                function test(){ return "test" }
196
+                function test() { return "test" }
137 197
             ],
138 198
         }
139 199
     ]
140 200
 
141
-    let client:RPCSocket, server:RPCServer
201
+    let client: RPCSocket, server: RPCServer
142 202
 
143 203
     before(done => {
144 204
         const expressServer = express()
145 205
         const httpServer = new http.Server(expressServer)
146 206
 
147
-        expressServer.get('/REST_ping', (req, res)=>{
207
+        expressServer.get('/REST_ping', (req, res) => {
148 208
             return res
149
-            .send('REST_pong')
150
-            .status(200)
209
+                .send('REST_pong')
210
+                .status(200)
151 211
         })
152 212
 
153 213
         server = new RPCServer(
@@ -157,7 +217,7 @@ describe('RPCServer with premade http server', () => {
157 217
         httpServer.listen(8080)
158 218
 
159 219
         client = new RPCSocket(8080, 'localhost')
160
-        
220
+
161 221
         done()
162 222
     })
163 223
 
@@ -171,10 +231,10 @@ describe('RPCServer with premade http server', () => {
171 231
     it('should serve REST', (done) => {
172 232
         fetch('http://localhost:8080/REST_ping').then(response => {
173 233
             response.text().then(text => {
174
-                if(text === "REST_pong")
234
+                if (text === "REST_pong")
175 235
                     done()
176 236
                 else
177
-                    done(new Error("REST repsonse was "+text))
237
+                    done(new Error("REST repsonse was " + text))
178 238
             })
179 239
         })
180 240
     })
@@ -197,6 +257,54 @@ describe('RPCServer with premade http server', () => {
197 257
 })
198 258
 
199 259
 
260
+describe('Serverside Triggers', () => {
261
+    let server, client
262
+    const closerFunction = (done) => () => {
263
+        client.close()
264
+        server.close()
265
+        done()
266
+    }
267
+
268
+    it('trigger onCallback', (done) => {
269
+        server = makeServer(closerFunction(done))
270
+        client = new RPCSocket(21010, "localhost")
271
+        client.connect().then(_ => {
272
+            client['test'].subscribe(noop).then(_ => client['test'].triggerCallback())
273
+        })
274
+    })
275
+
276
+    it('trigger connectionHandler', (done) => {
277
+        server = makeServer(undefined, closerFunction(done))
278
+        client = new RPCSocket(21010, "localhost")
279
+        client.connect()
280
+    })
281
+
282
+    
283
+    it('trigger hook closeHandler', (done) => {
284
+        server = makeServer(undefined, undefined, closerFunction(done))
285
+        client = new RPCSocket(21010, "localhost")
286
+        client.connect().then(_ => {
287
+            client['test'].subscribe(function cb(){
288
+                cb['destroy']()
289
+            }).then(_ => client['test'].triggerCallback())
290
+        })
291
+    })
292
+    
293
+
294
+    it('trigger global closeHandler', (done) => {
295
+        server = makeServer(undefined, undefined, undefined, () => {
296
+            server.close()
297
+            done()
298
+        })
299
+        client = new RPCSocket(21010, "localhost")
300
+        client.connect().then(_ => {
301
+            client['test'].subscribe(noop).then(_ => client.close())
302
+        })
303
+    })
304
+    
305
+
306
+})
307
+
200 308
 describe('RPCSocket', () => {
201 309
     let client: RPCSocket
202 310
     let server: RPCServer
@@ -266,6 +374,7 @@ describe('RPCSocket', () => {
266 374
     })
267 375
 })
268 376
 
377
+
269 378
 describe('It should do unhook', () => {
270 379
     const yesCandy = "OK"
271 380
     const noCandy = "stolen"
@@ -304,12 +413,12 @@ describe('It should do unhook', () => {
304 413
             function publish(): string { cb(candy); return candy },
305 414
             function unsubscribe(): string { candy = noCandy; cb(candy); cb = () => { }; return candy }
306 415
             ]
307
-        }], 
308
-        {
309
-            connectionHandler: (socket) => { },
310
-            closeHandler: (socket) => { },
311
-            errorHandler: (socket, err) => { throw err }
312
-        })
416
+        }],
417
+            {
418
+                connectionHandler: noop,
419
+                closeHandler: noop,
420
+                errorHandler: (socket, err) => { throw err }
421
+            })
313 422
         server.listen(21010)
314 423
         client = new RPCSocket(21010, "localhost")
315 424
         return await client.connect()
@@ -321,7 +430,7 @@ describe('It should do unhook', () => {
321 430
     })
322 431
 
323 432
     it('Subscribe with param', (done) => {
324
-        client['test'].subscribeWithParam("OK", c => { }).then(async (res) => {
433
+        client['test'].subscribeWithParam("OK", noop).then(async (res) => {
325 434
             if (res.uuid === candy) {
326 435
                 done()
327 436
             } else
@@ -363,10 +472,6 @@ type SesameTestIfc = {
363 472
         subscribe: (callback: Function) => Promise<topicDTO>
364 473
         manyParams: <A = string, B = number, C = boolean, D = Object>(a: A, b: B, c: C, d: D) => Promise<[A, B, C, D]>
365 474
     }
366
-
367
-    other: {
368
-        echo: (x: any) => Promise<any>
369
-    }
370 475
 }
371 476
 
372 477
 describe('Sesame should unlock the socket', () => {
@@ -387,17 +492,11 @@ describe('Sesame should unlock the socket', () => {
387 492
                             topic: 'test'
388 493
                         }
389 494
                     },
390
-                    onClose: (a) => { }
495
+                    onDestroy: noop
391 496
                 },
392
-                async function checkCandy() { cb(candy); cb = () => { }; return candy },
497
+                async function checkCandy() { cb(candy); cb = noop; return candy },
393 498
                 async function manyParams(a, b, c, d) { return [a, b, c, d] }
394 499
             ],
395
-        }, {
396
-            name: 'other',
397
-            RPCs: () => [
398
-                async function echo(x) { return x }
399
-            ]
400
-
401 500
         }], {
402 501
             sesame: (_sesame) => _sesame === 'sesame!'
403 502
         })
@@ -413,7 +512,7 @@ describe('Sesame should unlock the socket', () => {
413 512
         client.close()
414 513
         server.close()
415 514
     })
416
-
515
+    
417 516
     it('should work with sesame', (done) => {
418 517
         client.test.checkCandy().then(c => done())
419 518
     })
@@ -575,7 +674,7 @@ describe("Errorhandler functionality", () => {
575 674
             })
576 675
                 .then(r => {
577 676
                     if (r != null)
578
-                        done("UNEXPECTED RESULT " + r)
677
+                        done(new Error("UNEXPECTED RESULT " + r))
579 678
                 })
580 679
                 .catch((e) => {
581 680
                     done(new Error("UNEXPECTED CLIENT ERROR " + e.message))
@@ -614,11 +713,10 @@ describe("Errorhandler functionality", () => {
614 713
             })
615 714
                 .then(r => {
616 715
                     if (r != null)
617
-                        done("UNEXPECTED RESULT " + r)
716
+                        done(new Error("UNEXPECTED RESULT " + r))
618 717
                 })
619 718
                 .catch((e) => {
620
-                    done("UNEXPECTED CLIENT ERROR " + e)
621
-                    done(e)
719
+                    done(new Error("UNEXPECTED CLIENT ERROR " + e))
622 720
                 })
623 721
                 .finally(() => {
624 722
                     cli.close()
@@ -670,7 +768,7 @@ describe("Class binding", () => {
670 768
     before(done => {
671 769
         exporter1 = new MyExporter()
672 770
         serv = new RPCServer<myExporterIfc>(
673
-            [exporter1], 
771
+            [exporter1],
674 772
             {
675 773
                 accessFilter: async (sesame, exporter) => {
676 774
                     if (exporter.name === 'MyExporter') {
@@ -772,6 +870,25 @@ describe("attaching handlers before connecting", () => {
772 870
         })
773 871
     })
774 872
 
873
+    it("fires error if call is unknown", (done) => {
874
+        const serv = new RPCServer().listen(21004)
875
+        const sock = new RPCSocket(21004, 'localhost')
876
+
877
+        sock.on('error', (err) => {
878
+            sock.close()
879
+            serv.close()
880
+            done()
881
+        })
882
+
883
+        sock.connect().then(_ => {
884
+            sock.call("unknownRPC123", "AAAAA").catch(e => {  }).then(x => {
885
+                done(new Error("unexpected return value"))
886
+            })
887
+        }).catch(e => {
888
+            done(e)
889
+        })
890
+    })
891
+
775 892
     /*
776 893
      * ## 1.11.0 breaking ##
777 894
      * 

Loading…
Cancel
Save