Browse Source

fix

master
peter 5 years ago
parent
commit
2b765eb2a2
11 changed files with 165 additions and 77 deletions
  1. 7
    0
      index.ts
  2. 1
    1
      package-lock.json
  3. 40
    21
      src/Backend.ts
  4. 4
    6
      src/Frontend.ts
  5. 1
    2
      src/Interfaces.ts
  6. 10
    8
      src/Types.ts
  7. 49
    29
      src/Utils.ts
  8. 1
    1
      src/webpack.js
  9. 29
    4
      test/TestBackend.ts
  10. 22
    5
      test/TestFrontend.ts
  11. 1
    0
      test/index.html

+ 7
- 0
index.ts View File

1
+import * as Back from './src/backend/RPCSocketServer';
2
+import * as Front from './src/frontend/RPCSocket';
3
+
4
+export {
5
+    Back as Backend,
6
+    Front as Frontend
7
+}

+ 1
- 1
package-lock.json View File

1
 {
1
 {
2
   "name": "rpclibrary",
2
   "name": "rpclibrary",
3
-  "version": "1.0.1",
3
+  "version": "1.0.3",
4
   "lockfileVersion": 1,
4
   "lockfileVersion": 1,
5
   "requires": true,
5
   "requires": true,
6
   "dependencies": {
6
   "dependencies": {

+ 40
- 21
src/Backend.ts View File

18
 export class Server{
18
 export class Server{
19
     private ws = http.createServer()
19
     private ws = http.createServer()
20
     private io = bsock.createServer()
20
     private io = bsock.createServer()
21
+    private visibility:T.Visibility
22
+    private closeHandler:T.CloseHandler
23
+    private errorHandler: T.ErrorHandler
24
+    private connectionHandler: T.ConnectionHandler
25
+
21
 
26
 
22
     constructor(
27
     constructor(
23
         private port:number,
28
         private port:number,
24
         private exporters: I.Exporter[] = [],
29
         private exporters: I.Exporter[] = [],
25
-        private conf: T.SocketConf = {
26
-            errorHandler: (socket:I.Socket) => (error:any) => { socket.destroy(); console.error(error) },
27
-            closeHandler: (socket:I.Socket) => () => { console.log("Socket closing") },
28
-            connectionHandler: (socket:I.Socket) => { console.log("New websocket connection in port "+socket.port)},
29
-            visibility: "127.0.0.1"
30
-        }
30
+        conf: T.SocketConf =  {}
31
     ){
31
     ){
32
+        
33
+        if(!conf.visibility) this.visibility = "127.0.0.1"        
34
+        
35
+        if(!conf.errorHandler) this.errorHandler = 
36
+        (socket:I.Socket) => (error:any) => { 
37
+            socket.destroy(); 
38
+            console.error(error) 
39
+        }
40
+        
41
+        if(!conf.closeHandler) this.closeHandler = 
42
+        (socket:I.Socket) => () => { 
43
+            console.log("Socket on port "+socket.port+"closing") 
44
+        }
45
+        
46
+        if(!conf.connectionHandler) this.connectionHandler = 
47
+        (socket:I.Socket) => { 
48
+            console.log("New websocket connection in port "+socket.port)
49
+        }
50
+
51
+        let badRPC 
52
+        if(badRPC = exporters.flatMap(ex => ex.exportRPCs()).find(rpc => !rpc.name))
53
+        throw new Error(`
54
+            RPC did not provide a name. 
55
+            \nUse funtion name(..){ .. } syntax instead.
56
+            \n
57
+            \n<------------OFFENDING RPC:
58
+            \n`+badRPC.toString()+`
59
+            \n>------------OFFENDING RPC`)
60
+
32
         this.startWebsocket()
61
         this.startWebsocket()
33
     }
62
     }
34
 
63
 
36
         try{
65
         try{
37
             this.io.attach(this.ws)
66
             this.io.attach(this.ws)
38
             this.io.on('socket', (socket:I.Socket) => {
67
             this.io.on('socket', (socket:I.Socket) => {
39
-                socket.on('error', this.conf.errorHandler(socket))
40
-                socket.on('close', this.conf.closeHandler(socket))
41
-                if(this.conf.visibility === "127.0.0.1")
42
-                    this.initRPCs(socket)
43
-                else
44
-                    this.initPublicRPCs(socket)
68
+                socket.on('error', this.errorHandler(socket))
69
+                socket.on('close', this.closeHandler(socket))
70
+                this.connectionHandler(socket)
71
+                this.initRPCs(socket)
45
             })
72
             })
46
-            this.ws.listen(this.port, this.conf.visibility)
73
+            this.ws.listen(this.port, this.visibility)
47
         }catch(e){
74
         }catch(e){
48
             //@ts-ignore
75
             //@ts-ignore
49
             this.errorHandler(undefined)("Unable to connect to socket")
76
             this.errorHandler(undefined)("Unable to connect to socket")
57
             ...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter))
84
             ...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter))
58
         ]
85
         ]
59
     }
86
     }
