From b97d88b9e13b54621ea4411985db7fcb45b2bcb1 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 25 Feb 2020 15:20:45 +0100 Subject: [PATCH] fix attaching listeners before connecting --- package.json | 2 +- src/Backend.ts | 5 +---- src/Frontend.ts | 42 +++++++++++++++++++++++++++++++++++------- src/Interfaces.ts | 4 ++-- src/Types.ts | 7 ++++++- test/Test.ts | 31 +++++++++++++++++++++++++++++++ 6 files changed, 76 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 89b6562..ab7f732 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rpclibrary", - "version": "1.7.1", + "version": "1.8.0", "description": "rpclibrary is a websocket on steroids!", "main": "./js/Index.js", "repository": { diff --git a/src/Backend.ts b/src/Backend.ts index 00e80a1..2a889c6 100644 --- a/src/Backend.ts +++ b/src/Backend.ts @@ -53,13 +53,10 @@ export class RPCServer< if(conf.connectionHandler) conf.connectionHandler(socket) } - exporters.forEach(U.fixNames) + exporters.forEach(U.fixNames) //TSC for some reason doesn't preserve name properties of methods let badRPC = exporters.flatMap(ex => ex.exportRPCs()).find(rpc => !rpc.name) if(badRPC){ - console.log(badRPC); - - throw new Error(` RPC did not provide a name. \nUse 'funtion name(..){ .. }' syntax instead. diff --git a/src/Frontend.ts b/src/Frontend.ts index 4ec7cf7..06743e7 100644 --- a/src/Frontend.ts +++ b/src/Frontend.ts @@ -18,6 +18,9 @@ export class RPCSocket implements I.Socket{ } private socket: I.Socket + private closeHandlers: T.CloseHandler[] = [] + private errorHandlers: T.ErrorHandler[] = [] + private hooks : {[name in string]: T.AnyFunction} = {} /** * @@ -35,7 +38,11 @@ export class RPCSocket implements I.Socket{ * @param handler The handler to attach */ public hook(name: string, handler: (...args:any[]) => any | Promise){ - return this.socket.hook(name, handler) + if(!this.socket){ + this.hooks[name] = handler + }else{ + this.socket.hook(name, handler) + } } /** @@ -43,7 +50,11 @@ export class RPCSocket implements I.Socket{ * @param name The function name */ public unhook(name: string){ - return this.socket.unhook(name) + if(!this.socket){ + delete this.hooks[name] + }else{ + this.socket.unhook(name) + } } /** @@ -51,22 +62,32 @@ export class RPCSocket implements I.Socket{ * @param type 'error' or 'close' * @param f The listener to attach */ - public on(type: "error" | "close", f: (e?: any) => void){ - return this.socket.on(type, f) + public on(type: T, f: T.HandlerType[T]){ + if(!this.socket){ + switch(type){ + case "error": this.errorHandlers.push( f); break; + case "close": this.closeHandlers.push( f); break; + default: throw new Error('socket.on only supports ´error´ and ´close´ as first parameter. Got: ´'+type+'´') + } + }else{ + this.socket.on(type, f) + } } /** * Destroys the socket */ public destroy(){ - return this.socket.destroy() + if(!this.socket) return; + this.socket.destroy() } /** * Closes the socket. It may attempt to reconnect. */ public close(){ - return this.socket.close() + if(!this.socket) return; + this.socket.close() } /** @@ -75,6 +96,7 @@ export class RPCSocket implements I.Socket{ * @param args other arguments */ public async call (rpcname: string, ...args: any[]) : Promise{ + if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first") return await this.socket.call.apply(this.socket, [rpcname, ...args]) } @@ -84,6 +106,7 @@ export class RPCSocket implements I.Socket{ * @param args other arguments */ public async fire(rpcname: string, ...args: any[]) : Promise{ + if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first") await this.socket.fire.apply(this.socket, [rpcname, ...args]) } @@ -92,6 +115,11 @@ export class RPCSocket implements I.Socket{ */ public async connect( sesame?: string ) : Promise{ this.socket = await bsock.connect(this.port, this.server, this.conf.tls?this.conf.tls:false) + this.errorHandlers.forEach(h => this.socket.on('error', h)) + this.closeHandlers.forEach(h => this.socket.on('close', h)) + Object.entries(this.hooks).forEach((kv: [string, T.AnyFunction]) => { + this.socket.hook(kv[0], kv[1]) + }) const info:T.ExtendedRpcInfo[] = await this.info() info.forEach(i => { @@ -117,6 +145,7 @@ export class RPCSocket implements I.Socket{ * Get a list of available RPCs from the server */ public async info(){ + if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first") return await this.socket.call('info') } @@ -154,7 +183,6 @@ export class RPCSocket implements I.Socket{ const r = await this.socket.call("${fnName}", ${sesame} ${argParams}) if(r && r.result === 'Success'){ this.socket.hook(r.uuid, callback) - this.socket.on('error', e => this.socket.unhook(r.uuid)) } return r }`) diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 4648985..4a39bde 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -18,8 +18,8 @@ export interface RPCExporter< */ export interface Socket extends Destroyable { port: number - hook: (rpcname: string, handler: T.AnyFunction) => I.Socket - unhook: (rpcname:string) => I.Socket + hook: (rpcname: string, handler: T.AnyFunction) => void + unhook: (rpcname:string) => void call: (rpcname:string, ...args: any[]) => Promise fire: (rpcname:string, ...args: any[]) => Promise on: T.OnFunction diff --git a/src/Types.ts b/src/Types.ts index 52413e9..138e381 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -12,6 +12,11 @@ export type ExceptionHandling = 'local' | 'remote' export type SesameConf = { sesame?: string | SesameFunction } +export type HandlerType = { + 'error' : ErrorHandler + 'close' : CloseHandler +} + export type ServerConf = { connectionHandler?: ConnectionHandler errorHandler?: ErrorHandler @@ -79,5 +84,5 @@ export type CallInfo = BaseInfo & { export type RpcInfo = HookInfo | CallInfo export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } -export type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => I.Socket +export type OnFunction = (type: T, f: HandlerType[T]) => void export type HookCloseFunction = (res:SubscriptionResponse, rpc:HookRPC) => any diff --git a/test/Test.ts b/test/Test.ts index 80a44b2..be4c4e7 100644 --- a/test/Test.ts +++ b/test/Test.ts @@ -570,4 +570,35 @@ describe("Class binding", ()=>{ done() }) }) +}) + + +describe("attaching handlers before connecting", ()=>{ + + + it("fires error if server is unreachable", (done)=>{ + const sock = new RPCSocket(21004, 'localhost') + let errorHandleCount = 0 + + sock.on('error', (socket) => { + //attached listener fires first + if(errorHandleCount != 0){ + console.log("Error handler didn't fire first"); + }else{ + errorHandleCount++ + } + }) + + sock.connect().then(_ => { + console.log("Unexpected successful connect") + }).catch(e => { + //catch clause fires second + if(errorHandleCount != 1){ + console.log("catch clause didn't fire second"); + }else{ + sock.destroy() + done() + } + }) + }) }) \ No newline at end of file