peter 5 лет назад
Родитель
Сommit
2b765eb2a2
11 измененных файлов: 165 добавлений и 77 удалений
  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 Просмотреть файл

@@ -0,0 +1,7 @@
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 Просмотреть файл

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

+ 40
- 21
src/Backend.ts Просмотреть файл

@@ -18,17 +18,46 @@ export {
18 18
 export class Server{
19 19
     private ws = http.createServer()
20 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 27
     constructor(
23 28
         private port:number,
24 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 61
         this.startWebsocket()
33 62
     }
34 63
 
@@ -36,14 +65,12 @@ export class Server{
36 65
         try{
37 66
             this.io.attach(this.ws)
38 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 74
         }catch(e){
48 75
             //@ts-ignore
49 76
             this.errorHandler(undefined)("Unable to connect to socket")
@@ -57,12 +84,4 @@ export class Server{
57 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 Просмотреть файл

@@ -1,6 +1,6 @@
1 1
 'use strict'
2 2
 
3
-var bsock = require('bsock')
3
+import bsock = require('bsock');
4 4
 
5 5
 import * as T from './Types'; 
6 6
 import * as U from './Utils'; 
@@ -26,8 +26,6 @@ export class Client implements I.Socket{
26 26
     constructor(public port:number, private server: string, private tls: boolean = false){
27 27
     }
28 28
 
29
-
30
-
31 29
     public hook(name: T.Name, args: T.Arg){
32 30
         return this.socket.hook(name, args)
33 31
     }
@@ -96,9 +94,9 @@ export class Client implements I.Socket{
96 94
         return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => {
97 95
                             const r = await this.socket.call("`+fnName+`", `+argParams+`)
98 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,7 +109,7 @@ export class Client implements I.Socket{
111 109
         return eval( `( () => async (`+headerArgs+`) => {
112 110
                             const r = await this.socket.call("`+fnName+`", `+argParams+`)
113 111
                             this.socket.unhook(`+argParams+`)
114
-                            return res
112
+                            return r
115 113
                         } )()` )
116 114
     }
117 115
 

+ 1
- 2
src/Interfaces.ts Просмотреть файл

@@ -3,8 +3,7 @@ import * as I from "./Interfaces"
3 3
 
4 4
 export interface Exporter{
5 5
     name: T.Name
6
-    localRPCs() : T.RPC[]
7
-    publicRPCs() : T.RPC[]
6
+    exportRPCs() : T.RPC[]
8 7
 }
9 8
 
10 9
 export interface Socket {

+ 10
- 8
src/Types.ts Просмотреть файл

@@ -6,12 +6,14 @@ export type Any = any
6 6
 export type Arg = string
7 7
 export type Name = Arg
8 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 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 19
 export type RPCType = 'Hook' | 'Unhook' | 'Call'
@@ -23,7 +25,7 @@ export type BaseRPC = {
23 25
 
24 26
 export type HookRPC = BaseRPC & {
25 27
     type: 'Hook'
26
-    clbk: CallbackFunction
28
+    hook: CallbackFunction
27 29
     unhook: UnhookFunction
28 30
 }
29 31
 
@@ -32,10 +34,10 @@ export type UnhookRPC = BaseRPC & {
32 34
     unhook: UnhookFunction
33 35
 }
34 36
 
35
-export type CallRPC = BaseRPC & {
37
+export type CallRPC = (BaseRPC & {
36 38
     type: 'Call'
37 39
     call: AsyncFunction
38
-}
40
+} ) | Function
39 41
 
40 42
 export type RPC = CallRPC | UnhookRPC | HookRPC
41 43
 

+ 49
- 29
src/Utils.ts Просмотреть файл

@@ -4,38 +4,59 @@ import * as T from "./Types";
4 4
 import * as I from "./Interfaces";
5 5
 
6 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 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 57
 export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = true):T.ExtendedRpcInfo[]{
37 58
     const owner = exporter.name
38
-    const RPCs = [...exporter.publicRPCs(), ...exporter.localRPCs()]
59
+    const RPCs = [...exporter.exportRPCs()]
39 60
     const suffix = makeUnique?"-"+uuid().substr(0,4):""
40 61
     return RPCs.map(rpc => rpcToRpcinfo(rpc, owner))
41 62
     .map(info => {
@@ -59,12 +80,12 @@ export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = tr
59 80
 }
60 81
 
61 82
 const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { 
62
-    const argsArr = extractArgs(rpc.clbk)
83
+    const argsArr = extractArgs(rpc.hook)
63 84
     argsArr.pop()
64 85
     const args = argsArr.join(',')
65 86
 
66 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 89
             socket.call(res.uid, x)
69 90
         })
70 91
         if(res.result == 'Success'){
@@ -72,7 +93,6 @@ const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => {
72 93
                 const unhookRes = await rpc.unhook(res.uid)
73 94
                 console.log("Specific close handler for", rpc.name, res.uid, unhookRes)
74 95
             })
75
-
76 96
         }
77 97
         return res
78 98
     }`)

+ 1
- 1
src/webpack.js Просмотреть файл

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

+ 29
- 4
test/TestBackend.ts Просмотреть файл

@@ -1,11 +1,36 @@
1 1
 import { Server } from '../src/Backend'
2
+import { SubscriptionResponse } from '../src/Responses'
3
+
4
+let subcallback
2 5
 
3 6
 new Server(20000, [{
4 7
     name: "HelloWorldRPCGroup",
5
-    publicRPCs: () => [],
6
-    localRPCs: () => [{
8
+    exportRPCs: () => [{
7 9
         type: 'Call',
8 10
         name: 'echo',
9 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 Просмотреть файл

@@ -1,7 +1,24 @@
1 1
 import { Client } from '../src/Frontend'
2
-
2
+console.log(Client)
3 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 Просмотреть файл

@@ -0,0 +1 @@
1
+<script src="../lib/RPCaller.min.js"></script>

Загрузка…
Отмена
Сохранить