60
-
61
-    protected initPublicRPCs(socket:I.Socket){
62
-        socket.hook('info', () => rpcInfos)
63
-
64
-        const rpcInfos:T.ExtendedRpcInfo[] = [
65
-            ...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter))
66
-        ]
67
-    }
68
 }
87
 }

+ 4
- 6
src/Frontend.ts View File

1
 'use strict'
1
 'use strict'
2
 
2
 
3
-var bsock = require('bsock')
3
+import bsock = require('bsock');
4
 
4
 
5
 import * as T from './Types'; 
5
 import * as T from './Types'; 
6
 import * as U from './Utils'; 
6
 import * as U from './Utils'; 
26
     constructor(public port:number, private server: string, private tls: boolean = false){
26
     constructor(public port:number, private server: string, private tls: boolean = false){
27
     }
27
     }
28
 
28
 
29
-
30
-
31
     public hook(name: T.Name, args: T.Arg){
29
     public hook(name: T.Name, args: T.Arg){
32
         return this.socket.hook(name, args)
30
         return this.socket.hook(name, args)
33
     }
31
     }
96
         return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => {
94
         return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => {
97
                             const r = await this.socket.call("`+fnName+`", `+argParams+`)
95
                             const r = await this.socket.call("`+fnName+`", `+argParams+`)
98
                             if(r.uid != null){
96
                             if(r.uid != null){
99
-                                this.socket.hook(res.uid, callback)
97
+                                this.socket.hook(r.uid, callback)
100
                             }
98
                             }
101
-                            return res
99
+                            return r
102
                         } )()` )
100
                         } )()` )
103
     }
101
     }
104
 
102
 
111
         return eval( `( () => async (`+headerArgs+`) => {
109
         return eval( `( () => async (`+headerArgs+`) => {
112
                             const r = await this.socket.call("`+fnName+`", `+argParams+`)
110
                             const r = await this.socket.call("`+fnName+`", `+argParams+`)
113
                             this.socket.unhook(`+argParams+`)
111
                             this.socket.unhook(`+argParams+`)
114
-                            return res
112
+                            return r
115
                         } )()` )
113
                         } )()` )
116
     }
114
     }
117
 
115
 

+ 1
- 2
src/Interfaces.ts View File

3
 
3
 
4
 export interface Exporter{
4
 export interface Exporter{
5
     name: T.Name
5
     name: T.Name
6
-    localRPCs() : T.RPC[]
7
-    publicRPCs() : T.RPC[]
6
+    exportRPCs() : T.RPC[]
8
 }
7
 }
9
 
8
 
10
 export interface Socket {
9
 export interface Socket {

+ 10
- 8
src/Types.ts View File

6
 export type Arg = string
6
 export type Arg = string
7
 export type Name = Arg
7
 export type Name = Arg
8
 export type Owner = Name
8
 export type Owner = Name
9
-
9
+export type ConnectionHandler = (socket:I.Socket) => void
10
+export type ErrorHandler = (socket:I.Socket) => (error:any) => void
11
+export type CloseHandler = (socket:I.Socket) => () =>  void
10
 export type SocketConf = {
12
 export type SocketConf = {
11
-    connectionHandler: (socket:I.Socket) => void
12
-    errorHandler: (socket:I.Socket) => (error:any) => void
13
-    closeHandler: (socket:I.Socket) => () =>  void
14
-    visibility: Visibility
13
+    connectionHandler?: ConnectionHandler
14
+    errorHandler?: ErrorHandler
15
+    closeHandler?: CloseHandler
16
+    visibility?: Visibility
15
 }
17
 }
16
 
18
 
17
 export type RPCType = 'Hook' | 'Unhook' | 'Call'
19
 export type RPCType = 'Hook' | 'Unhook' | 'Call'
23
 
25
 
24
 export type HookRPC = BaseRPC & {
26
 export type HookRPC = BaseRPC & {
25
     type: 'Hook'
27
     type: 'Hook'
26
-    clbk: CallbackFunction
28
+    hook: CallbackFunction
27
     unhook: UnhookFunction
29
     unhook: UnhookFunction
28
 }
30
 }
29
 
31
 
32
     unhook: UnhookFunction
34
     unhook: UnhookFunction
33
 }
35
 }
