Browse Source

fix attaching listeners before connecting

master
peter 4 years ago
parent
commit
b97d88b9e1
6 changed files with 76 additions and 15 deletions
  1. 1
    1
      package.json
  2. 1
    4
      src/Backend.ts
  3. 35
    7
      src/Frontend.ts
  4. 2
    2
      src/Interfaces.ts
  5. 6
    1
      src/Types.ts
  6. 31
    0
      test/Test.ts

+ 1
- 1
package.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "rpclibrary",
3
-  "version": "1.7.1",
3
+  "version": "1.8.0",
4 4
   "description": "rpclibrary is a websocket on steroids!",
5 5
   "main": "./js/Index.js",
6 6
   "repository": {

+ 1
- 4
src/Backend.ts View File

@@ -53,13 +53,10 @@ export class RPCServer<
53 53
             if(conf.connectionHandler) conf.connectionHandler(socket)
54 54
         }
55 55
 
56
-        exporters.forEach(U.fixNames)
56
+        exporters.forEach(U.fixNames) //TSC for some reason doesn't preserve name properties of methods
57 57
 
58 58
         let badRPC = exporters.flatMap(ex => ex.exportRPCs()).find(rpc => !rpc.name)
59 59
         if(badRPC){
60
-            console.log(badRPC);
61
-            
62
-
63 60
             throw new Error(`
64 61
             RPC did not provide a name. 
65 62
             \nUse 'funtion name(..){ .. }' syntax instead.

+ 35
- 7
src/Frontend.ts View File

@@ -18,6 +18,9 @@ export class RPCSocket implements I.Socket{
18 18
     }
19 19
 
20 20
     private socket: I.Socket
21
+    private closeHandlers: T.CloseHandler[] = []
22
+    private errorHandlers: T.ErrorHandler[] = []
23
+    private hooks : {[name in string]: T.AnyFunction} = {} 
21 24
 
22 25
     /**
23 26
      * 
@@ -35,7 +38,11 @@ export class RPCSocket implements I.Socket{
35 38
      * @param handler The handler to attach
36 39
      */
37 40
     public hook(name: string, handler: (...args:any[]) => any | Promise<any>){
38
-        return this.socket.hook(name, handler)
41
+        if(!this.socket){
42
+            this.hooks[name] = handler
43
+        }else{
44
+            this.socket.hook(name, handler)
45
+        }
39 46
     }
40 47
 
41 48
     /**
@@ -43,7 +50,11 @@ export class RPCSocket implements I.Socket{
43 50
      * @param name The function name 
44 51
      */
45 52
     public unhook(name: string){
46
-        return this.socket.unhook(name)
53
+        if(!this.socket){
54
+            delete this.hooks[name]
55
+        }else{
56
+            this.socket.unhook(name)
57
+        }
47 58
     }
48 59
 
49 60
     /**
@@ -51,22 +62,32 @@ export class RPCSocket implements I.Socket{
51 62
      * @param type 'error' or 'close'
52 63
      * @param f The listener to attach
53 64
      */
54
-    public on(type: "error" | "close", f: (e?: any) => void){
55
-        return this.socket.on(type, f)
65
+    public on<T extends "error" | "close">(type: T, f: T.HandlerType[T]){
66
+        if(!this.socket){
67
+            switch(type){
68
+                case "error": this.errorHandlers.push(<T.HandlerType['error']> f); break;
69
+                case "close": this.closeHandlers.push(<T.HandlerType['close']> f); break;
70
+                default: throw new Error('socket.on only supports ´error´ and ´close´ as first parameter. Got: ´'+type+'´')
71
+            }
72
+        }else{
73
+            this.socket.on(type, f)
74
+        }
56 75
     }
57 76
 
58 77
     /**
59 78
      * Destroys the socket
60 79
      */
61 80
     public destroy(){
62
-        return this.socket.destroy()
81
+        if(!this.socket) return;
82
+        this.socket.destroy()
63 83
     }
64 84
 
65 85
     /**
66 86
      * Closes the socket. It may attempt to reconnect.
67 87
      */
68 88
     public close(){
69
-        return this.socket.close()
89
+        if(!this.socket) return;
90
+        this.socket.close()
70 91
     }
71 92
 
72 93
     /**
@@ -75,6 +96,7 @@ export class RPCSocket implements I.Socket{
75 96
      * @param args other arguments
76 97
      */
77 98
     public async call (rpcname: string, ...args: any[]) : Promise<any>{
99
+        if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
78 100
         return await this.socket.call.apply(this.socket, [rpcname, ...args])
79 101
     }
80 102
 
@@ -84,6 +106,7 @@ export class RPCSocket implements I.Socket{
84 106
      * @param args other arguments
85 107
      */
86 108
     public async fire(rpcname: string, ...args: any[]) : Promise<void>{
109
+        if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
87 110
         await this.socket.fire.apply(this.socket, [rpcname, ...args])
88 111
     }
89 112
     
@@ -92,6 +115,11 @@ export class RPCSocket implements I.Socket{
92 115
      */
93 116
     public async connect<T extends T.RPCInterface= T.RPCInterface>( sesame?: string ) : Promise<RPCSocket & T>{
94 117
         this.socket = await bsock.connect(this.port, this.server, this.conf.tls?this.conf.tls:false)
118
+        this.errorHandlers.forEach(h => this.socket.on('error', h))
119
+        this.closeHandlers.forEach(h => this.socket.on('close', h))
120
+        Object.entries(this.hooks).forEach((kv: [string, T.AnyFunction]) => {
121
+            this.socket.hook(kv[0], kv[1])
122
+        })
95 123
 
96 124
         const info:T.ExtendedRpcInfo[] = await this.info()
97 125
         info.forEach(i => {
@@ -117,6 +145,7 @@ export class RPCSocket implements I.Socket{
117 145
      * Get a list of available RPCs from the server
118 146
      */
119 147
     public async info(){
148
+        if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
120 149
         return await this.socket.call('info')
121 150
     }
122 151
 
@@ -154,7 +183,6 @@ export class RPCSocket implements I.Socket{
154 183
             const r = await this.socket.call("${fnName}", ${sesame} ${argParams})
155 184
             if(r && r.result === 'Success'){
156 185
                 this.socket.hook(r.uuid, callback)
157
-                this.socket.on('error', e => this.socket.unhook(r.uuid))
158 186
             }
159 187
             return r
160 188
         }`)

+ 2
- 2
src/Interfaces.ts View File

@@ -18,8 +18,8 @@ export interface RPCExporter<
18 18
  */
19 19
 export interface Socket extends Destroyable {
20 20
     port: number
21
-    hook: (rpcname: string, handler: T.AnyFunction) => I.Socket
22
-    unhook: (rpcname:string) => I.Socket
21
+    hook: (rpcname: string, handler: T.AnyFunction) => void
22
+    unhook: (rpcname:string) => void
23 23
     call: (rpcname:string, ...args: any[]) => Promise<any>
24 24
     fire: (rpcname:string, ...args: any[]) => Promise<any>
25 25
     on: T.OnFunction    

+ 6
- 1
src/Types.ts View File

@@ -12,6 +12,11 @@ export type ExceptionHandling = 'local' | 'remote'
12 12
 export type SesameConf = {
13 13
     sesame?: string | SesameFunction
14 14
 }
15
+export type HandlerType = {
16
+    'error' : ErrorHandler
17
+    'close' : CloseHandler
18
+}
19
+
15 20
 export type ServerConf = {
16 21
     connectionHandler?: ConnectionHandler
17 22
     errorHandler?: ErrorHandler
@@ -79,5 +84,5 @@ export type CallInfo = BaseInfo & {
79 84
 export type RpcInfo = HookInfo |  CallInfo
80 85
 export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } 
81 86
 
82
-export type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => I.Socket
87
+export type OnFunction = <T extends "error" | "close">(type: T, f: HandlerType[T]) => void
83 88
 export type HookCloseFunction<T = {}> = (res:SubscriptionResponse<T>, rpc:HookRPC<any, any, T>) => any

+ 31
- 0
test/Test.ts View File

@@ -570,4 +570,35 @@ describe("Class binding", ()=>{
570 570
             done()
571 571
         })
572 572
     })
573
+})
574
+
575
+
576
+describe("attaching handlers before connecting", ()=>{
577
+
578
+
579
+    it("fires error if server is unreachable", (done)=>{
580
+        const sock = new RPCSocket(21004, 'localhost')
581
+        let errorHandleCount = 0
582
+
583
+        sock.on('error', (socket) => {
584
+            //attached listener fires first
585
+            if(errorHandleCount != 0){
586
+                console.log("Error handler didn't fire first");
587
+            }else{
588
+                errorHandleCount++
589
+            }
590
+        })
591
+
592
+        sock.connect().then(_ => {
593
+            console.log("Unexpected successful connect")
594
+        }).catch(e => {
595
+            //catch clause fires second
596
+            if(errorHandleCount != 1){
597
+                console.log("catch clause didn't fire second");
598
+            }else{
599
+                sock.destroy()
600
+                done()
601
+            }
602
+        })
603
+    })
573 604
 })

Loading…
Cancel
Save