sesame function
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rpclibrary",
|
"name": "rpclibrary",
|
||||||
"version": "1.4.7",
|
"version": "1.5.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": {
|
||||||
|
|||||||
+11
-6
@@ -20,6 +20,7 @@ export class RPCServer<
|
|||||||
private closeHandler:T.CloseHandler
|
private closeHandler:T.CloseHandler
|
||||||
private errorHandler: T.ErrorHandler
|
private errorHandler: T.ErrorHandler
|
||||||
private connectionHandler: T.ConnectionHandler
|
private connectionHandler: T.ConnectionHandler
|
||||||
|
private sesame? : T.SesameFunction
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws On RPC with no name
|
* @throws On RPC with no name
|
||||||
@@ -30,11 +31,16 @@ 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>[] = [],
|
||||||
private conf: T.ServerConf = {}
|
conf: T.ServerConf = {}
|
||||||
){
|
){
|
||||||
|
|
||||||
if(!conf.visibility) this.visibility = "127.0.0.1"
|
if(!conf.visibility) this.visibility = "127.0.0.1"
|
||||||
|
|
||||||
|
if(conf.sesame){
|
||||||
|
console.log("Setting Sesame")
|
||||||
|
this.sesame = U.makeSesameFunction(conf.sesame)
|
||||||
|
}
|
||||||
|
|
||||||
this.errorHandler = (socket:I.Socket) => (error:any) => {
|
this.errorHandler = (socket:I.Socket) => (error:any) => {
|
||||||
if(conf.errorHandler) conf.errorHandler(socket, error)
|
if(conf.errorHandler) conf.errorHandler(socket, error)
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
@@ -42,12 +48,12 @@ export class RPCServer<
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.closeHandler = (socket:I.Socket) => {
|
this.closeHandler = (socket:I.Socket) => {
|
||||||
if(!conf.closeHandler) console.log("Socket on port "+socket.port+" closing")
|
if(!conf.closeHandler) console.log("Connection on port "+socket.port+" closing")
|
||||||
else conf.closeHandler(socket)
|
else conf.closeHandler(socket)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connectionHandler = (socket:I.Socket) => {
|
this.connectionHandler = (socket:I.Socket) => {
|
||||||
if(!conf.connectionHandler) console.log("New websocket connection on port "+socket.port)
|
if(!conf.connectionHandler) console.log("New connection on port "+socket.port)
|
||||||
else conf.connectionHandler(socket)
|
else conf.connectionHandler(socket)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,15 +80,14 @@ export class RPCServer<
|
|||||||
})
|
})
|
||||||
this.ws.listen(this.port, this.visibility)
|
this.ws.listen(this.port, this.visibility)
|
||||||
}catch(e){
|
}catch(e){
|
||||||
//@ts-ignore
|
this.errorHandler(this.io, e)
|
||||||
this.errorHandler(undefined)("Unable to connect to socket")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.conf.sesame))
|
...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter, this.sesame))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -7,8 +7,10 @@ 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 SesameFunction = (sesame : string) => boolean
|
||||||
|
|
||||||
export type SesameConf = {
|
export type SesameConf = {
|
||||||
sesame?: string
|
sesame?: string | SesameFunction
|
||||||
}
|
}
|
||||||
export type ServerConf = {
|
export type ServerConf = {
|
||||||
connectionHandler?: ConnectionHandler
|
connectionHandler?: ConnectionHandler
|
||||||
@@ -65,7 +67,7 @@ export type BaseInfo = {
|
|||||||
|
|
||||||
export type HookInfo<SubresT = {}> = BaseInfo & {
|
export type HookInfo<SubresT = {}> = BaseInfo & {
|
||||||
type: 'Hook',
|
type: 'Hook',
|
||||||
generator: (socket?:I.Socket) => (...args:any) => SubresT
|
generator: (socket?:I.Socket) => (...args:any[]) => SubresT
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CallInfo = BaseInfo & {
|
export type CallInfo = BaseInfo & {
|
||||||
|
|||||||
+18
-7
@@ -10,7 +10,8 @@ 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, sesame?:string):T.RpcInfo => {
|
export const rpcToRpcinfo = <SubResT = {}>(rpc : T.RPC<any, any, SubResT>, owner: string, sesame?:T.SesameFunction):T.RpcInfo => {
|
||||||
|
|
||||||
switch (typeof rpc){
|
switch (typeof rpc){
|
||||||
case "object":
|
case "object":
|
||||||
if(rpc['call']){
|
if(rpc['call']){
|
||||||
@@ -19,7 +20,7 @@ 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: sesame?async (_sesame, ...args) => {if(sesame === _sesame) return await rpc['call'].apply({}, args)}:rpc['call'],
|
call: sesame?async (_sesame, ...args) => {if(sesame(_sesame)) return await rpc['call'].apply({}, args)}:rpc['call'], // check & remove sesame
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
const generator = hookGenerator(<T.HookRPC<any, any, any>>rpc, sesame)
|
const generator = hookGenerator(<T.HookRPC<any, any, any>>rpc, sesame)
|
||||||
@@ -44,7 +45,7 @@ RPC did not provide a name.
|
|||||||
argNames: extractArgs(rpc),
|
argNames: extractArgs(rpc),
|
||||||
type: "Call",
|
type: "Call",
|
||||||
name: rpc.name,
|
name: rpc.name,
|
||||||
call: async(...args) => (<Function>rpc).apply({}, args),
|
call: sesame?async (_sesame, ...args) => {if(sesame(_sesame)) return await rpc.apply({}, args)}:rpc, // check & remove sesame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error("Bad socketIORPC type "+ typeof rpc)
|
throw new Error("Bad socketIORPC type "+ typeof rpc)
|
||||||
@@ -56,7 +57,7 @@ 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>, sesame?:string, makeUnique = true):T.ExtendedRpcInfo[]{
|
export function rpcHooker<SubResT = {}>(socket: I.Socket, exporter:I.RPCExporter<any, any, SubResT>, sesame?:T.SesameFunction, makeUnique = true):T.ExtendedRpcInfo[]{
|
||||||
const owner = exporter.name
|
const owner = exporter.name
|
||||||
const RPCs = [...exporter.exportRPCs()]
|
const RPCs = [...exporter.exportRPCs()]
|
||||||
|
|
||||||
@@ -85,15 +86,15 @@ 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>, sesame?:string): T.HookInfo['generator'] => {
|
const hookGenerator = (rpc:T.HookRPC<any, any, any>, sesame?:T.SesameFunction): T.HookInfo['generator'] => {
|
||||||
const argsArr = extractArgs(rpc.hook)
|
const argsArr = extractArgs(rpc.hook)
|
||||||
if(sesame){
|
if(sesame){
|
||||||
const _sesame = argsArr.shift()
|
const _sesame = argsArr.shift()
|
||||||
if(sesame !== _sesame){
|
if(!sesame(_sesame!)){
|
||||||
throw new Error('Bad sesame')
|
throw new Error('Bad sesame')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
argsArr.pop()
|
argsArr.pop() //remove 'callback' from the end
|
||||||
const args = argsArr.join(',')
|
const args = argsArr.join(',')
|
||||||
|
|
||||||
return eval(`(socket) => async (`+args+`) => {
|
return eval(`(socket) => async (`+args+`) => {
|
||||||
@@ -132,3 +133,13 @@ export function makeSubResponse<T extends {} = {}>(extension:T):SubscriptionResp
|
|||||||
...extension
|
...extension
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function makeSesameFunction (sesame : T.SesameFunction | string) : T.SesameFunction {
|
||||||
|
if(typeof sesame === 'function'){
|
||||||
|
return sesame
|
||||||
|
}
|
||||||
|
|
||||||
|
return (testSesame : string) => {
|
||||||
|
return testSesame === sesame
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -224,3 +224,46 @@ describe('It should do unhook', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
type SesameTestIfc = { test: { checkCandy: ()=>Promise<string>} }
|
||||||
|
|
||||||
|
describe('Sesame should unlock the socket', () => {
|
||||||
|
let candy = "OK"
|
||||||
|
let client: RPCSocket & SesameTestIfc
|
||||||
|
let server: RPCServer<{topic: string}, SesameTestIfc>
|
||||||
|
|
||||||
|
before(async() => {
|
||||||
|
server = new RPCServer<{ topic: string }, SesameTestIfc>(20004, [{
|
||||||
|
name: "test",
|
||||||
|
exportRPCs: () => [
|
||||||
|
async function checkCandy():Promise<string> { return candy },
|
||||||
|
]}
|
||||||
|
],{
|
||||||
|
sesame: (_sesame) => _sesame === 'sesame!'
|
||||||
|
})
|
||||||
|
const sock = new RPCSocket(20004, "localhost")
|
||||||
|
client = await sock.connect<SesameTestIfc>('sesame!')
|
||||||
|
})
|
||||||
|
|
||||||
|
after(() => {
|
||||||
|
client.destroy()
|
||||||
|
server.destroy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not work without sesame', (done) => {
|
||||||
|
const sock = new RPCSocket(20004, "localhost")
|
||||||
|
sock.connect<SesameTestIfc>( /* no sesame */).then(async (c) => {
|
||||||
|
c.test.checkCandy().then(d => {
|
||||||
|
if(d === candy)
|
||||||
|
done("should not be able to get candy")
|
||||||
|
done()
|
||||||
|
}).finally(() => {
|
||||||
|
sock.destroy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should work with sesame', (done) => {
|
||||||
|
client.test.checkCandy().then(c => done())
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user