v0.1.4 sesame sockets

This commit is contained in:
2019-11-02 20:20:48 +01:00
parent 2828d1af67
commit 4087a12a87
8 changed files with 618 additions and 771 deletions
+538 -740
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "rpclibrary", "name": "rpclibrary",
"version": "1.3.17", "version": "1.4.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": {
+2 -2
View File
@@ -30,7 +30,7 @@ export class RPCServer<
constructor( constructor(
private port:number, private port:number,
private exporters: I.RPCExporter<T.RPCInterface<InterfaceT>, keyof InterfaceT, SubResType>[] = [], private exporters: I.RPCExporter<T.RPCInterface<InterfaceT>, keyof InterfaceT, SubResType>[] = [],
conf: T.SocketConf = {} private conf: T.ServerConf = {}
){ ){
if(!conf.visibility) this.visibility = "127.0.0.1" if(!conf.visibility) this.visibility = "127.0.0.1"
@@ -82,7 +82,7 @@ export class RPCServer<
protected initRPCs(socket:I.Socket){ protected initRPCs(socket:I.Socket){
socket.hook('info', () => rpcInfos) socket.hook('info', () => rpcInfos)
const rpcInfos:T.ExtendedRpcInfo[] = [ const rpcInfos:T.ExtendedRpcInfo[] = [
...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter)) ...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter, this.conf.sesame))
] ]
} }
+22 -9
View File
@@ -17,8 +17,8 @@ function stripAfterEquals(str:string):string{
* A websocket-on-steroids with built-in RPC capabilities * A websocket-on-steroids with built-in RPC capabilities
*/ */
export class RPCSocket implements I.Socket{ export class RPCSocket implements I.Socket{
static async makeSocket<T extends T.RPCInterface= T.RPCInterface>(port:number, server: string, tls: boolean = false): Promise<RPCSocket & T.RPCInterface<T>> { static async makeSocket<T extends T.RPCInterface= T.RPCInterface>(port:number, server: string, conf?:T.SocketConf): Promise<RPCSocket & T.RPCInterface<T>> {
const socket = <RPCSocket & T> new RPCSocket(port, server, tls) const socket = <RPCSocket & T> new RPCSocket(port, server, conf)
return await socket.connect<T>() return await socket.connect<T>()
} }
@@ -30,7 +30,7 @@ export class RPCSocket implements I.Socket{
* @param server Server address * @param server Server address
* @param tls @default false use TLS * @param tls @default false use TLS
*/ */
constructor(public port:number, private server: string, private tls: boolean = false){ constructor(public port:number, private server: string, private conf:T.SocketConf = { tls: false }){
Object.defineProperty(this, 'socket', {value: undefined, writable: true}) Object.defineProperty(this, 'socket', {value: undefined, writable: true})
} }
@@ -95,18 +95,18 @@ export class RPCSocket implements I.Socket{
/** /**
* Connects to the server and attaches available RPCs to this object * Connects to the server and attaches available RPCs to this object
*/ */
public async connect<T extends T.RPCInterface= T.RPCInterface>() : Promise<RPCSocket & T.RPCInterface<T>>{ public async connect<T extends T.RPCInterface= T.RPCInterface>( sesame?: string ) : Promise<RPCSocket & T.RPCInterface<T>>{
this.socket = await bsock.connect(this.port, this.server, this.tls) this.socket = await bsock.connect(this.port, this.server, this.conf.tls?this.conf.tls:false)
const info:T.ExtendedRpcInfo[] = await this.info() const info:T.ExtendedRpcInfo[] = await this.info()
info.forEach(i => { info.forEach(i => {
let f: any let f: any
switch (i.type) { switch (i.type) {
case 'Call': case 'Call':
f = this.callGenerator(i.uniqueName, i.argNames) f = this.callGenerator(i.uniqueName, i.argNames, sesame)
break break
case 'Hook': case 'Hook':
f = this.hookGenerator(i.uniqueName, i.argNames) f = this.hookGenerator(i.uniqueName, i.argNames, sesame)
break break
} }
if(this[i.owner] == null) if(this[i.owner] == null)
@@ -129,10 +129,13 @@ export class RPCSocket implements I.Socket{
* @param fnName The function name * @param fnName The function name
* @param fnArgs A string-list of parameters * @param fnArgs A string-list of parameters
*/ */
private callGenerator(fnName: string, fnArgs:string[]): T.AnyFunction{ private callGenerator(fnName: string, fnArgs:string[], sesame?:string): T.AnyFunction{
const headerArgs = fnArgs.join(",") const headerArgs = fnArgs.join(",")
const argParams = fnArgs.map(stripAfterEquals).join(",") const argParams = fnArgs.map(stripAfterEquals).join(",")
if(!sesame)
return eval( '( () => async ('+headerArgs+') => { return await this.socket.call("'+fnName+'", '+argParams+')} )()' ) return eval( '( () => async ('+headerArgs+') => { return await this.socket.call("'+fnName+'", '+argParams+')} )()' )
else
return eval( '( () => async ('+headerArgs+') => { return await this.socket.call("'+fnName+'", "'+sesame+'", '+argParams+')} )()' )
} }
/** /**
@@ -140,9 +143,10 @@ export class RPCSocket implements I.Socket{
* @param fnName The function name * @param fnName The function name
* @param fnArgs A string-list of parameters * @param fnArgs A string-list of parameters
*/ */
private hookGenerator(fnName: string, fnArgs:string[]): T.HookFunction{ private hookGenerator(fnName: string, fnArgs:string[], sesame?:string): T.HookFunction{
const headerArgs = fnArgs.join(",") const headerArgs = fnArgs.join(",")
const argParams = fnArgs.map(stripAfterEquals).join(",") const argParams = fnArgs.map(stripAfterEquals).join(",")
if(!sesame){
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.result === 'Success'){ if(r.result === 'Success'){
@@ -150,5 +154,14 @@ export class RPCSocket implements I.Socket{
} }
return r return r
} )()` ) } )()` )
}else{
return eval( `( () => async (`+headerArgs+(headerArgs.length!==0?",":"")+` callback) => {
const r = await this.socket.call("`+fnName+`", "`+sesame+`", `+argParams+`)
if(r.result === 'Success'){
this.socket.hook(r.uuid, callback)
}
return r
} )()` )
}
} }
} }
+2 -2
View File
@@ -5,8 +5,8 @@ import * as I from "./Interfaces"
* Interface for all classes that export RPCs * Interface for all classes that export RPCs
*/ */
export interface RPCExporter< export interface RPCExporter<
Ifc extends T.RPCInterface, Ifc extends T.RPCInterface = T.RPCInterface,
Name extends keyof Ifc, Name extends keyof Ifc = keyof Ifc,
SubresT = {} SubresT = {}
>{ >{
name: Name name: Name
+8 -1
View File
@@ -7,11 +7,18 @@ export type Visibility = "127.0.0.1" | "0.0.0.0"
export type ConnectionHandler = (socket:I.Socket) => void export type ConnectionHandler = (socket:I.Socket) => void
export type ErrorHandler = (socket:I.Socket, error:any) => void export type ErrorHandler = (socket:I.Socket, error:any) => void
export type CloseHandler = (socket:I.Socket) => void export type CloseHandler = (socket:I.Socket) => void
export type SocketConf = { export type SesameConf = {
sesame?: string
}
export type ServerConf = {
connectionHandler?: ConnectionHandler connectionHandler?: ConnectionHandler
errorHandler?: ErrorHandler errorHandler?: ErrorHandler
closeHandler?: CloseHandler closeHandler?: CloseHandler
visibility?: Visibility visibility?: Visibility
} & SesameConf
export type SocketConf = {
tls:boolean
} }
export type ResponseType = "Subscribe" | "Success" | "Error" export type ResponseType = "Subscribe" | "Success" | "Error"
+15 -6
View File
@@ -10,7 +10,7 @@ import { SubscriptionResponse } from "./Types";
* @param owner The owning RPC group's name * @param owner The owning RPC group's name
* @throws Error on RPC without name property * @throws Error on RPC without name property
*/ */
export const rpcToRpcinfo = <SubResT = {}>(rpc : T.RPC<any, any, SubResT>, owner: string):T.RpcInfo => { export const rpcToRpcinfo = <SubResT = {}>(rpc : T.RPC<any, any, SubResT>, owner: string, sesame?:string):T.RpcInfo => {
switch (typeof rpc){ switch (typeof rpc){
case "object": case "object":
if(rpc['call']){ if(rpc['call']){
@@ -19,10 +19,10 @@ export const rpcToRpcinfo = <SubResT = {}>(rpc : T.RPC<any, any, SubResT>, owner
argNames: extractArgs(rpc['call']), argNames: extractArgs(rpc['call']),
type: "Call", type: "Call",
name: rpc.name, name: rpc.name,
call: rpc['call'], call: sesame?async (_sesame, ...args) => {if(sesame === _sesame) return await rpc['call'].apply({}, args)}:rpc['call'],
} }
}else{ }else{
const generator = hookGenerator(<T.HookRPC<any, any, any>>rpc) const generator = hookGenerator(<T.HookRPC<any, any, any>>rpc, sesame)
return { return {
owner: owner, owner: owner,
argNames: extractArgs(generator(undefined)), argNames: extractArgs(generator(undefined)),
@@ -56,15 +56,18 @@ RPC did not provide a name.
* @param exporter The exporter * @param exporter The exporter
* @param makeUnique @default true Attach a suffix to RPC names * @param makeUnique @default true Attach a suffix to RPC names
*/ */
export function rpcHooker<SubResT = {}>(socket: I.Socket, exporter:I.RPCExporter<any, any, SubResT>, makeUnique = true):T.ExtendedRpcInfo[]{ export function rpcHooker<SubResT = {}>(socket: I.Socket, exporter:I.RPCExporter<any, any, SubResT>, sesame?:string, makeUnique = true):T.ExtendedRpcInfo[]{
const owner = exporter.name const owner = exporter.name
const RPCs = [...exporter.exportRPCs()] const RPCs = [...exporter.exportRPCs()]
const suffix = makeUnique?"-"+uuidv4().substr(0,4):"" const suffix = makeUnique?"-"+uuidv4().substr(0,4):""
return RPCs.map(rpc => rpcToRpcinfo(rpc, owner)) return RPCs.map(rpc => rpcToRpcinfo(rpc, owner, sesame))
.map(info => { .map(info => {
const ret:any = info const ret:any = info
ret.uniqueName = info.name+suffix ret.uniqueName = info.name+suffix
switch(info.type){ switch(info.type){
case "Hook": case "Hook":
socket.hook(ret.uniqueName, info.generator(socket)) socket.hook(ret.uniqueName, info.generator(socket))
@@ -82,8 +85,14 @@ export function rpcHooker<SubResT = {}>(socket: I.Socket, exporter:I.RPCExporter
* @param rpc The RPC to transform * @param rpc The RPC to transform
* @returns A {@link HookFunction} * @returns A {@link HookFunction}
*/ */
const hookGenerator = (rpc:T.HookRPC<any, any, any>): T.HookInfo['generator'] => { const hookGenerator = (rpc:T.HookRPC<any, any, any>, sesame?:string): T.HookInfo['generator'] => {
const argsArr = extractArgs(rpc.hook) const argsArr = extractArgs(rpc.hook)
if(sesame){
const _sesame = argsArr.shift()
if(sesame !== _sesame){
throw new Error('Bad sesame')
}
}
argsArr.pop() argsArr.pop()
const args = argsArr.join(',') const args = argsArr.join(',')
+20
View File
@@ -51,3 +51,23 @@ RPCSocket.makeSocket<MyInterface>(20000, 'localhost').then((async (client) => {
await client.Group1.triggerCallbacks("Hello", "World", "Callbacks") await client.Group1.triggerCallbacks("Hello", "World", "Callbacks")
})) }))
const srv = new RPCServer(30000, [{
name: 'Group2',
exportRPCs: () => [{
name: 'echo',
call: async (x) => x
}]
}], {
sesame: 'open'
})
const s = new RPCSocket(30000, 'localhost')
s.connect("open").then(async() => {
s['Group2']['echo']('open', 'dfgfg').then(console.log)
s['Group2']['echo']('dfgfg').then(console.log)
s['Group2']['echo']('dfgfg').then(console.log)
})