fix attaching listeners before connecting
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rpclibrary",
|
"name": "rpclibrary",
|
||||||
"version": "1.7.1",
|
"version": "1.8.0",
|
||||||
"description": "rpclibrary is a websocket on steroids!",
|
"description": "rpclibrary is a websocket on steroids!",
|
||||||
"main": "./js/Index.js",
|
"main": "./js/Index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
+1
-4
@@ -53,13 +53,10 @@ export class RPCServer<
|
|||||||
if(conf.connectionHandler) conf.connectionHandler(socket)
|
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)
|
let badRPC = exporters.flatMap(ex => ex.exportRPCs()).find(rpc => !rpc.name)
|
||||||
if(badRPC){
|
if(badRPC){
|
||||||
console.log(badRPC);
|
|
||||||
|
|
||||||
|
|
||||||
throw new Error(`
|
throw new Error(`
|
||||||
RPC did not provide a name.
|
RPC did not provide a name.
|
||||||
\nUse 'funtion name(..){ .. }' syntax instead.
|
\nUse 'funtion name(..){ .. }' syntax instead.
|
||||||
|
|||||||
+35
-7
@@ -18,6 +18,9 @@ export class RPCSocket implements I.Socket{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private socket: 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
|
* @param handler The handler to attach
|
||||||
*/
|
*/
|
||||||
public hook(name: string, handler: (...args:any[]) => any | Promise<any>){
|
public hook(name: string, handler: (...args:any[]) => any | Promise<any>){
|
||||||
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
|
* @param name The function name
|
||||||
*/
|
*/
|
||||||
public unhook(name: string){
|
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 type 'error' or 'close'
|
||||||
* @param f The listener to attach
|
* @param f The listener to attach
|
||||||
*/
|
*/
|
||||||
public on(type: "error" | "close", f: (e?: any) => void){
|
public on<T extends "error" | "close">(type: T, f: T.HandlerType[T]){
|
||||||
return this.socket.on(type, f)
|
if(!this.socket){
|
||||||
|
switch(type){
|
||||||
|
case "error": this.errorHandlers.push(<T.HandlerType['error']> f); break;
|
||||||
|
case "close": this.closeHandlers.push(<T.HandlerType['close']> 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
|
* Destroys the socket
|
||||||
*/
|
*/
|
||||||
public destroy(){
|
public destroy(){
|
||||||
return this.socket.destroy()
|
if(!this.socket) return;
|
||||||
|
this.socket.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the socket. It may attempt to reconnect.
|
* Closes the socket. It may attempt to reconnect.
|
||||||
*/
|
*/
|
||||||
public close(){
|
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
|
* @param args other arguments
|
||||||
*/
|
*/
|
||||||
public async call (rpcname: string, ...args: any[]) : Promise<any>{
|
public async call (rpcname: string, ...args: any[]) : Promise<any>{
|
||||||
|
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])
|
return await this.socket.call.apply(this.socket, [rpcname, ...args])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,6 +106,7 @@ export class RPCSocket implements I.Socket{
|
|||||||
* @param args other arguments
|
* @param args other arguments
|
||||||
*/
|
*/
|
||||||
public async fire(rpcname: string, ...args: any[]) : Promise<void>{
|
public async fire(rpcname: string, ...args: any[]) : Promise<void>{
|
||||||
|
if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
|
||||||
await this.socket.fire.apply(this.socket, [rpcname, ...args])
|
await this.socket.fire.apply(this.socket, [rpcname, ...args])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,6 +115,11 @@ export class RPCSocket implements I.Socket{
|
|||||||
*/
|
*/
|
||||||
public async connect<T extends T.RPCInterface= T.RPCInterface>( sesame?: string ) : Promise<RPCSocket & T>{
|
public async connect<T extends T.RPCInterface= T.RPCInterface>( sesame?: string ) : Promise<RPCSocket & T>{
|
||||||
this.socket = await bsock.connect(this.port, this.server, this.conf.tls?this.conf.tls:false)
|
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()
|
const info:T.ExtendedRpcInfo[] = await this.info()
|
||||||
info.forEach(i => {
|
info.forEach(i => {
|
||||||
@@ -117,6 +145,7 @@ export class RPCSocket implements I.Socket{
|
|||||||
* Get a list of available RPCs from the server
|
* Get a list of available RPCs from the server
|
||||||
*/
|
*/
|
||||||
public async info(){
|
public async info(){
|
||||||
|
if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
|
||||||
return await this.socket.call('info')
|
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})
|
const r = await this.socket.call("${fnName}", ${sesame} ${argParams})
|
||||||
if(r && r.result === 'Success'){
|
if(r && r.result === 'Success'){
|
||||||
this.socket.hook(r.uuid, callback)
|
this.socket.hook(r.uuid, callback)
|
||||||
this.socket.on('error', e => this.socket.unhook(r.uuid))
|
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}`)
|
}`)
|
||||||
|
|||||||
+2
-2
@@ -18,8 +18,8 @@ export interface RPCExporter<
|
|||||||
*/
|
*/
|
||||||
export interface Socket extends Destroyable {
|
export interface Socket extends Destroyable {
|
||||||
port: number
|
port: number
|
||||||
hook: (rpcname: string, handler: T.AnyFunction) => I.Socket
|
hook: (rpcname: string, handler: T.AnyFunction) => void
|
||||||
unhook: (rpcname:string) => I.Socket
|
unhook: (rpcname:string) => void
|
||||||
call: (rpcname:string, ...args: any[]) => Promise<any>
|
call: (rpcname:string, ...args: any[]) => Promise<any>
|
||||||
fire: (rpcname:string, ...args: any[]) => Promise<any>
|
fire: (rpcname:string, ...args: any[]) => Promise<any>
|
||||||
on: T.OnFunction
|
on: T.OnFunction
|
||||||
|
|||||||
+6
-1
@@ -12,6 +12,11 @@ export type ExceptionHandling = 'local' | 'remote'
|
|||||||
export type SesameConf = {
|
export type SesameConf = {
|
||||||
sesame?: string | SesameFunction
|
sesame?: string | SesameFunction
|
||||||
}
|
}
|
||||||
|
export type HandlerType = {
|
||||||
|
'error' : ErrorHandler
|
||||||
|
'close' : CloseHandler
|
||||||
|
}
|
||||||
|
|
||||||
export type ServerConf = {
|
export type ServerConf = {
|
||||||
connectionHandler?: ConnectionHandler
|
connectionHandler?: ConnectionHandler
|
||||||
errorHandler?: ErrorHandler
|
errorHandler?: ErrorHandler
|
||||||
@@ -79,5 +84,5 @@ export type CallInfo = BaseInfo & {
|
|||||||
export type RpcInfo = HookInfo | CallInfo
|
export type RpcInfo = HookInfo | CallInfo
|
||||||
export type ExtendedRpcInfo = RpcInfo & { uniqueName: string }
|
export type ExtendedRpcInfo = RpcInfo & { uniqueName: string }
|
||||||
|
|
||||||
export type OnFunction = (type: 'error' | 'close', f: (e?:any)=>void) => I.Socket
|
export type OnFunction = <T extends "error" | "close">(type: T, f: HandlerType[T]) => void
|
||||||
export type HookCloseFunction<T = {}> = (res:SubscriptionResponse<T>, rpc:HookRPC<any, any, T>) => any
|
export type HookCloseFunction<T = {}> = (res:SubscriptionResponse<T>, rpc:HookRPC<any, any, T>) => any
|
||||||
|
|||||||
@@ -571,3 +571,34 @@ describe("Class binding", ()=>{
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user