fix listener bugs
This commit is contained in:
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rpclibrary",
|
||||
"version": "2.0.0",
|
||||
"version": "2.2.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
+12
-10
@@ -10,7 +10,7 @@ export class RPCServer<
|
||||
InterfaceT extends T.RPCInterface = T.RPCInterface,
|
||||
> {
|
||||
|
||||
private pio = PromiseIO.createServer()
|
||||
private pio = new PromiseIO()
|
||||
private closeHandler: T.CloseHandler
|
||||
private errorHandler: T.ErrorHandler
|
||||
private connectionHandler: T.ConnectionHandler
|
||||
@@ -37,7 +37,6 @@ export class RPCServer<
|
||||
return this.sesame!(sesame!)
|
||||
})
|
||||
|
||||
|
||||
this.errorHandler = (socket: I.Socket | PromiseIO) => (error: any, rpcName: string, args: any[]) => {
|
||||
if (conf.errorHandler) conf.errorHandler(socket, error, rpcName, args)
|
||||
else throw error
|
||||
@@ -65,11 +64,13 @@ export class RPCServer<
|
||||
}
|
||||
|
||||
try {
|
||||
this.pio.on('socket', (socket: I.Socket) => {
|
||||
socket.on('error', (err) => this.errorHandler(socket, err, "system", []))
|
||||
socket.on('close', () => this.closeHandler(socket))
|
||||
this.connectionHandler(socket)
|
||||
this.initRPCs(socket)
|
||||
|
||||
this.pio.on('socket', (clientSocket: I.Socket) => {
|
||||
const sock:any = clientSocket;
|
||||
clientSocket.on('disconnect', () => this.closeHandler(clientSocket))
|
||||
this.connectionHandler(clientSocket)
|
||||
this.initRPCs(clientSocket)
|
||||
|
||||
})
|
||||
} catch (e) {
|
||||
this.errorHandler(this.pio, e, 'system', [])
|
||||
@@ -88,12 +89,13 @@ export class RPCServer<
|
||||
return this
|
||||
}
|
||||
|
||||
protected initRPCs(socket: I.Socket) {
|
||||
socket.hook('info', async (sesame?: string) => {
|
||||
protected initRPCs(clientSocket: I.Socket) {
|
||||
clientSocket.hook('info', async (sesame?: string) => {
|
||||
const rpcs = await Promise.all(this.exporters.map(async exp => {
|
||||
const allowed = await this.accessFilter(sesame, exp)
|
||||
if (!allowed) return []
|
||||
return U.rpcHooker(socket, exp, this.errorHandler, this.sesame)
|
||||
const infos = U.rpcHooker(clientSocket, exp, this.errorHandler, this.sesame)
|
||||
return infos
|
||||
}))
|
||||
return rpcs.flat()
|
||||
})
|
||||
|
||||
+13
-9
@@ -16,7 +16,7 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
||||
return await socket.connect(sesame)
|
||||
}
|
||||
|
||||
private protocol: 'http:' | 'https:'
|
||||
private protocol: 'http' | 'https'
|
||||
private socket: I.Socket
|
||||
private handlers : {
|
||||
[name in string]: T.AnyFunction[]
|
||||
@@ -32,9 +32,9 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
||||
* @param server Server address
|
||||
* @param tls @default false use TLS
|
||||
*/
|
||||
constructor(public port:number, private server: string, conf:T.SocketConf = { tls: false }){
|
||||
constructor(public port:number, public address: string, conf:T.SocketConf = { tls: false }){
|
||||
Object.defineProperty(this, 'socket', {value: undefined, writable: true})
|
||||
this.protocol = conf.tls ? "https:" : "http:"
|
||||
this.protocol = conf.tls ? "https" : "http"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,7 +141,7 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
||||
public async connect( sesame?: string ) : Promise<T.ConnectedSocket<Ifc>> {
|
||||
|
||||
try{
|
||||
this.socket = await PromiseIOClient.connect(this.port, this.server, this.protocol)
|
||||
this.socket = await PromiseIOClient.connect(this.port, this.address, this.protocol)
|
||||
}catch(e){
|
||||
this.handlers['error'].forEach(h => h(e))
|
||||
throw e
|
||||
@@ -215,14 +215,17 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
||||
sesame = appendComma(sesame, true)
|
||||
headerArgs = fnArgs.length>0?headerArgs+",":headerArgs
|
||||
|
||||
return eval( `
|
||||
async (${headerArgs} callback) => {
|
||||
const frontendHookStr = `
|
||||
async (${headerArgs} $__callback__$) => {
|
||||
const r = await this.call("${fnName}", ${sesame} ${argParams})
|
||||
try{
|
||||
if(r){
|
||||
if(r.uuid){
|
||||
callback['destroy'] = () => { this.socket.unhook(r.uuid) }
|
||||
this.socket.hook(r.uuid, callback)
|
||||
$__callback__$['destroy'] = () => {
|
||||
this.socket.fire(r.uuid)
|
||||
this.socket.unhook(r.uuid)
|
||||
}
|
||||
this.socket.hook(r.uuid, $__callback__$)
|
||||
}
|
||||
return r.return
|
||||
}else{
|
||||
@@ -231,6 +234,7 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
||||
}catch(e){
|
||||
throw e
|
||||
}
|
||||
}`)
|
||||
}`
|
||||
return eval(frontendHookStr)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,6 +20,6 @@ export interface Socket {
|
||||
call: (rpcname: string, ...args: any[]) => Promise<any>
|
||||
fire: (rpcname: string, ...args: any[]) => Promise<any>
|
||||
on: (type: string, f: T.AnyFunction)=>any
|
||||
emit: (eventName: string, data: any) => void
|
||||
emit: (eventName: string, ...args: any[]) => void
|
||||
close(): void
|
||||
}
|
||||
@@ -5,21 +5,26 @@ import * as socketio from 'socket.io-client'
|
||||
|
||||
export class PromiseIOClient {
|
||||
|
||||
static connect = (port: number, host = "localhost", protocol : 'http:' | 'https:' = "http:"): Promise<I.Socket> => new Promise((res, rej) => {
|
||||
static connect = (port: number, host = "localhost", protocol : 'http' | 'https' = "http"): Promise<I.Socket> => new Promise((res, rej) => {
|
||||
try {
|
||||
const socket = socketio(`${protocol}//${host}:${port}`, {
|
||||
const address = `${host}:${port}`
|
||||
const socket = socketio(`${protocol}://${address}`, {
|
||||
reconnectionAttempts: 2,
|
||||
reconnectionDelay: 200,
|
||||
timeout: 450,
|
||||
reconnection: false,
|
||||
})
|
||||
|
||||
socket.on('connect_error', e => {
|
||||
sock.emit('error', e)
|
||||
rej(e)
|
||||
})
|
||||
|
||||
socket['address'] = address
|
||||
const sock = U.makePioSocket(socket)
|
||||
socket.on('connect', ()=>{ res(sock) })
|
||||
socket.on('connect', ()=>{
|
||||
res(sock)
|
||||
})
|
||||
|
||||
|
||||
/*
|
||||
|
||||
+15
-6
@@ -4,6 +4,7 @@ import * as U from '../Utils'
|
||||
import * as T from '../Types'
|
||||
import socketio = require('socket.io')
|
||||
|
||||
|
||||
export class PromiseIO {
|
||||
io?: Server
|
||||
httpServer: httpServer
|
||||
@@ -12,15 +13,23 @@ export class PromiseIO {
|
||||
connect: []
|
||||
}
|
||||
|
||||
static createServer(): PromiseIO {
|
||||
return new PromiseIO();
|
||||
}
|
||||
|
||||
attach(httpServer: httpServer) {
|
||||
this.httpServer = httpServer
|
||||
this.io = socketio(httpServer, { cookie:false })
|
||||
this.io!.on('connection', (sock: Socket) => {
|
||||
const pioSock = U.makePioSocket(sock)
|
||||
|
||||
this.io!.on('connection', (clientSocket: Socket) => {
|
||||
console.log(this.listeners);
|
||||
|
||||
clientSocket.use((packet, next) => {
|
||||
console.log(this.listeners[packet[0]]);
|
||||
|
||||
console.log(packet[0]);
|
||||
next()
|
||||
})
|
||||
|
||||
|
||||
clientSocket['address'] = clientSocket.handshake.headers["x-real-ip"] || clientSocket.handshake.address
|
||||
const pioSock = U.makePioSocket(clientSocket)
|
||||
this.listeners['socket'].forEach(listener => listener(pioSock))
|
||||
this.listeners['connect'].forEach(listener => listener(pioSock))
|
||||
/*
|
||||
|
||||
+4
-4
@@ -5,8 +5,6 @@ import { PromiseIO } from "./PromiseIO/Server";
|
||||
export type PioBindListener = (...args: any) => void
|
||||
export type PioHookListener = AnyFunction
|
||||
|
||||
|
||||
|
||||
export type AnyFunction = (...args:any) => any
|
||||
export type HookFunction = AnyFunction
|
||||
export type AccessFilter<InterfaceT extends RPCInterface = RPCInterface> = (sesame:string|undefined, exporter: I.RPCExporter<InterfaceT, keyof InterfaceT>) => Promise<boolean> | boolean
|
||||
@@ -57,7 +55,7 @@ export type HookRPC<Name, Func extends AnyFunction> = {
|
||||
name: Name
|
||||
hook: Func
|
||||
onCallback?: AnyFunction
|
||||
onClose?: HookCloseFunction<ReturnType<Func> extends Promise<infer T> ? T : ReturnType<Func>>
|
||||
onDestroy?: HookCloseFunction<ReturnType<Func> extends Promise<infer T> ? T : ReturnType<Func>>
|
||||
}
|
||||
|
||||
export type RPC<Name, Func extends AnyFunction> = HookRPC<Name, Func> | CallRPC<Name,Func> | Func
|
||||
@@ -101,4 +99,6 @@ export type HookCloseFunction<T> = (res: T, rpc:HookRPC<any, any>) => any
|
||||
|
||||
export type AsyncIfc<Ifc extends RPCInterface> = { [grp in keyof Ifc]: {[rpcname in keyof Ifc[grp]] : AsyncAnyFunction<Ifc[grp][rpcname]> } }
|
||||
|
||||
export type AsyncAnyFunction<F extends AnyFunction = AnyFunction> = F extends (...args: Parameters<F>) => infer R ? ((...args: Parameters<F>) => R extends Promise<any> ? R : Promise<R> ) : Promise<any>
|
||||
export type AsyncAnyFunction<F extends AnyFunction = AnyFunction> = F extends (...args: Parameters<F>) => infer R
|
||||
? ((...args: Parameters<F>) => R extends Promise<any> ? R : Promise<R> )
|
||||
: Promise<any>
|
||||
+50
-53
@@ -2,8 +2,7 @@ import * as uuidv4 from "uuid/v4"
|
||||
|
||||
import * as T from "./Types";
|
||||
import * as I from "./Interfaces";
|
||||
import { Server as ioServer, Socket as ioSocket, Socket } from "socket.io"
|
||||
import { Socket as ioClientSocket } from "socket.io-client"
|
||||
import { Socket } from "socket.io"
|
||||
|
||||
/**
|
||||
* Translate an RPC to RPCInfo for serialization.
|
||||
@@ -17,20 +16,22 @@ export const rpcToRpcinfo = (socket: I.Socket, rpc: T.RPC<any, any>, owner: stri
|
||||
switch (typeof rpc) {
|
||||
case "object":
|
||||
if (rpc['call']) {
|
||||
const _rpc: T.CallRPC<any, any> = rpc
|
||||
return {
|
||||
owner: owner,
|
||||
argNames: extractArgs(rpc['call']),
|
||||
type: "Call",
|
||||
name: rpc.name,
|
||||
call: sesame ? async (_sesame, ...args) => { if (sesame(_sesame)) return await rpc['call'].apply({}, args); socket.close() } : rpc['call'], // check & remove sesame
|
||||
call: sesame ? async ($__sesame__$, ...args) => { if (sesame($__sesame__$)) return await rpc['call'].apply({}, args); socket.close() } : rpc['call'], // check & remove sesame
|
||||
}
|
||||
} else {
|
||||
const generator = hookGenerator(<T.HookRPC<any, any>>rpc, errorHandler, sesame)
|
||||
const _rpc: T.HookRPC<any, any> = rpc
|
||||
const generator = hookGenerator(_rpc, errorHandler, sesame)
|
||||
return {
|
||||
owner: owner,
|
||||
argNames: extractArgs(generator(undefined)),
|
||||
type: "Hook",
|
||||
name: rpc.name,
|
||||
name: _rpc.name,
|
||||
generator: generator,
|
||||
}
|
||||
}
|
||||
@@ -47,7 +48,7 @@ RPC did not provide a name.
|
||||
argNames: extractArgs(rpc),
|
||||
type: "Call",
|
||||
name: rpc.name,
|
||||
call: sesame ? async (_sesame, ...args) => { if (sesame(_sesame)) return await rpc.apply({}, args); throw makeError(rpc.name) } : rpc, // check & remove sesame
|
||||
call: sesame ? async ($__sesame__$, ...args) => { if (sesame($__sesame__$)) return await rpc.apply({}, args); throw makeError(rpc.name) } : rpc, // check & remove sesame
|
||||
}
|
||||
}
|
||||
throw new Error("Bad socketIORPC type " + typeof rpc)
|
||||
@@ -55,23 +56,22 @@ RPC did not provide a name.
|
||||
|
||||
/**
|
||||
* Utility function to apply the RPCs of an {@link RPCExporter}.
|
||||
* @param socket The websocket (implementation: bsock) to hook on
|
||||
* @param serverSocket The websocket (implementation: socket.io) to hook on
|
||||
* @param exporter The exporter
|
||||
* @param makeUnique @default true Attach a suffix to RPC names
|
||||
*/
|
||||
export function rpcHooker(socket: I.Socket, exporter: I.RPCExporter<any, any>, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction, makeUnique = true): T.ExtendedRpcInfo[] {
|
||||
export function rpcHooker(serverSocket: I.Socket, exporter: I.RPCExporter<any, any>, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction, makeUnique = true): T.ExtendedRpcInfo[] {
|
||||
const owner = exporter.name
|
||||
const RPCs = typeof exporter.RPCs === "function" ? exporter.RPCs() : exporter.RPCs
|
||||
|
||||
return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame))
|
||||
return RPCs
|
||||
.map(rpc => rpcToRpcinfo(serverSocket, rpc, owner, errorHandler, sesame))
|
||||
.map(info => {
|
||||
const suffix = makeUnique ? "-" + uuidv4().substr(0, 4) : ""
|
||||
const ret: any = info
|
||||
ret.uniqueName = info.name + suffix
|
||||
let rpcFunction = info.type === 'Hook' ? info.generator(socket)
|
||||
: info.call
|
||||
|
||||
socket.hook(ret.uniqueName, callGenerator(info.name, socket, rpcFunction, errorHandler))
|
||||
let rpcFunction = info.type === 'Hook' ? info.generator(serverSocket) : info.call
|
||||
serverSocket.hook(ret.uniqueName, callGenerator(info.name, serverSocket, rpcFunction, errorHandler))
|
||||
return ret
|
||||
})
|
||||
}
|
||||
@@ -80,18 +80,20 @@ export function rpcHooker(socket: I.Socket, exporter: I.RPCExporter<any, any>, e
|
||||
* Decorate an RPC with the error handler
|
||||
* @param rpcFunction the function to decorate
|
||||
*/
|
||||
const callGenerator = (rpcName: string, socket: I.Socket, rpcFunction: T.AnyFunction, errorHandler: T.ErrorHandler): T.AnyFunction => {
|
||||
const callGenerator = (rpcName: string, $__socket__$: I.Socket, rpcFunction: T.AnyFunction, errorHandler: T.ErrorHandler): T.AnyFunction => {
|
||||
const argsArr = extractArgs(rpcFunction)
|
||||
const args = argsArr.join(',')
|
||||
const argsStr = argsArr.map(stripAfterEquals).join(',')
|
||||
|
||||
return eval(`async (` + args + `) => {
|
||||
const callStr = `async (${args}) => {
|
||||
try{
|
||||
return await rpcFunction(`+ argsStr + `)
|
||||
return await rpcFunction(${argsStr})
|
||||
}catch(e){
|
||||
errorHandler(socket)(e, rpcName, [`+ args + `])
|
||||
errorHandler($__socket__$)(e, rpcName, [${args}])
|
||||
}
|
||||
}`)
|
||||
}`
|
||||
|
||||
return eval(callStr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,43 +109,39 @@ export function stripAfterEquals(str: string): string {
|
||||
* @param rpc The RPC to transform
|
||||
* @returns A {@link HookFunction}
|
||||
*/
|
||||
const hookGenerator = (rpc: T.HookRPC<any, any>, /*not unused!*/ errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => {
|
||||
|
||||
const hookGenerator = (rpc: T.HookRPC<any, any>, errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction, injectSocket?: boolean): T.HookInfo['generator'] => {
|
||||
let argsArr = extractArgs(rpc.hook)
|
||||
argsArr.pop() //remove 'callback' from the end
|
||||
let callArgs = argsArr.join(',')
|
||||
argsArr.pop() //remove callback param
|
||||
|
||||
let callArgs = argsArr.join(',')
|
||||
const args = sesameFn ? (['sesame', ...argsArr].join(','))
|
||||
: callArgs
|
||||
|
||||
callArgs = appendComma(callArgs, false)
|
||||
|
||||
//note rpc.hook is the associated RPC, not a socket.hook
|
||||
return eval(`
|
||||
(clientSocket) => async (${args}) => {
|
||||
const hookStr = `
|
||||
($__socket__$) => async (${args}) => {
|
||||
try{
|
||||
if(sesameFn && !sesameFn(sesame)) return
|
||||
const uuid = uuidv4()
|
||||
const res = await rpc.hook(${callArgs} (...cbargs) => {
|
||||
if(rpc.onCallback){
|
||||
rpc.onCallback.apply({}, cbargs)
|
||||
}
|
||||
clientSocket.call.apply(clientSocket, [uuid, ...cbargs])
|
||||
${rpc.onCallback ? `rpc.onCallback.apply({}, cbargs)` : ``}
|
||||
$__socket__$.call.apply($__socket__$, [uuid, ...cbargs])
|
||||
})
|
||||
if(rpc.onClose){
|
||||
clientSocket.on('close', () => rpc.onClose(res, rpc))
|
||||
}
|
||||
${rpc.onDestroy ? `$__socket__$.bind(uuid, () => {
|
||||
rpc.onDestroy(res, rpc)
|
||||
})` : ``}
|
||||
return {'uuid': uuid, 'return': res}
|
||||
}catch(e){
|
||||
//can throw to pass exception to client or swallow to keep it local
|
||||
errorHandler(clientSocket)(e, ${rpc.name}, [${args}])
|
||||
errorHandler($__socket__$)(e, ${rpc.name}, [${args}])
|
||||
}
|
||||
}`)
|
||||
}`
|
||||
|
||||
return eval(hookStr)
|
||||
}
|
||||
|
||||
const makeError = (callName: string) => {
|
||||
return new Error("Call not found: " + callName + ". ; Zone: <root> ; Task: Promise.then ; Value: Error: Call not found: " + callName)
|
||||
}
|
||||
const makeError = (callName: string) => new Error(`Call not found: ${callName}. ; Zone: <root> ; Task: Promise.then ; Value: Error: Call not found: ${callName}`)
|
||||
|
||||
/**
|
||||
* Extract a string list of parameters from a function
|
||||
@@ -189,24 +187,25 @@ export function fixNames(o: Object): void {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a socket.io instance into one conforming to I.Socket
|
||||
* @param socket A socket.io socket
|
||||
*/
|
||||
export const makePioSocket = (socket: any): I.Socket => {
|
||||
return {
|
||||
bind: (name: string, listener: T.PioBindListener) => socket.on(name, (...args: any) => {
|
||||
const ack = args.pop()
|
||||
listener.apply(null, args)
|
||||
ack()
|
||||
}),
|
||||
return <I.Socket>{
|
||||
id: socket.id,
|
||||
bind: (name: string, listener: T.PioBindListener) => socket.on(name, (...args: any) => listener.apply(null, args)),
|
||||
|
||||
hook: (name: string, listener: T.PioHookListener) => {
|
||||
const args = extractArgs(listener)
|
||||
let argNames
|
||||
let restParam = args.find(e => e.includes('...'))
|
||||
if(!restParam){
|
||||
argNames = [...args, '...__args__'].join(',')
|
||||
restParam = '__args__'
|
||||
}else{
|
||||
if (!restParam) {
|
||||
argNames = [...args, '...$__args__$'].join(',')
|
||||
restParam = '$__args__$'
|
||||
} else {
|
||||
argNames = [...args].join(',')
|
||||
restParam = restParam.replace('...','')
|
||||
restParam = restParam.replace('...', '')
|
||||
}
|
||||
|
||||
const decoratedListener = eval(`(() => async (${argNames}) => {
|
||||
@@ -229,7 +228,7 @@ export const makePioSocket = (socket: any): I.Socket => {
|
||||
call: (name: string, ...args: any) => {
|
||||
return new Promise((res, rej) => {
|
||||
const params: any = [name, ...args, (resp) => {
|
||||
if(isError(resp)){
|
||||
if (isError(resp)) {
|
||||
const err = new Error()
|
||||
err.stack = resp.stack
|
||||
err.name = resp.name
|
||||
@@ -256,17 +255,15 @@ export const makePioSocket = (socket: any): I.Socket => {
|
||||
}
|
||||
},
|
||||
|
||||
id: socket.id,
|
||||
on: (...args) => socket.on.apply(socket, args),
|
||||
emit: (...args) => socket.emit.apply(socket, args),
|
||||
close: () => {
|
||||
socket
|
||||
socket.disconnect(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const isError = function(e){
|
||||
export const isError = function (e) {
|
||||
return e && e.stack && e.message && typeof e.stack === 'string'
|
||||
&& typeof e.message === 'string';
|
||||
}
|
||||
}
|
||||
|
||||
+158
-41
@@ -1,16 +1,20 @@
|
||||
import { describe, it } from "mocha";
|
||||
import { RPCServer, RPCSocket } from '../Index'
|
||||
import { RPCExporter } from "../src/Interfaces";
|
||||
import { RPCExporter, Socket } from "../src/Interfaces";
|
||||
import { ConnectedSocket } from "../src/Types";
|
||||
import * as log from 'why-is-node-running';
|
||||
import * as http from 'http';
|
||||
import * as express from 'express';
|
||||
import * as fetch from 'node-fetch';
|
||||
import { PromiseIO } from "../src/PromiseIO/Server";
|
||||
import { PromiseIOClient } from "../src/PromiseIO/Client";
|
||||
|
||||
const noop = (...args) => { }
|
||||
|
||||
const add = (...args: number[]) => { return args.reduce((a, b) => a + b, 0) }
|
||||
function makeServer() {
|
||||
function makeServer(onCallback = noop, connectionHandler = noop, hookCloseHandler = noop, closeHandler = noop, errorHandler = (socket, err) => { throw err }) {
|
||||
let subcallback
|
||||
const sv = new RPCServer([{
|
||||
const serv = new RPCServer([{
|
||||
name: 'test',
|
||||
RPCs: [
|
||||
{
|
||||
@@ -22,36 +26,92 @@ function makeServer() {
|
||||
subcallback = callback
|
||||
return { topic: "test" }
|
||||
},
|
||||
onClose: (res) => { }
|
||||
onDestroy: hookCloseHandler
|
||||
}, {
|
||||
name: 'subscribe',
|
||||
hook: async (callback) => {
|
||||
subcallback = callback
|
||||
return { topic: "test" }
|
||||
},
|
||||
onClose: (res, rpc) => {
|
||||
console.log("onClose", rpc.name === 'subscribe' && res ? "OK" : "")
|
||||
subcallback = null
|
||||
},
|
||||
onCallback: (...args: any) => {
|
||||
console.log("onCallback", args[0] === "test" && args[1] === "callback" ? "OK" : "")
|
||||
}
|
||||
onDestroy: hookCloseHandler,
|
||||
onCallback: onCallback
|
||||
},
|
||||
add,
|
||||
function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) },
|
||||
function brokenRPC(){ throw new Error("Intended error") }
|
||||
]
|
||||
}],
|
||||
{
|
||||
connectionHandler: (socket) => {
|
||||
},
|
||||
closeHandler: (socket) => { },
|
||||
errorHandler: (socket, err) => { throw err }
|
||||
connectionHandler: connectionHandler,
|
||||
closeHandler: closeHandler,
|
||||
errorHandler: errorHandler
|
||||
})
|
||||
sv.listen(21010)
|
||||
return sv
|
||||
serv.listen(21010)
|
||||
return serv
|
||||
}
|
||||
|
||||
|
||||
describe('PromiseIO', () => {
|
||||
|
||||
it("bind + fire", (done) => {
|
||||
const server = new PromiseIO()
|
||||
server.attach(new http.Server())
|
||||
server.on("socket", clientSocket => {
|
||||
clientSocket.bind("test123", (p1,p2) => {
|
||||
server.close()
|
||||
if(p1 === "p1" && p2 === "p2")
|
||||
done()
|
||||
})
|
||||
});
|
||||
|
||||
server.listen(21003)
|
||||
PromiseIOClient.connect(21003, "localhost", "http").then(cli => {
|
||||
cli.fire("test123", "p1", "p2")
|
||||
cli.close()
|
||||
})
|
||||
})
|
||||
|
||||
it("hook + call", (done) => {
|
||||
const server = new PromiseIO()
|
||||
server.attach(new http.Server())
|
||||
server.on("socket", clientSocket => {
|
||||
clientSocket.hook("test123", (p1,p2) => {
|
||||
if(p1 === "p1" && p2 === "p2")
|
||||
return "OK"
|
||||
})
|
||||
});
|
||||
|
||||
server.listen(21003)
|
||||
PromiseIOClient.connect(21003, "localhost", "http").then(cli => {
|
||||
cli.call("test123", "p1", "p2").then(resp => {
|
||||
cli.close()
|
||||
server.close()
|
||||
|
||||
if(resp === "OK")
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it("on + emit", (done) => {
|
||||
const server = new PromiseIO()
|
||||
server.attach(new http.Server())
|
||||
server.on("socket", clientSocket => {
|
||||
clientSocket.on("test123", (p1,p2) => {
|
||||
server.close()
|
||||
if(p1 === "p1" && p2 === "p2")
|
||||
done()
|
||||
})
|
||||
});
|
||||
|
||||
server.listen(21003)
|
||||
PromiseIOClient.connect(21003, "localhost", "http").then(cli => {
|
||||
cli.emit("test123", "p1", "p2")
|
||||
cli.close()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('RPCServer', () => {
|
||||
let client, server
|
||||
const echo = (x) => x
|
||||
@@ -133,18 +193,18 @@ describe('RPCServer with premade http server', () => {
|
||||
{
|
||||
name: 'Grp2',
|
||||
RPCs: [
|
||||
function test(){ return "test" }
|
||||
function test() { return "test" }
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
let client:RPCSocket, server:RPCServer
|
||||
let client: RPCSocket, server: RPCServer
|
||||
|
||||
before(done => {
|
||||
const expressServer = express()
|
||||
const httpServer = new http.Server(expressServer)
|
||||
|
||||
expressServer.get('/REST_ping', (req, res)=>{
|
||||
expressServer.get('/REST_ping', (req, res) => {
|
||||
return res
|
||||
.send('REST_pong')
|
||||
.status(200)
|
||||
@@ -171,10 +231,10 @@ describe('RPCServer with premade http server', () => {
|
||||
it('should serve REST', (done) => {
|
||||
fetch('http://localhost:8080/REST_ping').then(response => {
|
||||
response.text().then(text => {
|
||||
if(text === "REST_pong")
|
||||
if (text === "REST_pong")
|
||||
done()
|
||||
else
|
||||
done(new Error("REST repsonse was "+text))
|
||||
done(new Error("REST repsonse was " + text))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -197,6 +257,54 @@ describe('RPCServer with premade http server', () => {
|
||||
})
|
||||
|
||||
|
||||
describe('Serverside Triggers', () => {
|
||||
let server, client
|
||||
const closerFunction = (done) => () => {
|
||||
client.close()
|
||||
server.close()
|
||||
done()
|
||||
}
|
||||
|
||||
it('trigger onCallback', (done) => {
|
||||
server = makeServer(closerFunction(done))
|
||||
client = new RPCSocket(21010, "localhost")
|
||||
client.connect().then(_ => {
|
||||
client['test'].subscribe(noop).then(_ => client['test'].triggerCallback())
|
||||
})
|
||||
})
|
||||
|
||||
it('trigger connectionHandler', (done) => {
|
||||
server = makeServer(undefined, closerFunction(done))
|
||||
client = new RPCSocket(21010, "localhost")
|
||||
client.connect()
|
||||
})
|
||||
|
||||
|
||||
it('trigger hook closeHandler', (done) => {
|
||||
server = makeServer(undefined, undefined, closerFunction(done))
|
||||
client = new RPCSocket(21010, "localhost")
|
||||
client.connect().then(_ => {
|
||||
client['test'].subscribe(function cb(){
|
||||
cb['destroy']()
|
||||
}).then(_ => client['test'].triggerCallback())
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('trigger global closeHandler', (done) => {
|
||||
server = makeServer(undefined, undefined, undefined, () => {
|
||||
server.close()
|
||||
done()
|
||||
})
|
||||
client = new RPCSocket(21010, "localhost")
|
||||
client.connect().then(_ => {
|
||||
client['test'].subscribe(noop).then(_ => client.close())
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
describe('RPCSocket', () => {
|
||||
let client: RPCSocket
|
||||
let server: RPCServer
|
||||
@@ -266,6 +374,7 @@ describe('RPCSocket', () => {
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('It should do unhook', () => {
|
||||
const yesCandy = "OK"
|
||||
const noCandy = "stolen"
|
||||
@@ -306,8 +415,8 @@ describe('It should do unhook', () => {
|
||||
]
|
||||
}],
|
||||
{
|
||||
connectionHandler: (socket) => { },
|
||||
closeHandler: (socket) => { },
|
||||
connectionHandler: noop,
|
||||
closeHandler: noop,
|
||||
errorHandler: (socket, err) => { throw err }
|
||||
})
|
||||
server.listen(21010)
|
||||
@@ -321,7 +430,7 @@ describe('It should do unhook', () => {
|
||||
})
|
||||
|
||||
it('Subscribe with param', (done) => {
|
||||
client['test'].subscribeWithParam("OK", c => { }).then(async (res) => {
|
||||
client['test'].subscribeWithParam("OK", noop).then(async (res) => {
|
||||
if (res.uuid === candy) {
|
||||
done()
|
||||
} else
|
||||
@@ -363,10 +472,6 @@ type SesameTestIfc = {
|
||||
subscribe: (callback: Function) => Promise<topicDTO>
|
||||
manyParams: <A = string, B = number, C = boolean, D = Object>(a: A, b: B, c: C, d: D) => Promise<[A, B, C, D]>
|
||||
}
|
||||
|
||||
other: {
|
||||
echo: (x: any) => Promise<any>
|
||||
}
|
||||
}
|
||||
|
||||
describe('Sesame should unlock the socket', () => {
|
||||
@@ -387,17 +492,11 @@ describe('Sesame should unlock the socket', () => {
|
||||
topic: 'test'
|
||||
}
|
||||
},
|
||||
onClose: (a) => { }
|
||||
onDestroy: noop
|
||||
},
|
||||
async function checkCandy() { cb(candy); cb = () => { }; return candy },
|
||||
async function checkCandy() { cb(candy); cb = noop; return candy },
|
||||
async function manyParams(a, b, c, d) { return [a, b, c, d] }
|
||||
],
|
||||
}, {
|
||||
name: 'other',
|
||||
RPCs: () => [
|
||||
async function echo(x) { return x }
|
||||
]
|
||||
|
||||
}], {
|
||||
sesame: (_sesame) => _sesame === 'sesame!'
|
||||
})
|
||||
@@ -575,7 +674,7 @@ describe("Errorhandler functionality", () => {
|
||||
})
|
||||
.then(r => {
|
||||
if (r != null)
|
||||
done("UNEXPECTED RESULT " + r)
|
||||
done(new Error("UNEXPECTED RESULT " + r))
|
||||
})
|
||||
.catch((e) => {
|
||||
done(new Error("UNEXPECTED CLIENT ERROR " + e.message))
|
||||
@@ -614,11 +713,10 @@ describe("Errorhandler functionality", () => {
|
||||
})
|
||||
.then(r => {
|
||||
if (r != null)
|
||||
done("UNEXPECTED RESULT " + r)
|
||||
done(new Error("UNEXPECTED RESULT " + r))
|
||||
})
|
||||
.catch((e) => {
|
||||
done("UNEXPECTED CLIENT ERROR " + e)
|
||||
done(e)
|
||||
done(new Error("UNEXPECTED CLIENT ERROR " + e))
|
||||
})
|
||||
.finally(() => {
|
||||
cli.close()
|
||||
@@ -772,6 +870,25 @@ describe("attaching handlers before connecting", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("fires error if call is unknown", (done) => {
|
||||
const serv = new RPCServer().listen(21004)
|
||||
const sock = new RPCSocket(21004, 'localhost')
|
||||
|
||||
sock.on('error', (err) => {
|
||||
sock.close()
|
||||
serv.close()
|
||||
done()
|
||||
})
|
||||
|
||||
sock.connect().then(_ => {
|
||||
sock.call("unknownRPC123", "AAAAA").catch(e => { }).then(x => {
|
||||
done(new Error("unexpected return value"))
|
||||
})
|
||||
}).catch(e => {
|
||||
done(e)
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
* ## 1.11.0 breaking ##
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user