diff --git a/package.json b/package.json index a979f16..2030a81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rpclibrary", - "version": "1.5.1", + "version": "1.5.2", "description": "rpclibrary is a websocket on steroids!", "main": "./js/Index.js", "repository": { diff --git a/src/Backend.ts b/src/Backend.ts index 0c84158..be355c6 100644 --- a/src/Backend.ts +++ b/src/Backend.ts @@ -37,7 +37,6 @@ export class RPCServer< if(!conf.visibility) this.visibility = "127.0.0.1" if(conf.sesame){ - console.log("Setting Sesame") this.sesame = U.makeSesameFunction(conf.sesame) } diff --git a/src/Frontend.ts b/src/Frontend.ts index aed48fe..d99abf9 100644 --- a/src/Frontend.ts +++ b/src/Frontend.ts @@ -106,7 +106,7 @@ export class RPCSocket implements I.Socket{ f = this.callGenerator(i.uniqueName, i.argNames, sesame) break case 'Hook': - f = this.hookGenerator(i.uniqueName, i.argNames, sesame) + f = this.frontEndHookGenerator(i.uniqueName, i.argNames, sesame) break } if(this[i.owner] == null) @@ -143,21 +143,28 @@ export class RPCSocket implements I.Socket{ * @param fnName The function name * @param fnArgs A string-list of parameters */ - private hookGenerator(fnName: string, fnArgs:string[], sesame?:string): T.HookFunction{ + private frontEndHookGenerator(fnName: string, fnArgs:string[], sesame?:string): T.HookFunction{ + fnArgs.pop() const headerArgs = fnArgs.join(",") const argParams = fnArgs.map(stripAfterEquals).join(",") + if(!sesame){ - return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => { + const headerArgs = fnArgs.join(",") + const argParams = fnArgs.map(stripAfterEquals).join(",") + + const f = eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => { const r = await this.socket.call("`+fnName+`", `+argParams+`) - if(r.result === 'Success'){ + if(r && r.result === 'Success'){ this.socket.hook(r.uuid, callback) } return r } )()` ) + return f }else{ + return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => { const r = await this.socket.call("`+fnName+`", "`+sesame+`", `+argParams+`) - if(r.result === 'Success'){ + if(r && r.result === 'Success'){ this.socket.hook(r.uuid, callback) } return r diff --git a/src/Utils.ts b/src/Utils.ts index b479e49..44f73ab 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -82,37 +82,38 @@ export function rpcHooker(socket: I.Socket, exporter:I.RPCExporter }) } /** - * Utility function to generate {@link HookFunction} from a RPC + * Utility function to generate {@link HookFunction} from a RPC for backend * @param rpc The RPC to transform * @returns A {@link HookFunction} */ -const hookGenerator = (rpc:T.HookRPC, sesame?:T.SesameFunction): T.HookInfo['generator'] => { +const hookGenerator = (rpc:T.HookRPC, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => { const argsArr = extractArgs(rpc.hook) - if(sesame){ - const _sesame = argsArr.shift() - if(!sesame(_sesame!)){ - throw new Error('Bad sesame') - } - } argsArr.pop() //remove 'callback' from the end - const args = argsArr.join(',') + const argsStr = argsArr.join(',') - return eval(`(socket) => async (`+args+`) => { + if(sesameFn){ + const args = ['sesame', ...argsArr].join(',') + const f = eval(`(socket) => async (`+args+`) => { + if(!sesameFn(sesame)) return + const res = await rpc.hook(`+argsStr+(argsStr.length!==0?',':'')+` (...cbargs) => { + if(rpc.onCallback) rpc.onCallback.apply({}, cbargs) + socket.call.apply(socket, [res.uuid, ...cbargs]) + }) + return res + }`) + return f + } + const args = argsArr.join(',') + return eval(`(socket) => async (`+args+`) => { const res = await rpc.hook(`+args+(args.length!==0?',':'')+` (...cbargs) => { if(rpc.onCallback) rpc.onCallback.apply({}, cbargs) socket.call.apply(socket, [res.uuid, ...cbargs]) }) - if(res.result === 'Success'){ - if(rpc.onClose){ - socket.on('close', async () => { - rpc.onClose(res, rpc) - }) - } - } return res }`) } + /** * Extract a string list of parameters from a function * @param f The source function @@ -134,6 +135,7 @@ export function makeSubResponse(extension:T):SubscriptionResp } } + export function makeSesameFunction (sesame : T.SesameFunction | string) : T.SesameFunction { if(typeof sesame === 'function'){ return sesame @@ -142,4 +144,4 @@ export function makeSesameFunction (sesame : T.SesameFunction | string) : T.Sesa return (testSesame : string) => { return testSesame === sesame } -} \ No newline at end of file +} diff --git a/test/Test.ts b/test/Test.ts index 9b9bd77..9dc1428 100644 --- a/test/Test.ts +++ b/test/Test.ts @@ -1,4 +1,4 @@ -import { describe, it } from "mocha"; +import { describe, it, Func } from "mocha"; import { RPCServer, RPCSocket, SubscriptionResponse, makeSubResponse } from '../Index' import * as uuidv4 from "uuid/v4" @@ -226,18 +226,35 @@ describe('It should do unhook', () => { }) -type SesameTestIfc = { test: { checkCandy: ()=>Promise} } +type SesameTestIfc = { + test: { + checkCandy: ()=>Promise + subscribe: (callback) => Promise> + } +} describe('Sesame should unlock the socket', () => { let candy = "OK" let client: RPCSocket & SesameTestIfc - let server: RPCServer<{topic: string}, SesameTestIfc> + let server: RPCServer + let cb = (...args) => {} before(async() => { - server = new RPCServer<{ topic: string }, SesameTestIfc>(20004, [{ + server = new RPCServer(20004, [{ name: "test", exportRPCs: () => [ - async function checkCandy():Promise { return candy }, + { + name: 'subscribe', + hook: async(callback) => { + cb = callback + return { + result: "Success", + uuid: uuidv4(), + topic: 'test' + } + } + }, + async function checkCandy():Promise { cb(candy); cb=()=>{}; return candy }, ]} ],{ sesame: (_sesame) => _sesame === 'sesame!' @@ -251,6 +268,10 @@ describe('Sesame should unlock the socket', () => { server.destroy() }) + it('should work with sesame', (done) => { + client.test.checkCandy().then(c => done()) + }) + it('should not work without sesame', (done) => { const sock = new RPCSocket(20004, "localhost") sock.connect( /* no sesame */).then(async (c) => { @@ -264,7 +285,35 @@ describe('Sesame should unlock the socket', () => { }) }) - it('should work with sesame', (done) => { - client.test.checkCandy().then(c => done()) + it('callback should work with sesame', (done) => { + client.test.subscribe((c) => { + if(c === candy){ + done() + } + }).then(d => { + if(d.result !== 'Success') + done('expected valid response') + + client.test.checkCandy() + }) }) -}) \ No newline at end of file + + it('callback should not work without sesame', (done) => { + const sock = new RPCSocket(20004, "localhost") + sock.connect( /* no sesame */).then(async (c) => { + c.test.subscribe((c) => { + console.log("CALLBACK TRIGGERED UNEXPECTED"); + + if(c === candy) + done("super not") + }).then(async d => { + await client.test.checkCandy() + if(d == null){ + done() + }else + done('unexpected valid response '+(d) ) + c.destroy() + }) + }) + }) +})