34
 
36
 
35
-export type CallRPC = BaseRPC & {
37
+export type CallRPC = (BaseRPC & {
36
     type: 'Call'
38
     type: 'Call'
37
     call: AsyncFunction
39
     call: AsyncFunction
38
-}
40
+} ) | Function
39
 
41
 
40
 export type RPC = CallRPC | UnhookRPC | HookRPC
42
 export type RPC = CallRPC | UnhookRPC | HookRPC
41
 
43
 

+ 49
- 29
src/Utils.ts View File

4
 import * as I from "./Interfaces";
4
 import * as I from "./Interfaces";
5
 
5
 
6
 export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => {
6
 export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => {
7
-    switch(rpc.type){
8
-        case "Call" :
9
-            return {
10
-                owner: owner,
11
-                argNames: extractArgs(rpc.call),
12
-                type: rpc.type,
13
-                name: rpc.name,
14
-                call: rpc.call,
15
-            }
16
-        case "Unhook" :
17
-            return {
18
-                owner: owner,
19
-                argNames: extractArgs(rpc.unhook),
20
-                type: rpc.type,
21
-                name: rpc.name,
22
-                unhook: rpc.unhook,
23
-            }
24
-        case "Hook" :
25
-            const generator = hookGenerator(rpc)
7
+    switch (typeof rpc){
8
+        case  "object":
9
+        switch(rpc.type){
10
+            case "Call" :
11
+                return {
12
+                    owner: owner,
13
+                    argNames: extractArgs(rpc.call),
14
+                    type: rpc.type,
15
+                    name: rpc.name,
16
+                    call: rpc.call,
17
+                }
18
+            case "Unhook" :
19
+                return {
20
+                    owner: owner,
21
+                    argNames: extractArgs(rpc.unhook),
22
+                    type: rpc.type,
23
+                    name: rpc.name,
24
+                    unhook: rpc.unhook,
25
+                }
26
+            case "Hook" :
27
+                const generator = hookGenerator(rpc)
28
+                return {
29
+                    owner: owner,
30
+                    argNames: extractArgs(generator(undefined)),
31
+                    type: rpc.type,
32
+                    name: rpc.name,
33
+                    unhook: rpc.unhook,
34
+                    generator: generator,
35
+                }
36
+        }
37
+        break;
38
+        case "function":
39
+            if(!rpc.name) throw new Error(`
40
+            RPC did not provide a name. 
41
+            \nUse funtion name(..){ .. } syntax instead.
42
+            \n
43
+            \n<------------OFFENDING RPC:
44
+            \n`+rpc.toString()+`
45
+            \n>------------OFFENDING RPC`)
26
             return {
46
             return {
27
-                owner: owner,
28
-                argNames: extractArgs(generator(undefined)),
29
-                type: rpc.type,
30
-                name: rpc.name,
31
-                unhook: rpc.unhook,
32
-                generator: generator,
47
+                type: "Call",
48
+                owner : owner,
49
+                argNames: extractArgs(rpc),
50
+                call: async(...args) => rpc.apply({}, args),
51
+                name: rpc.name
33
             }
52
             }
34
     }
53
     }
54
+    throw new Error("Bad socketIORPC type "+ typeof rpc)
35
 }
55
 }
