clean up RPC structure

This commit is contained in:
2019-09-21 14:17:23 +02:00
parent 160be94e3b
commit 67278ff2fe
8 changed files with 93 additions and 75 deletions
-2
View File
@@ -3,7 +3,6 @@ import * as Front from './src/Frontend';
import * as Types from './src/Types'; import * as Types from './src/Types';
import * as Utils from './src/Utils'; import * as Utils from './src/Utils';
import * as Interfaces from './src/Interfaces'; import * as Interfaces from './src/Interfaces';
import * as Responses from './src/Responses';
export { export {
Back as Backend, Back as Backend,
@@ -11,5 +10,4 @@ export {
Types, Types,
Utils, Utils,
Interfaces, Interfaces,
Responses
} }
+4 -2
View File
@@ -7,7 +7,9 @@ import * as T from './Types';
import * as U from './Utils'; import * as U from './Utils';
import * as I from './Interfaces'; import * as I from './Interfaces';
export class RPCServer{ export class RPCServer<
SubResType = {}
>{
private ws = http.createServer() private ws = http.createServer()
private io = bsock.createServer() private io = bsock.createServer()
private visibility:T.Visibility private visibility:T.Visibility
@@ -18,7 +20,7 @@ export class RPCServer{
constructor( constructor(
private port:number, private port:number,
private exporters: I.Exporter[] = [], private exporters: I.Exporter<SubResType>[] = [],
conf: T.SocketConf = {} conf: T.SocketConf = {}
){ ){
+2 -2
View File
@@ -81,8 +81,8 @@ export class RPCSocket implements I.Socket{
const argParams = fnArgs.map(stripAfterEquals).join(",") const argParams = fnArgs.map(stripAfterEquals).join(",")
return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => { return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => {
const r = await this.socket.call("`+fnName+`", `+argParams+`) const r = await this.socket.call("`+fnName+`", `+argParams+`)
if(r.uid != null){ if(r.result === 'Success'){
this.socket.hook(r.uid, callback) this.socket.hook(r.uuid, callback)
} }
return r return r
} )()` ) } )()` )
+2 -2
View File
@@ -1,9 +1,9 @@
import * as T from "./Types"; import * as T from "./Types";
import * as I from "./Interfaces" import * as I from "./Interfaces"
export interface Exporter{ export interface Exporter<T = {}>{
name: T.Name name: T.Name
exportRPCs() : T.RPC[] exportRPCs() : T.RPC<T>[]
} }
export interface Socket { export interface Socket {
-37
View File
@@ -1,37 +0,0 @@
export type Outcome = "Success" | "Error"
export class Response{
constructor(
public message?:string
){}
}
export class SuccessResponse extends Response{
result:Outcome = "Success"
constructor(
message?:string
){
super(message)
}
}
export class ErrorResponse extends Response{
result:Outcome = "Error"
constructor(
message: string = "Unknown error"
){
super(message)
}
}
export class SubscriptionResponse extends SuccessResponse{
constructor(
public uid: string,
message?:string
){
super(message)
}
}
+18 -10
View File
@@ -1,4 +1,3 @@
import * as R from "./Responses";
import * as I from "./Interfaces"; import * as I from "./Interfaces";
export type Visibility = "127.0.0.1" | "0.0.0.0" export type Visibility = "127.0.0.1" | "0.0.0.0"
@@ -16,13 +15,22 @@ export type SocketConf = {
visibility?: Visibility visibility?: Visibility
} }
export type ResponseType = "Subscribe" | "Success" | "Error"
export type Outcome = "Success" | "Error"
export type Respose<T> = T & { result: Outcome }
export type SuccessResponse<T = {}> = Respose<T> & { result: "Success" }
export type ErrorResponse<T = {}> = Respose<T> & { result: "Error" }
export type SubscriptionResponse<T = {}> = Respose<T> & { result: "Success"; uuid: string }
export type RPCType = 'Hook' | 'Unhook' | 'Call' export type RPCType = 'Hook' | 'Unhook' | 'Call'
export type HookRPC = { export type HookRPC<T = {}> = {
name: Name name: Name
hook: HookFunction hook: HookFunction<T>
onCallback?: CallbackFunction, onCallback?: CallbackFunction,
onClose?: HookCloseFunction onClose?: HookCloseFunction<T>
} }
export type CallRPC = { export type CallRPC = {
@@ -30,7 +38,7 @@ export type CallRPC = {
call: AsyncFunction call: AsyncFunction
} | Function } | Function
export type RPC = CallRPC | HookRPC export type RPC<T = {}> = CallRPC | HookRPC<T>
export type BaseInfo = { export type BaseInfo = {
name: Name, name: Name,
@@ -38,9 +46,9 @@ export type BaseInfo = {
argNames: Name[], argNames: Name[],
} }
export type HookInfo = BaseInfo & { export type HookInfo<T = {}> = BaseInfo & {
type: 'Hook', type: 'Hook',
generator: (socket) => HookFunction generator: (socket) => HookFunction<T>
} }
export type CallInfo = BaseInfo & { export type CallInfo = BaseInfo & {
@@ -52,7 +60,7 @@ 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 = (type: 'error' | 'close', f: (e?:any)=>void) => I.Socket
export type HookCloseFunction = (res:R.SubscriptionResponse, rpc:HookRPC) => any export type HookCloseFunction<T = {}> = (res:SubscriptionResponse<T>, rpc:HookRPC<T>) => any
export type HookFunction = (...args:any[]) => Promise<R.SubscriptionResponse | R.ErrorResponse> export type HookFunction<T = {}> = (...args:[any, ...any[]]) => Promise<SubscriptionResponse<T> | ErrorResponse>
export type AsyncFunction = (...args:any[]) => Promise<any> export type AsyncFunction = (...args:any[]) => Promise<any>
export type CallbackFunction = (arg: any) => void export type CallbackFunction = (...args:any[]) => void
+24 -16
View File
@@ -1,9 +1,10 @@
import * as uuid from "uuid/v4" import * as uuidv4 from "uuid/v4"
import * as T from "./Types"; import * as T from "./Types";
import * as I from "./Interfaces"; import * as I from "./Interfaces";
import { SubscriptionResponse } from "./Types";
export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => { export const rpcToRpcinfo = <SubResT = {}>(rpc : T.RPC<SubResT>, owner: T.Owner):T.RpcInfo => {
switch (typeof rpc){ switch (typeof rpc){
case "object": case "object":
if(rpc['call']){ if(rpc['call']){
@@ -15,7 +16,7 @@ export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => {
call: rpc['call'], call: rpc['call'],
} }
}else{ }else{
const generator = hookGenerator(<T.HookRPC>rpc) const generator = hookGenerator(<T.HookRPC<any>>rpc)
return { return {
owner: owner, owner: owner,
argNames: extractArgs(generator(undefined)), argNames: extractArgs(generator(undefined)),
@@ -26,27 +27,27 @@ export const rpcToRpcinfo = (rpc : T.RPC, owner: T.Owner):T.RpcInfo => {
} }
case "function": case "function":
if(!rpc.name) throw new Error(` if(!rpc.name) 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.
\n \n
\n<------------OFFENDING RPC: \n<------------OFFENDING RPC:
\n`+rpc.toString()+` \n`+rpc.toString()+`
\n>------------OFFENDING RPC`) \n>------------OFFENDING RPC`)
return { return {
type: "Call",
owner : owner, owner : owner,
argNames: extractArgs(rpc), argNames: extractArgs(rpc),
type: "Call",
name: rpc.name,
call: async(...args) => rpc.apply({}, args), call: async(...args) => rpc.apply({}, args),
name: rpc.name
} }
} }
throw new Error("Bad socketIORPC type "+ typeof rpc) throw new Error("Bad socketIORPC type "+ typeof rpc)
} }
export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = true):T.ExtendedRpcInfo[]{ export function rpcHooker<SubResT = {}>(socket: I.Socket, exporter:I.Exporter<SubResT>, makeUnique = true):T.ExtendedRpcInfo[]{
const owner = exporter.name const owner = exporter.name
const RPCs = [...exporter.exportRPCs()] const RPCs = [...exporter.exportRPCs()]
const suffix = makeUnique?"-"+uuid().substr(0,4):"" const suffix = makeUnique?"-"+uuidv4().substr(0,4):""
return RPCs.map(rpc => rpcToRpcinfo(rpc, owner)) return RPCs.map(rpc => rpcToRpcinfo(rpc, owner))
.map(info => { .map(info => {
const ret:any = info const ret:any = info
@@ -65,7 +66,7 @@ export function rpcHooker(socket: I.Socket, exporter:I.Exporter, makeUnique = tr
}) })
} }
const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => { const hookGenerator = (rpc:T.HookRPC<any>): T.HookInfo['generator'] => {
const argsArr = extractArgs(rpc.hook) const argsArr = extractArgs(rpc.hook)
argsArr.pop() argsArr.pop()
const args = argsArr.join(',') const args = argsArr.join(',')
@@ -73,9 +74,9 @@ const hookGenerator = (rpc:T.HookRPC): T.HookInfo['generator'] => {
return eval(`(socket) => async (`+args+`) => { return eval(`(socket) => async (`+args+`) => {
const res = await rpc.hook(`+args+(args.length!==0?',':'')+` (...cbargs) => { const res = await rpc.hook(`+args+(args.length!==0?',':'')+` (...cbargs) => {
if(rpc.onCallback) rpc.onCallback.apply({}, cbargs) if(rpc.onCallback) rpc.onCallback.apply({}, cbargs)
socket.call.apply(socket, [res.uid, ...cbargs]) socket.call.apply(socket, [res.uuid, ...cbargs])
}) })
if(res.result == 'Success'){ if(res.result === 'Success'){
if(rpc.onClose){ if(rpc.onClose){
socket.on('close', async () => { socket.on('close', async () => {
rpc.onClose(res, rpc) rpc.onClose(res, rpc)
@@ -90,3 +91,10 @@ const extractArgs = (f:Function):T.Arg[] => {
let fn let fn
return (fn = String(f)).substr(0, fn.indexOf(")")).substr(fn.indexOf("(")+1).split(",") return (fn = String(f)).substr(0, fn.indexOf(")")).substr(fn.indexOf("(")+1).split(",")
} }
export function makeSubResponse(uuid?:string):SubscriptionResponse{
return {
result: "Success",
uuid: uuid?uuid:uuidv4(),
}
}
+43 -4
View File
@@ -1,6 +1,8 @@
import { RPCServer } from '../src/Backend' import { RPCServer } from '../src/Backend'
import { SubscriptionResponse, ErrorResponse, SuccessResponse } from '../src/Responses' import { SubscriptionResponse, ErrorResponse, SuccessResponse } from '../src/Types'
import { HookRPC } from '../src/Types' import { HookRPC } from '../src/Types'
import * as uuidv4 from "uuid/v4"
import { makeSubResponse } from '../src/Utils'
let subcallback let subcallback
@@ -14,19 +16,56 @@ new RPCServer(20000, [{
name: 'simpleSubscribe', name: 'simpleSubscribe',
hook: async(callback) => { hook: async(callback) => {
subcallback = callback subcallback = callback
return new SubscriptionResponse(""+Math.random()) return makeSubResponse()
} }
},{ },{
name: 'subscribe', name: 'subscribe',
hook: async (callback):Promise<any> => { hook: async (callback):Promise<any> => {
subcallback = callback subcallback = callback
return new SubscriptionResponse(""+Math.random()) return makeSubResponse()
}, },
onClose: (res:SubscriptionResponse, rpc:HookRPC) => { onClose: (res:SubscriptionResponse, rpc:HookRPC) => {
console.log("Specific close handler for", rpc.name, res) console.log("Specific close handler for", rpc.name, res)
subcallback = null subcallback = null
}, },
onCallback: (...args) => { console.log.apply(console, args) } onCallback: (...args:any) => { console.log.apply(console, args) }
},
function add(...args:number[]):number {return args.reduce((a,b)=>a+b, 0)},
function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)},
]
}])
new RPCServer<{ topic: string}>(20001, [{
name: "HelloWorldRPCGroup",
exportRPCs: () => [
{
name: 'echo',
call: async (s:string) => s,
},{
name: 'simpleSubscribe',
hook: async(callback) => {
subcallback = callback
return {
result: "Success",
uuid: uuidv4(),
topic: ""
}
}
},{
name: 'subscribe',
hook: async (callback) => {
subcallback = callback
return {
result: "Success",
uuid: uuidv4(),
topic: ""
}
},
onClose: (res, rpc) => {
console.log("Specific close handler for", rpc.name, res)
subcallback = null
},
onCallback: (...args:any) => { console.log.apply(console, args) }
}, },
function add(...args:number[]):number {return args.reduce((a,b)=>a+b, 0)}, function add(...args:number[]):number {return args.reduce((a,b)=>a+b, 0)},
function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)}, function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)},