Преглед на файлове

fix attaching listeners before connecting

master
peter преди 4 години
родител
ревизия
b97d88b9e1
променени са 6 файла, в които са добавени 76 реда и са изтрити 15 реда
  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 Целия файл

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

+ 1
- 4
src/Backend.ts Целия файл

53
             if(conf.connectionHandler) conf.connectionHandler(socket)
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
         let badRPC = exporters.flatMap(ex => ex.exportRPCs()).find(rpc => !rpc.name)
58
         let badRPC = exporters.flatMap(ex => ex.exportRPCs()).find(rpc => !rpc.name)
59
         if(badRPC){
59
         if(badRPC){
60
-            console.log(badRPC);
61
-            
62
-
63
             throw new Error(`
60
             throw new Error(`
64
             RPC did not provide a name. 
61
             RPC did not provide a name. 
65
             \nUse 'funtion name(..){ .. }' syntax instead.
62
             \nUse 'funtion name(..){ .. }' syntax instead.

+ 35
- 7
src/Frontend.ts Целия файл

18
     }
18
     }
19
 
19
 
20
     private socket: I.Socket
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
      * @param handler The handler to attach
38
      * @param handler The handler to attach
36
      */
39
      */
37
     public hook(name: string, handler: (...args:any[]) => any | Promise<any>){
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
      * @param name The function name 
50
      * @param name The function name 
44
      */
51
      */
45
     public unhook(name: string){
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
      * @param type 'error' or 'close'
62
      * @param type 'error' or 'close'
52
      * @param f The listener to attach
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
      * Destroys the socket
78
      * Destroys the socket
60
      */
79
      */
61
     public destroy(){
80
     public destroy(){
62
-        return this.socket.destroy()
81
+        if(!this.socket) return;
82
+        this.socket.destroy()
63
     }
83
     }
64
 
84
 
65
     /**
85
     /**
66
      * Closes the socket. It may attempt to reconnect.
86
      * Closes the socket. It may attempt to reconnect.
67
      */
87
      */
68
     public close(){
88
     public close(){
69
-        return this.socket.close()
89
+        if(!this.socket) return;
90
+        this.socket.close()
70
     }
91
     }
71
 
92
 
72
     /**
93
     /**
75
      * @param args other arguments
96
      * @param args other arguments
76
      */
97
      */
77
     public async call (rpcname: string, ...args: any[]) : Promise<any>{
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
         return await this.socket.call.apply(this.socket, [rpcname, ...args])
100
         return await this.socket.call.apply(this.socket, [rpcname, ...args])
79
     }
101
     }
80
 
102
 
84
      * @param args other arguments
106
      * @param args other arguments
85
      */
107
      */
86
     public async fire(rpcname: string, ...args: any[]) : Promise<void>{
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
         await this.socket.fire.apply(this.socket, [rpcname, ...args])
110
         await this.socket.fire.apply(this.socket, [rpcname, ...args])
88
     }
111
     }
89
     
112
     
92
      */
115
      */
93
     public async connect<T extends T.RPCInterface= T.RPCInterface>( sesame?: string ) : Promise<RPCSocket & T>{
116
     public async connect<T extends T.RPCInterface= T.RPCInterface>( sesame?: string ) : Promise<RPCSocket & T>{
94
         this.socket = await bsock.connect(this.port, this.server, this.conf.tls?this.conf.tls:false)
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
         const info:T.ExtendedRpcInfo[] = await this.info()
124
         const info:T.ExtendedRpcInfo[] = await this.info()
97
         info.forEach(i => {
125
         info.forEach(i => {
117
      * Get a list of available RPCs from the server
145
      * Get a list of available RPCs from the server
118
      */
146
      */
119
     public async info(){
147
     public async info(){
148
+        if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
120
         return await this.socket.call('info')
149
         return await this.socket.call('info')
121
     }
150
     }
122
 
151
 
154
             const r = await this.socket.call("${fnName}", ${sesame} ${argParams})
183
             const r = await this.socket.call("${fnName}", ${sesame} ${argParams})
155
             if(r && r.result === 'Success'){
184
             if(r && r.result === 'Success'){
156
                 this.socket.hook(r.uuid, callback)
185
                 this.socket.hook(r.uuid, callback)
157
-                this.socket.on('error', e => this.socket.unhook(r.uuid))
158
             }
186
             }
159
             return r
187
             return r
160
         }`)
188
         }`)

+ 2
- 2
src/Interfaces.ts Целия файл

18
  */
18
  */
19
 export interface Socket extends Destroyable {
19
 export interface Socket extends Destroyable {
20
     port: number
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
     call: (rpcname:string, ...args: any[]) => Promise<any>
23
     call: (rpcname:string, ...args: any[]) => Promise<any>
24
     fire: (rpcname:string, ...args: any[]) => Promise<any>
24
     fire: (rpcname:string, ...args: any[]) => Promise<any>
25
     on: T.OnFunction    
25
     on: T.OnFunction    

+ 6
- 1
src/Types.ts Целия файл

12
 export type SesameConf = {
12
 export type SesameConf = {
13
     sesame?: string | SesameFunction
13
     sesame?: string | SesameFunction
14
 }
14
 }
15
+export type HandlerType = {
16
+    'error' : ErrorHandler
17
+    'close' : CloseHandler
18
+}
19
+
15
 export type ServerConf = {
20
 export type ServerConf = {
16
     connectionHandler?: ConnectionHandler
21
     connectionHandler?: ConnectionHandler
17
     errorHandler?: ErrorHandler
22
     errorHandler?: ErrorHandler
79
 export type RpcInfo = HookInfo |  CallInfo
84
 export type RpcInfo = HookInfo |  CallInfo
80
 export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } 
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
 export type HookCloseFunction<T = {}> = (res:SubscriptionResponse<T>, rpc:HookRPC<any, any, T>) => any
88
 export type HookCloseFunction<T = {}> = (res:SubscriptionResponse<T>, rpc:HookRPC<any, any, T>) => any

+ 31
- 0
test/Test.ts Целия файл

570
             done()
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…
Отказ
Запис