56
+
36
 export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = true):T.ExtendedRpcInfo[]{
57
 export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = true):T.ExtendedRpcInfo[]{
37
     const owner = exporter.name
58
     const owner = exporter.name
38
-    const RPCs = [...exporter.publicRPCs(), ...exporter.localRPCs()]
59
+    const RPCs = [...exporter.exportRPCs()]
39
     const suffix = makeUnique?"-"+uuid().substr(0,4):""
60
     const suffix = makeUnique?"-"+uuid().substr(0,4):""
40
     return RPCs.map(rpc => rpcToRpcinfo(rpc, owner))
61
     return RPCs.map(rpc => rpcToRpcinfo(rpc, owner))
41
     .map(info => {
62
     .map(info => {
59
 }
80
 }
60
 
81
 
61
 const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { 
82
 const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { 
62
-    const argsArr = extractArgs(rpc.clbk)
83
+    const argsArr = extractArgs(rpc.hook)
63
     argsArr.pop()
84
     argsArr.pop()
64
     const args = argsArr.join(',')
85
     const args = argsArr.join(',')
65
 
86
 
66
     return eval(`(socket) => async (`+args+`) => { 
87
     return eval(`(socket) => async (`+args+`) => { 
67
-        const res = await rpc.clbk(`+args+(args.length!==0?',':'')+` (x) => {
88
+        const res = await rpc.hook(`+args+(args.length!==0?',':'')+` (x) => {
68
             socket.call(res.uid, x)
89
             socket.call(res.uid, x)
69
         })
90
         })
70
         if(res.result == 'Success'){
91
         if(res.result == 'Success'){
72
                 const unhookRes = await rpc.unhook(res.uid)
93
                 const unhookRes = await rpc.unhook(res.uid)
73
                 console.log("Specific close handler for", rpc.name, res.uid, unhookRes)
94
                 console.log("Specific close handler for", rpc.name, res.uid, unhookRes)
74
             })
95
             })
75
-
76
         }
96
         }
77
         return res
97
         return res
78
     }`)
98
     }`)

+ 1
- 1
src/webpack.js View File

5
   target: "web",
5
   target: "web",
6
   entry: path.resolve(__dirname, '..', 'js', 'src', 'Frontend.js'),
6
   entry: path.resolve(__dirname, '..', 'js', 'src', 'Frontend.js'),
7
   output: {
7
   output: {
8
-      path: path.resolve(__dirname, '..', 'js', 'src'),
8
+      path: path.resolve(__dirname, '..', 'js', 'rpclibrary.browser.js'),
9
       filename: "Frontend.js",
9
       filename: "Frontend.js",
10
       libraryTarget: 'commonjs',
10
       libraryTarget: 'commonjs',
11
   },
11
   },

+ 29
- 4
test/TestBackend.ts View File

1
 import { Server } from '../src/Backend'
1
 import { Server } from '../src/Backend'
2
+import { SubscriptionResponse } from '../src/Responses'
3
+
4
+let subcallback
2
 
5
 
3
 new Server(20000, [{
6
 new Server(20000, [{
4
     name: "HelloWorldRPCGroup",
7
     name: "HelloWorldRPCGroup",
5
-    publicRPCs: () => [],
6
-    localRPCs: () => [{
8
+    exportRPCs: () => [{
7
         type: 'Call',
9
         type: 'Call',
8
         name: 'echo',
10
         name: 'echo',
9
         call: async (s:string) => s,
11
         call: async (s:string) => s,
10
-    }]
11
-}])
12
+    },
13
+    {
14
+        type: 'Hook',
15
+        name: 'subscribe',
16
+        hook: async (callback):Promise<any> => {
17
+            subcallback = callback
18
+            return new SubscriptionResponse(""+Math.random())
19
+        },
20
+        unhook: async (uid):Promise<any> => { subcallback = null }
21
+    },
22
+    function add(...args:number[]):number {return args.reduce((a,b)=>a+b, 0)},
23
+    function triggerCallback(message):number {return subcallback(message)},
24
+]
25
+}])
26
+
27
+try{
28
+    new Server(20001, [{
29
+        name: "bad",
30
+        exportRPCs: ()  => [
31
+            (aaa,bbb,ccc) => { return aaa+bbb+ccc }
32
+        ]
33
+    }])
34
+}catch(badRPCError){
35
+    console.log("expected bad-RPC error happened: "+ !!badRPCError)
36
+}

+ 22
- 5
test/TestFrontend.ts View File

1
 import { Client } from '../src/Frontend'
1
 import { Client } from '../src/Frontend'
2
-
2
+console.log(Client)
3
 const client = new Client(20000, 'localhost')
3
 const client = new Client(20000, 'localhost')
4
-client.connect().then(_ => {
5
-    client.info().then(console.log)
6
-    client["HelloWorldRPCGroup"].echo("x").then(console.log)
7
-})
4
+client.connect().then(async _ => {
5
+    await client.info().then(console.log)
6
+    
7
+    await client["HelloWorldRPCGroup"].echo("x")
8
+    .then(hopefullyX => console.log("echo('x') returned x: ", hopefullyX === "x", hopefullyX))
9
+
10
+    await client["HelloWorldRPCGroup"].add(1,2,3)
11
+    .then(hopefully6 => console.log("add(1,2,3) returned 6: ", hopefully6 === 6, hopefully6))
12
+
13
+    let counter = 0
14
+    const handler = (s) => {
15
+        counter++
16
+        if(counter === 3)
17
+            console.log("callback was called 3 times", counter === 3)
18
+    }
19
+
20
+    await client["HelloWorldRPCGroup"].subscribe(handler)
21
+    client["HelloWorldRPCGroup"].triggerCallback("test1")
22
+    client["HelloWorldRPCGroup"].triggerCallback("test2")
23
+    client["HelloWorldRPCGroup"].triggerCallback("test3")
24
+})

+ 1
- 0
test/index.html View File

1
+<script src="../lib/RPCaller.min.js"></script>

Loading…
Cancel
Save