From 8352ac1bf7ff68b3a855b7185a319781e419fec2 Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 29 Nov 2020 05:50:53 +0100 Subject: [PATCH] fix listener bugs --- package-lock.json | 2 +- src/Backend.ts | 22 ++-- src/Frontend.ts | 22 ++-- src/Interfaces.ts | 2 +- src/PromiseIO/Client.ts | 11 +- src/PromiseIO/Server.ts | 21 ++-- src/Types.ts | 8 +- src/Utils.ts | 111 ++++++++++---------- test/Test.ts | 223 ++++++++++++++++++++++++++++++---------- 9 files changed, 278 insertions(+), 144 deletions(-) diff --git a/package-lock.json b/package-lock.json index fe8464f..1b02f90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "rpclibrary", - "version": "2.0.0", + "version": "2.2.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/Backend.ts b/src/Backend.ts index f901837..4705102 100644 --- a/src/Backend.ts +++ b/src/Backend.ts @@ -10,7 +10,7 @@ export class RPCServer< InterfaceT extends T.RPCInterface = T.RPCInterface, > { - private pio = PromiseIO.createServer() + private pio = new PromiseIO() private closeHandler: T.CloseHandler private errorHandler: T.ErrorHandler private connectionHandler: T.ConnectionHandler @@ -37,7 +37,6 @@ export class RPCServer< return this.sesame!(sesame!) }) - this.errorHandler = (socket: I.Socket | PromiseIO) => (error: any, rpcName: string, args: any[]) => { if (conf.errorHandler) conf.errorHandler(socket, error, rpcName, args) else throw error @@ -65,11 +64,13 @@ export class RPCServer< } try { - this.pio.on('socket', (socket: I.Socket) => { - socket.on('error', (err) => this.errorHandler(socket, err, "system", [])) - socket.on('close', () => this.closeHandler(socket)) - this.connectionHandler(socket) - this.initRPCs(socket) + + this.pio.on('socket', (clientSocket: I.Socket) => { + const sock:any = clientSocket; + clientSocket.on('disconnect', () => this.closeHandler(clientSocket)) + this.connectionHandler(clientSocket) + this.initRPCs(clientSocket) + }) } catch (e) { this.errorHandler(this.pio, e, 'system', []) @@ -88,12 +89,13 @@ export class RPCServer< return this } - protected initRPCs(socket: I.Socket) { - socket.hook('info', async (sesame?: string) => { + protected initRPCs(clientSocket: I.Socket) { + clientSocket.hook('info', async (sesame?: string) => { const rpcs = await Promise.all(this.exporters.map(async exp => { const allowed = await this.accessFilter(sesame, exp) if (!allowed) return [] - return U.rpcHooker(socket, exp, this.errorHandler, this.sesame) + const infos = U.rpcHooker(clientSocket, exp, this.errorHandler, this.sesame) + return infos })) return rpcs.flat() }) diff --git a/src/Frontend.ts b/src/Frontend.ts index 0a529cc..c3a9475 100644 --- a/src/Frontend.ts +++ b/src/Frontend.ts @@ -16,7 +16,7 @@ export class RPCSocket implements I return await socket.connect(sesame) } - private protocol: 'http:' | 'https:' + private protocol: 'http' | 'https' private socket: I.Socket private handlers : { [name in string]: T.AnyFunction[] @@ -32,9 +32,9 @@ export class RPCSocket implements I * @param server Server address * @param tls @default false use TLS */ - constructor(public port:number, private server: string, conf:T.SocketConf = { tls: false }){ + constructor(public port:number, public address: string, conf:T.SocketConf = { tls: false }){ Object.defineProperty(this, 'socket', {value: undefined, writable: true}) - this.protocol = conf.tls ? "https:" : "http:" + this.protocol = conf.tls ? "https" : "http" } /** @@ -141,7 +141,7 @@ export class RPCSocket implements I public async connect( sesame?: string ) : Promise> { try{ - this.socket = await PromiseIOClient.connect(this.port, this.server, this.protocol) + this.socket = await PromiseIOClient.connect(this.port, this.address, this.protocol) }catch(e){ this.handlers['error'].forEach(h => h(e)) throw e @@ -215,14 +215,17 @@ export class RPCSocket implements I sesame = appendComma(sesame, true) headerArgs = fnArgs.length>0?headerArgs+",":headerArgs - return eval( ` - async (${headerArgs} callback) => { + const frontendHookStr = ` + async (${headerArgs} $__callback__$) => { const r = await this.call("${fnName}", ${sesame} ${argParams}) try{ if(r){ if(r.uuid){ - callback['destroy'] = () => { this.socket.unhook(r.uuid) } - this.socket.hook(r.uuid, callback) + $__callback__$['destroy'] = () => { + this.socket.fire(r.uuid) + this.socket.unhook(r.uuid) + } + this.socket.hook(r.uuid, $__callback__$) } return r.return }else{ @@ -231,6 +234,7 @@ export class RPCSocket implements I }catch(e){ throw e } - }`) + }` + return eval(frontendHookStr) } } diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 973ceac..f34eae2 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -20,6 +20,6 @@ export interface Socket { call: (rpcname: string, ...args: any[]) => Promise fire: (rpcname: string, ...args: any[]) => Promise on: (type: string, f: T.AnyFunction)=>any - emit: (eventName: string, data: any) => void + emit: (eventName: string, ...args: any[]) => void close(): void } \ No newline at end of file diff --git a/src/PromiseIO/Client.ts b/src/PromiseIO/Client.ts index d884e44..9b40e8a 100644 --- a/src/PromiseIO/Client.ts +++ b/src/PromiseIO/Client.ts @@ -5,21 +5,26 @@ import * as socketio from 'socket.io-client' export class PromiseIOClient { - static connect = (port: number, host = "localhost", protocol : 'http:' | 'https:' = "http:"): Promise => new Promise((res, rej) => { + static connect = (port: number, host = "localhost", protocol : 'http' | 'https' = "http"): Promise => new Promise((res, rej) => { try { - const socket = socketio(`${protocol}//${host}:${port}`, { + const address = `${host}:${port}` + const socket = socketio(`${protocol}://${address}`, { reconnectionAttempts: 2, reconnectionDelay: 200, timeout: 450, reconnection: false, }) + socket.on('connect_error', e => { sock.emit('error', e) rej(e) }) + socket['address'] = address const sock = U.makePioSocket(socket) - socket.on('connect', ()=>{ res(sock) }) + socket.on('connect', ()=>{ + res(sock) + }) /* diff --git a/src/PromiseIO/Server.ts b/src/PromiseIO/Server.ts index 44d666b..a4e158e 100644 --- a/src/PromiseIO/Server.ts +++ b/src/PromiseIO/Server.ts @@ -4,6 +4,7 @@ import * as U from '../Utils' import * as T from '../Types' import socketio = require('socket.io') + export class PromiseIO { io?: Server httpServer: httpServer @@ -12,15 +13,23 @@ export class PromiseIO { connect: [] } - static createServer(): PromiseIO { - return new PromiseIO(); - } - attach(httpServer: httpServer) { this.httpServer = httpServer this.io = socketio(httpServer, { cookie:false }) - this.io!.on('connection', (sock: Socket) => { - const pioSock = U.makePioSocket(sock) + + this.io!.on('connection', (clientSocket: Socket) => { + console.log(this.listeners); + + clientSocket.use((packet, next) => { + console.log(this.listeners[packet[0]]); + + console.log(packet[0]); + next() + }) + + + clientSocket['address'] = clientSocket.handshake.headers["x-real-ip"] || clientSocket.handshake.address + const pioSock = U.makePioSocket(clientSocket) this.listeners['socket'].forEach(listener => listener(pioSock)) this.listeners['connect'].forEach(listener => listener(pioSock)) /* diff --git a/src/Types.ts b/src/Types.ts index 2fa590f..556c473 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -5,8 +5,6 @@ import { PromiseIO } from "./PromiseIO/Server"; export type PioBindListener = (...args: any) => void export type PioHookListener = AnyFunction - - export type AnyFunction = (...args:any) => any export type HookFunction = AnyFunction export type AccessFilter = (sesame:string|undefined, exporter: I.RPCExporter) => Promise | boolean @@ -57,7 +55,7 @@ export type HookRPC = { name: Name hook: Func onCallback?: AnyFunction - onClose?: HookCloseFunction extends Promise ? T : ReturnType> + onDestroy?: HookCloseFunction extends Promise ? T : ReturnType> } export type RPC = HookRPC | CallRPC | Func @@ -101,4 +99,6 @@ export type HookCloseFunction = (res: T, rpc:HookRPC) => any export type AsyncIfc = { [grp in keyof Ifc]: {[rpcname in keyof Ifc[grp]] : AsyncAnyFunction } } -export type AsyncAnyFunction = F extends (...args: Parameters) => infer R ? ((...args: Parameters) => R extends Promise ? R : Promise ) : Promise \ No newline at end of file +export type AsyncAnyFunction = F extends (...args: Parameters) => infer R + ? ((...args: Parameters) => R extends Promise ? R : Promise ) + : Promise \ No newline at end of file diff --git a/src/Utils.ts b/src/Utils.ts index 114356a..d5122ba 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -2,8 +2,7 @@ import * as uuidv4 from "uuid/v4" import * as T from "./Types"; import * as I from "./Interfaces"; -import { Server as ioServer, Socket as ioSocket, Socket } from "socket.io" -import { Socket as ioClientSocket } from "socket.io-client" +import { Socket } from "socket.io" /** * Translate an RPC to RPCInfo for serialization. @@ -17,20 +16,22 @@ export const rpcToRpcinfo = (socket: I.Socket, rpc: T.RPC, owner: stri switch (typeof rpc) { case "object": if (rpc['call']) { + const _rpc: T.CallRPC = rpc return { owner: owner, argNames: extractArgs(rpc['call']), type: "Call", name: rpc.name, - call: sesame ? async (_sesame, ...args) => { if (sesame(_sesame)) return await rpc['call'].apply({}, args); socket.close() } : rpc['call'], // check & remove sesame + call: sesame ? async ($__sesame__$, ...args) => { if (sesame($__sesame__$)) return await rpc['call'].apply({}, args); socket.close() } : rpc['call'], // check & remove sesame } } else { - const generator = hookGenerator(>rpc, errorHandler, sesame) + const _rpc: T.HookRPC = rpc + const generator = hookGenerator(_rpc, errorHandler, sesame) return { owner: owner, argNames: extractArgs(generator(undefined)), type: "Hook", - name: rpc.name, + name: _rpc.name, generator: generator, } } @@ -47,7 +48,7 @@ RPC did not provide a name. argNames: extractArgs(rpc), type: "Call", name: rpc.name, - call: sesame ? async (_sesame, ...args) => { if (sesame(_sesame)) return await rpc.apply({}, args); throw makeError(rpc.name) } : rpc, // check & remove sesame + call: sesame ? async ($__sesame__$, ...args) => { if (sesame($__sesame__$)) return await rpc.apply({}, args); throw makeError(rpc.name) } : rpc, // check & remove sesame } } throw new Error("Bad socketIORPC type " + typeof rpc) @@ -55,23 +56,22 @@ RPC did not provide a name. /** * Utility function to apply the RPCs of an {@link RPCExporter}. - * @param socket The websocket (implementation: bsock) to hook on + * @param serverSocket The websocket (implementation: socket.io) to hook on * @param exporter The exporter * @param makeUnique @default true Attach a suffix to RPC names */ -export function rpcHooker(socket: I.Socket, exporter: I.RPCExporter, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction, makeUnique = true): T.ExtendedRpcInfo[] { +export function rpcHooker(serverSocket: I.Socket, exporter: I.RPCExporter, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction, makeUnique = true): T.ExtendedRpcInfo[] { const owner = exporter.name const RPCs = typeof exporter.RPCs === "function" ? exporter.RPCs() : exporter.RPCs - return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame)) + return RPCs + .map(rpc => rpcToRpcinfo(serverSocket, rpc, owner, errorHandler, sesame)) .map(info => { const suffix = makeUnique ? "-" + uuidv4().substr(0, 4) : "" const ret: any = info ret.uniqueName = info.name + suffix - let rpcFunction = info.type === 'Hook' ? info.generator(socket) - : info.call - - socket.hook(ret.uniqueName, callGenerator(info.name, socket, rpcFunction, errorHandler)) + let rpcFunction = info.type === 'Hook' ? info.generator(serverSocket) : info.call + serverSocket.hook(ret.uniqueName, callGenerator(info.name, serverSocket, rpcFunction, errorHandler)) return ret }) } @@ -80,18 +80,20 @@ export function rpcHooker(socket: I.Socket, exporter: I.RPCExporter, e * Decorate an RPC with the error handler * @param rpcFunction the function to decorate */ -const callGenerator = (rpcName: string, socket: I.Socket, rpcFunction: T.AnyFunction, errorHandler: T.ErrorHandler): T.AnyFunction => { +const callGenerator = (rpcName: string, $__socket__$: I.Socket, rpcFunction: T.AnyFunction, errorHandler: T.ErrorHandler): T.AnyFunction => { const argsArr = extractArgs(rpcFunction) const args = argsArr.join(',') const argsStr = argsArr.map(stripAfterEquals).join(',') - return eval(`async (` + args + `) => { + const callStr = `async (${args}) => { try{ - return await rpcFunction(`+ argsStr + `) + return await rpcFunction(${argsStr}) }catch(e){ - errorHandler(socket)(e, rpcName, [`+ args + `]) + errorHandler($__socket__$)(e, rpcName, [${args}]) } - }`) + }` + + return eval(callStr); } /** @@ -107,43 +109,39 @@ export function stripAfterEquals(str: string): string { * @param rpc The RPC to transform * @returns A {@link HookFunction} */ -const hookGenerator = (rpc: T.HookRPC, /*not unused!*/ errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => { - +const hookGenerator = (rpc: T.HookRPC, errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction, injectSocket?: boolean): T.HookInfo['generator'] => { let argsArr = extractArgs(rpc.hook) - argsArr.pop() //remove 'callback' from the end - let callArgs = argsArr.join(',') + argsArr.pop() //remove callback param + let callArgs = argsArr.join(',') const args = sesameFn ? (['sesame', ...argsArr].join(',')) - : callArgs + : callArgs callArgs = appendComma(callArgs, false) - //note rpc.hook is the associated RPC, not a socket.hook - return eval(` - (clientSocket) => async (${args}) => { + const hookStr = ` + ($__socket__$) => async (${args}) => { try{ if(sesameFn && !sesameFn(sesame)) return const uuid = uuidv4() const res = await rpc.hook(${callArgs} (...cbargs) => { - if(rpc.onCallback){ - rpc.onCallback.apply({}, cbargs) - } - clientSocket.call.apply(clientSocket, [uuid, ...cbargs]) + ${rpc.onCallback ? `rpc.onCallback.apply({}, cbargs)` : ``} + $__socket__$.call.apply($__socket__$, [uuid, ...cbargs]) }) - if(rpc.onClose){ - clientSocket.on('close', () => rpc.onClose(res, rpc)) - } + ${rpc.onDestroy ? `$__socket__$.bind(uuid, () => { + rpc.onDestroy(res, rpc) + })` : ``} return {'uuid': uuid, 'return': res} }catch(e){ //can throw to pass exception to client or swallow to keep it local - errorHandler(clientSocket)(e, ${rpc.name}, [${args}]) + errorHandler($__socket__$)(e, ${rpc.name}, [${args}]) } - }`) + }` + + return eval(hookStr) } -const makeError = (callName: string) => { - return new Error("Call not found: " + callName + ". ; Zone: ; Task: Promise.then ; Value: Error: Call not found: " + callName) -} +const makeError = (callName: string) => new Error(`Call not found: ${callName}. ; Zone: ; Task: Promise.then ; Value: Error: Call not found: ${callName}`) /** * Extract a string list of parameters from a function @@ -189,24 +187,25 @@ export function fixNames(o: Object): void { }) } +/** + * Transforms a socket.io instance into one conforming to I.Socket + * @param socket A socket.io socket + */ export const makePioSocket = (socket: any): I.Socket => { - return { - bind: (name: string, listener: T.PioBindListener) => socket.on(name, (...args: any) => { - const ack = args.pop() - listener.apply(null, args) - ack() - }), + return { + id: socket.id, + bind: (name: string, listener: T.PioBindListener) => socket.on(name, (...args: any) => listener.apply(null, args)), hook: (name: string, listener: T.PioHookListener) => { const args = extractArgs(listener) let argNames let restParam = args.find(e => e.includes('...')) - if(!restParam){ - argNames = [...args, '...__args__'].join(',') - restParam = '__args__' - }else{ + if (!restParam) { + argNames = [...args, '...$__args__$'].join(',') + restParam = '$__args__$' + } else { argNames = [...args].join(',') - restParam = restParam.replace('...','') + restParam = restParam.replace('...', '') } const decoratedListener = eval(`(() => async (${argNames}) => { @@ -229,7 +228,7 @@ export const makePioSocket = (socket: any): I.Socket => { call: (name: string, ...args: any) => { return new Promise((res, rej) => { const params: any = [name, ...args, (resp) => { - if(isError(resp)){ + if (isError(resp)) { const err = new Error() err.stack = resp.stack err.name = resp.name @@ -238,7 +237,7 @@ export const makePioSocket = (socket: any): I.Socket => { } res(resp) }] - socket.emit.apply(socket, params) + socket.emit.apply(socket, params) }) }, @@ -256,17 +255,15 @@ export const makePioSocket = (socket: any): I.Socket => { } }, - id: socket.id, on: (...args) => socket.on.apply(socket, args), emit: (...args) => socket.emit.apply(socket, args), close: () => { - socket socket.disconnect(true) } } } -export const isError = function(e){ - return e && e.stack && e.message && typeof e.stack === 'string' - && typeof e.message === 'string'; - } +export const isError = function (e) { + return e && e.stack && e.message && typeof e.stack === 'string' + && typeof e.message === 'string'; +} diff --git a/test/Test.ts b/test/Test.ts index 7206ace..7e89dce 100644 --- a/test/Test.ts +++ b/test/Test.ts @@ -1,16 +1,20 @@ import { describe, it } from "mocha"; import { RPCServer, RPCSocket } from '../Index' -import { RPCExporter } from "../src/Interfaces"; +import { RPCExporter, Socket } from "../src/Interfaces"; import { ConnectedSocket } from "../src/Types"; import * as log from 'why-is-node-running'; import * as http from 'http'; import * as express from 'express'; import * as fetch from 'node-fetch'; +import { PromiseIO } from "../src/PromiseIO/Server"; +import { PromiseIOClient } from "../src/PromiseIO/Client"; + +const noop = (...args) => { } const add = (...args: number[]) => { return args.reduce((a, b) => a + b, 0) } -function makeServer() { +function makeServer(onCallback = noop, connectionHandler = noop, hookCloseHandler = noop, closeHandler = noop, errorHandler = (socket, err) => { throw err }) { let subcallback - const sv = new RPCServer([{ + const serv = new RPCServer([{ name: 'test', RPCs: [ { @@ -22,36 +26,92 @@ function makeServer() { subcallback = callback return { topic: "test" } }, - onClose: (res) => { } + onDestroy: hookCloseHandler }, { name: 'subscribe', hook: async (callback) => { subcallback = callback return { topic: "test" } }, - onClose: (res, rpc) => { - console.log("onClose", rpc.name === 'subscribe' && res ? "OK" : "") - subcallback = null - }, - onCallback: (...args: any) => { - console.log("onCallback", args[0] === "test" && args[1] === "callback" ? "OK" : "") - } + onDestroy: hookCloseHandler, + onCallback: onCallback }, add, function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) }, + function brokenRPC(){ throw new Error("Intended error") } ] - }], - { - connectionHandler: (socket) => { - }, - closeHandler: (socket) => { }, - errorHandler: (socket, err) => { throw err } - }) - sv.listen(21010) - return sv + }], + { + connectionHandler: connectionHandler, + closeHandler: closeHandler, + errorHandler: errorHandler + }) + serv.listen(21010) + return serv } +describe('PromiseIO', () => { + + it("bind + fire", (done) => { + const server = new PromiseIO() + server.attach(new http.Server()) + server.on("socket", clientSocket => { + clientSocket.bind("test123", (p1,p2) => { + server.close() + if(p1 === "p1" && p2 === "p2") + done() + }) + }); + + server.listen(21003) + PromiseIOClient.connect(21003, "localhost", "http").then(cli => { + cli.fire("test123", "p1", "p2") + cli.close() + }) + }) + + it("hook + call", (done) => { + const server = new PromiseIO() + server.attach(new http.Server()) + server.on("socket", clientSocket => { + clientSocket.hook("test123", (p1,p2) => { + if(p1 === "p1" && p2 === "p2") + return "OK" + }) + }); + + server.listen(21003) + PromiseIOClient.connect(21003, "localhost", "http").then(cli => { + cli.call("test123", "p1", "p2").then(resp => { + cli.close() + server.close() + + if(resp === "OK") + done() + }) + }) + }) + + it("on + emit", (done) => { + const server = new PromiseIO() + server.attach(new http.Server()) + server.on("socket", clientSocket => { + clientSocket.on("test123", (p1,p2) => { + server.close() + if(p1 === "p1" && p2 === "p2") + done() + }) + }); + + server.listen(21003) + PromiseIOClient.connect(21003, "localhost", "http").then(cli => { + cli.emit("test123", "p1", "p2") + cli.close() + }) + }) +}) + describe('RPCServer', () => { let client, server const echo = (x) => x @@ -133,21 +193,21 @@ describe('RPCServer with premade http server', () => { { name: 'Grp2', RPCs: [ - function test(){ return "test" } + function test() { return "test" } ], } ] - let client:RPCSocket, server:RPCServer + let client: RPCSocket, server: RPCServer before(done => { const expressServer = express() const httpServer = new http.Server(expressServer) - expressServer.get('/REST_ping', (req, res)=>{ + expressServer.get('/REST_ping', (req, res) => { return res - .send('REST_pong') - .status(200) + .send('REST_pong') + .status(200) }) server = new RPCServer( @@ -157,7 +217,7 @@ describe('RPCServer with premade http server', () => { httpServer.listen(8080) client = new RPCSocket(8080, 'localhost') - + done() }) @@ -171,10 +231,10 @@ describe('RPCServer with premade http server', () => { it('should serve REST', (done) => { fetch('http://localhost:8080/REST_ping').then(response => { response.text().then(text => { - if(text === "REST_pong") + if (text === "REST_pong") done() else - done(new Error("REST repsonse was "+text)) + done(new Error("REST repsonse was " + text)) }) }) }) @@ -197,6 +257,54 @@ describe('RPCServer with premade http server', () => { }) +describe('Serverside Triggers', () => { + let server, client + const closerFunction = (done) => () => { + client.close() + server.close() + done() + } + + it('trigger onCallback', (done) => { + server = makeServer(closerFunction(done)) + client = new RPCSocket(21010, "localhost") + client.connect().then(_ => { + client['test'].subscribe(noop).then(_ => client['test'].triggerCallback()) + }) + }) + + it('trigger connectionHandler', (done) => { + server = makeServer(undefined, closerFunction(done)) + client = new RPCSocket(21010, "localhost") + client.connect() + }) + + + it('trigger hook closeHandler', (done) => { + server = makeServer(undefined, undefined, closerFunction(done)) + client = new RPCSocket(21010, "localhost") + client.connect().then(_ => { + client['test'].subscribe(function cb(){ + cb['destroy']() + }).then(_ => client['test'].triggerCallback()) + }) + }) + + + it('trigger global closeHandler', (done) => { + server = makeServer(undefined, undefined, undefined, () => { + server.close() + done() + }) + client = new RPCSocket(21010, "localhost") + client.connect().then(_ => { + client['test'].subscribe(noop).then(_ => client.close()) + }) + }) + + +}) + describe('RPCSocket', () => { let client: RPCSocket let server: RPCServer @@ -266,6 +374,7 @@ describe('RPCSocket', () => { }) }) + describe('It should do unhook', () => { const yesCandy = "OK" const noCandy = "stolen" @@ -304,12 +413,12 @@ describe('It should do unhook', () => { function publish(): string { cb(candy); return candy }, function unsubscribe(): string { candy = noCandy; cb(candy); cb = () => { }; return candy } ] - }], - { - connectionHandler: (socket) => { }, - closeHandler: (socket) => { }, - errorHandler: (socket, err) => { throw err } - }) + }], + { + connectionHandler: noop, + closeHandler: noop, + errorHandler: (socket, err) => { throw err } + }) server.listen(21010) client = new RPCSocket(21010, "localhost") return await client.connect() @@ -321,7 +430,7 @@ describe('It should do unhook', () => { }) it('Subscribe with param', (done) => { - client['test'].subscribeWithParam("OK", c => { }).then(async (res) => { + client['test'].subscribeWithParam("OK", noop).then(async (res) => { if (res.uuid === candy) { done() } else @@ -363,10 +472,6 @@ type SesameTestIfc = { subscribe: (callback: Function) => Promise manyParams: (a: A, b: B, c: C, d: D) => Promise<[A, B, C, D]> } - - other: { - echo: (x: any) => Promise - } } describe('Sesame should unlock the socket', () => { @@ -387,17 +492,11 @@ describe('Sesame should unlock the socket', () => { topic: 'test' } }, - onClose: (a) => { } + onDestroy: noop }, - async function checkCandy() { cb(candy); cb = () => { }; return candy }, + async function checkCandy() { cb(candy); cb = noop; return candy }, async function manyParams(a, b, c, d) { return [a, b, c, d] } ], - }, { - name: 'other', - RPCs: () => [ - async function echo(x) { return x } - ] - }], { sesame: (_sesame) => _sesame === 'sesame!' }) @@ -413,7 +512,7 @@ describe('Sesame should unlock the socket', () => { client.close() server.close() }) - + it('should work with sesame', (done) => { client.test.checkCandy().then(c => done()) }) @@ -575,7 +674,7 @@ describe("Errorhandler functionality", () => { }) .then(r => { if (r != null) - done("UNEXPECTED RESULT " + r) + done(new Error("UNEXPECTED RESULT " + r)) }) .catch((e) => { done(new Error("UNEXPECTED CLIENT ERROR " + e.message)) @@ -614,11 +713,10 @@ describe("Errorhandler functionality", () => { }) .then(r => { if (r != null) - done("UNEXPECTED RESULT " + r) + done(new Error("UNEXPECTED RESULT " + r)) }) .catch((e) => { - done("UNEXPECTED CLIENT ERROR " + e) - done(e) + done(new Error("UNEXPECTED CLIENT ERROR " + e)) }) .finally(() => { cli.close() @@ -670,7 +768,7 @@ describe("Class binding", () => { before(done => { exporter1 = new MyExporter() serv = new RPCServer( - [exporter1], + [exporter1], { accessFilter: async (sesame, exporter) => { if (exporter.name === 'MyExporter') { @@ -772,6 +870,25 @@ describe("attaching handlers before connecting", () => { }) }) + it("fires error if call is unknown", (done) => { + const serv = new RPCServer().listen(21004) + const sock = new RPCSocket(21004, 'localhost') + + sock.on('error', (err) => { + sock.close() + serv.close() + done() + }) + + sock.connect().then(_ => { + sock.call("unknownRPC123", "AAAAA").catch(e => { }).then(x => { + done(new Error("unexpected return value")) + }) + }).catch(e => { + done(e) + }) + }) + /* * ## 1.11.0 breaking ## *