From 2b765eb2a272725fd4f4c49b0a11c6073d81ac38 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 21 Sep 2019 02:56:03 +0200 Subject: [PATCH] fix --- index.ts | 7 ++++ package-lock.json | 2 +- src/Backend.ts | 61 ++++++++++++++++++++++------------ src/Frontend.ts | 10 +++--- src/Interfaces.ts | 3 +- src/Types.ts | 18 +++++----- src/Utils.ts | 78 ++++++++++++++++++++++++++++---------------- src/webpack.js | 2 +- test/TestBackend.ts | 33 ++++++++++++++++--- test/TestFrontend.ts | 27 ++++++++++++--- test/index.html | 1 + 11 files changed, 165 insertions(+), 77 deletions(-) create mode 100644 index.ts create mode 100644 test/index.html diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..d47b585 --- /dev/null +++ b/index.ts @@ -0,0 +1,7 @@ +import * as Back from './src/backend/RPCSocketServer'; +import * as Front from './src/frontend/RPCSocket'; + +export { + Back as Backend, + Front as Frontend +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 70a4801..5eab954 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "rpclibrary", - "version": "1.0.1", + "version": "1.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/Backend.ts b/src/Backend.ts index 8e633c5..6d01b58 100644 --- a/src/Backend.ts +++ b/src/Backend.ts @@ -18,17 +18,46 @@ export { export class Server{ private ws = http.createServer() private io = bsock.createServer() + private visibility:T.Visibility + private closeHandler:T.CloseHandler + private errorHandler: T.ErrorHandler + private connectionHandler: T.ConnectionHandler + constructor( private port:number, private exporters: I.Exporter[] = [], - private conf: T.SocketConf = { - errorHandler: (socket:I.Socket) => (error:any) => { socket.destroy(); console.error(error) }, - closeHandler: (socket:I.Socket) => () => { console.log("Socket closing") }, - connectionHandler: (socket:I.Socket) => { console.log("New websocket connection in port "+socket.port)}, - visibility: "127.0.0.1" - } + conf: T.SocketConf = {} ){ + + if(!conf.visibility) this.visibility = "127.0.0.1" + + if(!conf.errorHandler) this.errorHandler = + (socket:I.Socket) => (error:any) => { + socket.destroy(); + console.error(error) + } + + if(!conf.closeHandler) this.closeHandler = + (socket:I.Socket) => () => { + console.log("Socket on port "+socket.port+"closing") + } + + if(!conf.connectionHandler) this.connectionHandler = + (socket:I.Socket) => { + console.log("New websocket connection in port "+socket.port) + } + + let badRPC + if(badRPC = exporters.flatMap(ex => ex.exportRPCs()).find(rpc => !rpc.name)) + throw new Error(` + RPC did not provide a name. + \nUse funtion name(..){ .. } syntax instead. + \n + \n<------------OFFENDING RPC: + \n`+badRPC.toString()+` + \n>------------OFFENDING RPC`) + this.startWebsocket() } @@ -36,14 +65,12 @@ export class Server{ try{ this.io.attach(this.ws) this.io.on('socket', (socket:I.Socket) => { - socket.on('error', this.conf.errorHandler(socket)) - socket.on('close', this.conf.closeHandler(socket)) - if(this.conf.visibility === "127.0.0.1") - this.initRPCs(socket) - else - this.initPublicRPCs(socket) + socket.on('error', this.errorHandler(socket)) + socket.on('close', this.closeHandler(socket)) + this.connectionHandler(socket) + this.initRPCs(socket) }) - this.ws.listen(this.port, this.conf.visibility) + this.ws.listen(this.port, this.visibility) }catch(e){ //@ts-ignore this.errorHandler(undefined)("Unable to connect to socket") @@ -57,12 +84,4 @@ export class Server{ ...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter)) ] } - - protected initPublicRPCs(socket:I.Socket){ - socket.hook('info', () => rpcInfos) - - const rpcInfos:T.ExtendedRpcInfo[] = [ - ...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter)) - ] - } } \ No newline at end of file diff --git a/src/Frontend.ts b/src/Frontend.ts index d277257..891af91 100644 --- a/src/Frontend.ts +++ b/src/Frontend.ts @@ -1,6 +1,6 @@ 'use strict' -var bsock = require('bsock') +import bsock = require('bsock'); import * as T from './Types'; import * as U from './Utils'; @@ -26,8 +26,6 @@ export class Client implements I.Socket{ constructor(public port:number, private server: string, private tls: boolean = false){ } - - public hook(name: T.Name, args: T.Arg){ return this.socket.hook(name, args) } @@ -96,9 +94,9 @@ export class Client implements I.Socket{ return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => { const r = await this.socket.call("`+fnName+`", `+argParams+`) if(r.uid != null){ - this.socket.hook(res.uid, callback) + this.socket.hook(r.uid, callback) } - return res + return r } )()` ) } @@ -111,7 +109,7 @@ export class Client implements I.Socket{ return eval( `( () => async (`+headerArgs+`) => { const r = await this.socket.call("`+fnName+`", `+argParams+`) this.socket.unhook(`+argParams+`) - return res + return r } )()` ) } diff --git a/src/Interfaces.ts b/src/Interfaces.ts index b64fbff..65a7910 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -3,8 +3,7 @@ import * as I from "./Interfaces" export interface Exporter{ name: T.Name - localRPCs() : T.RPC[] - publicRPCs() : T.RPC[] + exportRPCs() : T.RPC[] } export interface Socket { diff --git a/src/Types.ts b/src/Types.ts index 63dcdae..ca2cee8 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -6,12 +6,14 @@ export type Any = any export type Arg = string export type Name = Arg export type Owner = Name - +export type ConnectionHandler = (socket:I.Socket) => void +export type ErrorHandler = (socket:I.Socket) => (error:any) => void +export type CloseHandler = (socket:I.Socket) => () => void export type SocketConf = { - connectionHandler: (socket:I.Socket) => void - errorHandler: (socket:I.Socket) => (error:any) => void - closeHandler: (socket:I.Socket) => () => void - visibility: Visibility + connectionHandler?: ConnectionHandler + errorHandler?: ErrorHandler + closeHandler?: CloseHandler + visibility?: Visibility } export type RPCType = 'Hook' | 'Unhook' | 'Call' @@ -23,7 +25,7 @@ export type BaseRPC = { export type HookRPC = BaseRPC & { type: 'Hook' - clbk: CallbackFunction + hook: CallbackFunction unhook: UnhookFunction } @@ -32,10 +34,10 @@ export type UnhookRPC = BaseRPC & { unhook: UnhookFunction } -export type CallRPC = BaseRPC & { +export type CallRPC = (BaseRPC & { type: 'Call' call: AsyncFunction -} +} ) | Function export type RPC = CallRPC | UnhookRPC | HookRPC diff --git a/src/Utils.ts b/src/Utils.ts index d288c61..fef5823 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -4,38 +4,59 @@ import * as T from "./Types"; import * as I from "./Interfaces"; export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => { - switch(rpc.type){ - case "Call" : + switch (typeof rpc){ + case "object": + switch(rpc.type){ + case "Call" : + return { + owner: owner, + argNames: extractArgs(rpc.call), + type: rpc.type, + name: rpc.name, + call: rpc.call, + } + case "Unhook" : + return { + owner: owner, + argNames: extractArgs(rpc.unhook), + type: rpc.type, + name: rpc.name, + unhook: rpc.unhook, + } + case "Hook" : + const generator = hookGenerator(rpc) + return { + owner: owner, + argNames: extractArgs(generator(undefined)), + type: rpc.type, + name: rpc.name, + unhook: rpc.unhook, + generator: generator, + } + } + break; + case "function": + if(!rpc.name) throw new Error(` + RPC did not provide a name. + \nUse funtion name(..){ .. } syntax instead. + \n + \n<------------OFFENDING RPC: + \n`+rpc.toString()+` + \n>------------OFFENDING RPC`) return { - owner: owner, - argNames: extractArgs(rpc.call), - type: rpc.type, - name: rpc.name, - call: rpc.call, - } - case "Unhook" : - return { - owner: owner, - argNames: extractArgs(rpc.unhook), - type: rpc.type, - name: rpc.name, - unhook: rpc.unhook, - } - case "Hook" : - const generator = hookGenerator(rpc) - return { - owner: owner, - argNames: extractArgs(generator(undefined)), - type: rpc.type, - name: rpc.name, - unhook: rpc.unhook, - generator: generator, + type: "Call", + owner : owner, + argNames: extractArgs(rpc), + call: async(...args) => rpc.apply({}, args), + name: rpc.name } } + throw new Error("Bad socketIORPC type "+ typeof rpc) } + export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = true):T.ExtendedRpcInfo[]{ const owner = exporter.name - const RPCs = [...exporter.publicRPCs(), ...exporter.localRPCs()] + const RPCs = [...exporter.exportRPCs()] const suffix = makeUnique?"-"+uuid().substr(0,4):"" return RPCs.map(rpc => rpcToRpcinfo(rpc, owner)) .map(info => { @@ -59,12 +80,12 @@ export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = tr } const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { - const argsArr = extractArgs(rpc.clbk) + const argsArr = extractArgs(rpc.hook) argsArr.pop() const args = argsArr.join(',') return eval(`(socket) => async (`+args+`) => { - const res = await rpc.clbk(`+args+(args.length!==0?',':'')+` (x) => { + const res = await rpc.hook(`+args+(args.length!==0?',':'')+` (x) => { socket.call(res.uid, x) }) if(res.result == 'Success'){ @@ -72,7 +93,6 @@ const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { const unhookRes = await rpc.unhook(res.uid) console.log("Specific close handler for", rpc.name, res.uid, unhookRes) }) - } return res }`) diff --git a/src/webpack.js b/src/webpack.js index ea871b9..ae46e31 100644 --- a/src/webpack.js +++ b/src/webpack.js @@ -5,7 +5,7 @@ const frontendConf = { target: "web", entry: path.resolve(__dirname, '..', 'js', 'src', 'Frontend.js'), output: { - path: path.resolve(__dirname, '..', 'js', 'src'), + path: path.resolve(__dirname, '..', 'js', 'rpclibrary.browser.js'), filename: "Frontend.js", libraryTarget: 'commonjs', }, diff --git a/test/TestBackend.ts b/test/TestBackend.ts index b794756..1e32d3d 100644 --- a/test/TestBackend.ts +++ b/test/TestBackend.ts @@ -1,11 +1,36 @@ import { Server } from '../src/Backend' +import { SubscriptionResponse } from '../src/Responses' + +let subcallback new Server(20000, [{ name: "HelloWorldRPCGroup", - publicRPCs: () => [], - localRPCs: () => [{ + exportRPCs: () => [{ type: 'Call', name: 'echo', call: async (s:string) => s, - }] -}]) \ No newline at end of file + }, + { + type: 'Hook', + name: 'subscribe', + hook: async (callback):Promise => { + subcallback = callback + return new SubscriptionResponse(""+Math.random()) + }, + unhook: async (uid):Promise => { subcallback = null } + }, + function add(...args:number[]):number {return args.reduce((a,b)=>a+b, 0)}, + function triggerCallback(message):number {return subcallback(message)}, +] +}]) + +try{ + new Server(20001, [{ + name: "bad", + exportRPCs: () => [ + (aaa,bbb,ccc) => { return aaa+bbb+ccc } + ] + }]) +}catch(badRPCError){ + console.log("expected bad-RPC error happened: "+ !!badRPCError) +} \ No newline at end of file diff --git a/test/TestFrontend.ts b/test/TestFrontend.ts index 12191e4..48c8fee 100644 --- a/test/TestFrontend.ts +++ b/test/TestFrontend.ts @@ -1,7 +1,24 @@ import { Client } from '../src/Frontend' - +console.log(Client) const client = new Client(20000, 'localhost') -client.connect().then(_ => { - client.info().then(console.log) - client["HelloWorldRPCGroup"].echo("x").then(console.log) -}) +client.connect().then(async _ => { + await client.info().then(console.log) + + await client["HelloWorldRPCGroup"].echo("x") + .then(hopefullyX => console.log("echo('x') returned x: ", hopefullyX === "x", hopefullyX)) + + await client["HelloWorldRPCGroup"].add(1,2,3) + .then(hopefully6 => console.log("add(1,2,3) returned 6: ", hopefully6 === 6, hopefully6)) + + let counter = 0 + const handler = (s) => { + counter++ + if(counter === 3) + console.log("callback was called 3 times", counter === 3) + } + + await client["HelloWorldRPCGroup"].subscribe(handler) + client["HelloWorldRPCGroup"].triggerCallback("test1") + client["HelloWorldRPCGroup"].triggerCallback("test2") + client["HelloWorldRPCGroup"].triggerCallback("test3") +}) \ No newline at end of file diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..32395da --- /dev/null +++ b/test/index.html @@ -0,0 +1 @@ + \ No newline at end of file