Unknown RPC errors, fixed error handler structure, multiple RPCservers on different paths, optional throws
This commit is contained in:
Generated
+8513
-119
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,7 @@
|
|||||||
"http": "0.0.0",
|
"http": "0.0.0",
|
||||||
"socket.io": "^2.3.0",
|
"socket.io": "^2.3.0",
|
||||||
"socket.io-client": "^2.3.0",
|
"socket.io-client": "^2.3.0",
|
||||||
|
"socketio-wildcard": "^2.0.0",
|
||||||
"uuid": "^3.3.3"
|
"uuid": "^3.3.3"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
+39
-16
@@ -8,11 +8,11 @@ import * as I from './Interfaces';
|
|||||||
|
|
||||||
export class RPCServer<
|
export class RPCServer<
|
||||||
InterfaceT extends T.RPCInterface = T.RPCInterface,
|
InterfaceT extends T.RPCInterface = T.RPCInterface,
|
||||||
> {
|
> {
|
||||||
|
|
||||||
private pio = new PromiseIO()
|
private pio = new PromiseIO()
|
||||||
private closeHandler: T.CloseHandler
|
private closeHandler: T.CloseHandler
|
||||||
private errorHandler: T.ErrorHandler
|
private errorHandler: (socket: I.Socket, error: any, rpcName: string, args: any[], forward?: boolean) => void
|
||||||
private connectionHandler: T.ConnectionHandler
|
private connectionHandler: T.ConnectionHandler
|
||||||
private sesame?: T.SesameFunction
|
private sesame?: T.SesameFunction
|
||||||
private accessFilter: T.AccessFilter<InterfaceT>
|
private accessFilter: T.AccessFilter<InterfaceT>
|
||||||
@@ -26,8 +26,12 @@ export class RPCServer<
|
|||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private exporters: T.ExporterArray<InterfaceT> = [],
|
private exporters: T.ExporterArray<InterfaceT> = [],
|
||||||
conf: T.ServerConf<InterfaceT> = {},
|
private conf: T.ServerConf<InterfaceT> = {},
|
||||||
) {
|
) {
|
||||||
|
if (conf.throwOnUnknownRPC == null) {
|
||||||
|
conf.throwOnUnknownRPC = true
|
||||||
|
}
|
||||||
|
|
||||||
if (conf.sesame) {
|
if (conf.sesame) {
|
||||||
this.sesame = U.makeSesameFunction(conf.sesame)
|
this.sesame = U.makeSesameFunction(conf.sesame)
|
||||||
}
|
}
|
||||||
@@ -37,9 +41,15 @@ export class RPCServer<
|
|||||||
return this.sesame!(sesame!)
|
return this.sesame!(sesame!)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.errorHandler = (socket: I.Socket | PromiseIO) => (error: any, rpcName: string, args: any[]) => {
|
this.errorHandler = (socket: I.Socket, error: any, rpcName: string, args: any[], forward: boolean = false) => {
|
||||||
if (conf.errorHandler) conf.errorHandler(socket, error, rpcName, args)
|
if (conf.errorHandler) conf.errorHandler(socket, error, rpcName, args)
|
||||||
else throw error
|
else {
|
||||||
|
if (forward) {
|
||||||
|
socket.call("$UNKNOWNRPC$", error)
|
||||||
|
} else {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closeHandler = (socket: I.Socket) => {
|
this.closeHandler = (socket: I.Socket) => {
|
||||||
@@ -52,7 +62,7 @@ export class RPCServer<
|
|||||||
|
|
||||||
exporters.forEach(U.fixNames) //TSC for some reason doesn't preserve name properties of methods
|
exporters.forEach(U.fixNames) //TSC for some reason doesn't preserve name properties of methods
|
||||||
|
|
||||||
let badRPC = exporters.flatMap(ex => typeof ex.RPCs === "function"?ex.RPCs():(ex as any)).find(rpc => !rpc.name)
|
let badRPC = exporters.flatMap(ex => typeof ex.RPCs === "function" ? ex.RPCs() : (ex as any)).find(rpc => !rpc.name)
|
||||||
if (badRPC) {
|
if (badRPC) {
|
||||||
throw new Error(`
|
throw new Error(`
|
||||||
RPC did not provide a name.
|
RPC did not provide a name.
|
||||||
@@ -66,25 +76,28 @@ export class RPCServer<
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
this.pio.on('socket', (clientSocket: I.Socket) => {
|
this.pio.on('socket', (clientSocket: I.Socket) => {
|
||||||
const sock:any = clientSocket;
|
|
||||||
clientSocket.on('disconnect', () => this.closeHandler(clientSocket))
|
clientSocket.on('disconnect', () => this.closeHandler(clientSocket))
|
||||||
this.connectionHandler(clientSocket)
|
this.connectionHandler(clientSocket)
|
||||||
this.initRPCs(clientSocket)
|
this.initRPCs(clientSocket)
|
||||||
|
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errorHandler(this.pio, e, 'system', [])
|
this.errorHandler(<unknown>undefined as I.Socket, e, 'system', [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public attach = (httpServer = new http.Server()) : RPCServer<InterfaceT> => {
|
public attach = (httpServer = new http.Server(), options?: SocketIO.ServerOptions): RPCServer<InterfaceT> => {
|
||||||
this.pio.attach(httpServer)
|
this.pio.attach(httpServer, options)
|
||||||
this.attached = true
|
this.attached = true
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
public listen(port:number) : RPCServer<InterfaceT>{
|
public listen(port: number, options?: SocketIO.ServerOptions): RPCServer<InterfaceT> {
|
||||||
if(!this.attached) this.attach()
|
if (!this.attached) {
|
||||||
|
this.attach(undefined, options)
|
||||||
|
} else {
|
||||||
|
if (options)
|
||||||
|
console.warn("RPCServer options were passed to listen(..) after attach(..) was called. Please pass them to attach(..) instead. Ignoring.")
|
||||||
|
}
|
||||||
this.pio.listen(port)
|
this.pio.listen(port)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
@@ -94,12 +107,22 @@ export class RPCServer<
|
|||||||
const rpcs = await Promise.all(this.exporters.map(async exp => {
|
const rpcs = await Promise.all(this.exporters.map(async exp => {
|
||||||
const allowed = await this.accessFilter(sesame, exp)
|
const allowed = await this.accessFilter(sesame, exp)
|
||||||
if (!allowed) return []
|
if (!allowed) return []
|
||||||
const infos = U.rpcHooker(clientSocket, exp, this.errorHandler, this.sesame)
|
return U.rpcHooker(clientSocket, exp, this.errorHandler, this.sesame)
|
||||||
return infos
|
|
||||||
}))
|
}))
|
||||||
return rpcs.flat()
|
const infos = rpcs.flat()
|
||||||
|
if (this.conf.throwOnUnknownRPC) {
|
||||||
|
clientSocket.on("*", (packet) => {
|
||||||
|
if (!infos.some(i => i.uniqueName === packet.data[0])) {
|
||||||
|
if (packet.data[0].startsWith('destroy_')) return
|
||||||
|
this.errorHandler(clientSocket, new Error(`Unknown RPC ${packet.data[0]}`), packet.data[0], [...packet.data].splice(1), true)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
return infos
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
close(): void {
|
close(): void {
|
||||||
this.pio.close()
|
this.pio.close()
|
||||||
|
|||||||
+47
-47
@@ -1,6 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
import { PromiseIOClient } from './PromiseIO/Client'
|
import { PromiseIOClient, defaultClientConfig } from './PromiseIO/Client'
|
||||||
import * as T from './Types';
|
import * as T from './Types';
|
||||||
import * as I from './Interfaces';
|
import * as I from './Interfaces';
|
||||||
import { stripAfterEquals, appendComma } from './Utils';
|
import { stripAfterEquals, appendComma } from './Utils';
|
||||||
@@ -9,22 +9,21 @@ import { stripAfterEquals, appendComma } from './Utils';
|
|||||||
/**
|
/**
|
||||||
* A websocket-on-steroids with built-in RPC capabilities
|
* A websocket-on-steroids with built-in RPC capabilities
|
||||||
*/
|
*/
|
||||||
export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I.Socket{
|
export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I.Socket {
|
||||||
|
|
||||||
static async makeSocket<T extends T.RPCInterface = T.RPCInterface>(port:number, server: string, sesame?:string, conf?:T.SocketConf): Promise<T.ConnectedSocket<T>> {
|
static async makeSocket<T extends T.RPCInterface = T.RPCInterface>(port: number, server: string, sesame?: string, conf: T.ClientConfig = defaultClientConfig): Promise<T.ConnectedSocket<T>> {
|
||||||
const socket = new RPCSocket<T>(port, server, conf)
|
const socket = new RPCSocket<T>(port, server, conf)
|
||||||
return await socket.connect(sesame)
|
return await socket.connect(sesame)
|
||||||
}
|
}
|
||||||
|
|
||||||
private protocol: 'http' | 'https'
|
|
||||||
private socket: I.Socket
|
private socket: I.Socket
|
||||||
private handlers : {
|
private handlers: {
|
||||||
[name in string]: T.AnyFunction[]
|
[name in string]: T.AnyFunction[]
|
||||||
} = {
|
} = {
|
||||||
error: [],
|
error: [],
|
||||||
close: []
|
close: []
|
||||||
}
|
}
|
||||||
private hooks : {[name in string]: T.AnyFunction} = {}
|
private hooks: { [name in string]: T.AnyFunction } = {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -32,9 +31,9 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @param server Server address
|
* @param server Server address
|
||||||
* @param tls @default false use TLS
|
* @param tls @default false use TLS
|
||||||
*/
|
*/
|
||||||
constructor(public port:number, public address: string, conf:T.SocketConf = { tls: false }){
|
constructor(public port: number, public address: string, private conf: T.ClientConfig = defaultClientConfig) {
|
||||||
Object.defineProperty(this, 'socket', {value: undefined, writable: true})
|
Object.defineProperty(this, 'socket', { value: undefined, writable: true })
|
||||||
this.protocol = conf.tls ? "https" : "http"
|
this.hook("$UNKNOWNRPC$", (err) => this.handlers['error'].forEach(handler => handler(err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,10 +41,10 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @param name The function name to listen on
|
* @param name The function name to listen on
|
||||||
* @param handler The handler to attach
|
* @param handler The handler to attach
|
||||||
*/
|
*/
|
||||||
public hook(name: string, handler: (...args:any[]) => any | Promise<any>){
|
public hook(name: string, handler: (...args: any[]) => any | Promise<any>) {
|
||||||
if(!this.socket){
|
if (!this.socket) {
|
||||||
this.hooks[name] = handler
|
this.hooks[name] = handler
|
||||||
}else{
|
} else {
|
||||||
this.socket.hook(name, handler)
|
this.socket.hook(name, handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,10 +54,10 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @param name The function name to listen on
|
* @param name The function name to listen on
|
||||||
* @param handler The handler to attach
|
* @param handler The handler to attach
|
||||||
*/
|
*/
|
||||||
public bind(name: string, handler: (...args:any[]) => any | Promise<any>){
|
public bind(name: string, handler: (...args: any[]) => any | Promise<any>) {
|
||||||
if(!this.socket){
|
if (!this.socket) {
|
||||||
this.hooks[name] = handler
|
this.hooks[name] = handler
|
||||||
}else{
|
} else {
|
||||||
this.socket.bind(name, handler)
|
this.socket.bind(name, handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,10 +66,10 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* Removes a {@link hook} listener by name.
|
* Removes a {@link hook} listener by name.
|
||||||
* @param name The function name
|
* @param name The function name
|
||||||
*/
|
*/
|
||||||
public unhook(name: string){
|
public unhook(name: string) {
|
||||||
if(!this.socket){
|
if (!this.socket) {
|
||||||
delete this.hooks[name]
|
delete this.hooks[name]
|
||||||
}else{
|
} else {
|
||||||
this.socket.unhook(name)
|
this.socket.unhook(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,13 +79,13 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @param type 'error' or 'close'
|
* @param type 'error' or 'close'
|
||||||
* @param f The listener to attach
|
* @param f The listener to attach
|
||||||
*/
|
*/
|
||||||
public on(type: string, f: T.AnyFunction){
|
public on(type: string, f: T.AnyFunction) {
|
||||||
if(!this.socket){
|
if (!this.socket) {
|
||||||
if(!this.handlers[type])
|
if (!this.handlers[type])
|
||||||
this.handlers[type] = []
|
this.handlers[type] = []
|
||||||
|
|
||||||
this.handlers[type].push(f)
|
this.handlers[type].push(f)
|
||||||
}else{
|
} else {
|
||||||
this.socket.on(type, f)
|
this.socket.on(type, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,16 +95,16 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @param eventName The event name to emit under
|
* @param eventName The event name to emit under
|
||||||
* @param data The data the event carries
|
* @param data The data the event carries
|
||||||
*/
|
*/
|
||||||
public emit(eventName:string, data:any){
|
public emit(eventName: string, data: any) {
|
||||||
if(!this.socket) return
|
if (!this.socket) return
|
||||||
this.socket.emit(eventName, data)
|
this.socket.emit(eventName, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the socket. It may attempt to reconnect.
|
* Closes the socket. It may attempt to reconnect.
|
||||||
*/
|
*/
|
||||||
public close(){
|
public close() {
|
||||||
if(!this.socket) return;
|
if (!this.socket) return;
|
||||||
this.socket.close()
|
this.socket.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,12 +113,13 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @param rpcname The function to call
|
* @param rpcname The function to call
|
||||||
* @param args other arguments
|
* @param args other arguments
|
||||||
*/
|
*/
|
||||||
public async call (rpcname: string, ...args: any[]) : Promise<any>{
|
public async call(rpcname: string, ...args: any[]): Promise<any> {
|
||||||
if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
|
if (!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
|
||||||
try{
|
|
||||||
|
try {
|
||||||
const val = await this.socket.call.apply(this.socket, [rpcname, ...args])
|
const val = await this.socket.call.apply(this.socket, [rpcname, ...args])
|
||||||
return val
|
return val
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
this.emit('error', e)
|
this.emit('error', e)
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
@@ -130,31 +130,31 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @param rpcname The function to call
|
* @param rpcname The function to call
|
||||||
* @param args other arguments
|
* @param args other arguments
|
||||||
*/
|
*/
|
||||||
public async fire(rpcname: string, ...args: any[]) : Promise<void>{
|
public async fire(rpcname: string, ...args: any[]): Promise<void> {
|
||||||
if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
|
if (!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
|
||||||
await this.socket.fire.apply(this.socket, [rpcname, ...args])
|
await this.socket.fire.apply(this.socket, [rpcname, ...args])
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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( sesame?: string ) : Promise<T.ConnectedSocket<Ifc>> {
|
public async connect(sesame?: string): Promise<T.ConnectedSocket<Ifc>> {
|
||||||
|
|
||||||
try{
|
try {
|
||||||
this.socket = await PromiseIOClient.connect(this.port, this.address, this.protocol)
|
this.socket = await PromiseIOClient.connect(this.port, this.address, this.conf)
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
this.handlers['error'].forEach(h => h(e))
|
this.handlers['error'].forEach(h => h(e))
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.entries(this.handlers).forEach(([k,v])=>{
|
Object.entries(this.handlers).forEach(([k, v]) => {
|
||||||
v.forEach(h => this.socket.on(k, h))
|
v.forEach(h => this.socket.on(k, h))
|
||||||
})
|
})
|
||||||
|
|
||||||
Object.entries(this.hooks).forEach((kv: [string, T.AnyFunction]) => {
|
Object.entries(this.hooks).forEach((kv: [string, T.AnyFunction]) => {
|
||||||
this.socket.hook(kv[0], kv[1])
|
this.socket.hook(kv[0], kv[1])
|
||||||
})
|
})
|
||||||
const info:T.ExtendedRpcInfo[] = await this.info(sesame)
|
const info: T.ExtendedRpcInfo[] = await this.info(sesame)
|
||||||
|
|
||||||
info.forEach(i => {
|
info.forEach(i => {
|
||||||
let f: any
|
let f: any
|
||||||
@@ -167,21 +167,21 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
f = this.frontEndHookGenerator(i.uniqueName, i.argNames, sesame)
|
f = this.frontEndHookGenerator(i.uniqueName, i.argNames, sesame)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if(this[i.owner] == null)
|
if (this[i.owner] == null)
|
||||||
this[i.owner] = {}
|
this[i.owner] = {}
|
||||||
this[i.owner][i.name] = f
|
this[i.owner][i.name] = f
|
||||||
this[i.owner][i.name].bind(this)
|
this[i.owner][i.name].bind(this)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
return <T.ConnectedSocket<Ifc>> (this as any)
|
return <T.ConnectedSocket<Ifc>>(this as any)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of available RPCs from the server
|
* Get a list of available RPCs from the server
|
||||||
*/
|
*/
|
||||||
public async info(sesame?:string){
|
public async info(sesame?: string) {
|
||||||
if(!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
|
if (!this.socket) throw new Error("The socket is not connected! Use socket.connect() first")
|
||||||
return await this.socket.call('info', sesame)
|
return await this.socket.call('info', sesame)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @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[], sesame?: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(",")
|
||||||
sesame = appendComma(sesame)
|
sesame = appendComma(sesame)
|
||||||
@@ -205,15 +205,15 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
* @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 frontEndHookGenerator(fnName: string, fnArgs:string[], sesame?:string): T.HookFunction{
|
private frontEndHookGenerator(fnName: string, fnArgs: string[], sesame?: string): T.HookFunction {
|
||||||
|
|
||||||
if(sesame)
|
if (sesame)
|
||||||
fnArgs.shift()
|
fnArgs.shift()
|
||||||
|
|
||||||
let headerArgs = fnArgs.join(",")
|
let headerArgs = fnArgs.join(",")
|
||||||
const argParams = fnArgs.map(stripAfterEquals).join(",")
|
const argParams = fnArgs.map(stripAfterEquals).join(",")
|
||||||
sesame = appendComma(sesame, true)
|
sesame = appendComma(sesame, true)
|
||||||
headerArgs = fnArgs.length>0?headerArgs+",":headerArgs
|
headerArgs = fnArgs.length > 0 ? headerArgs + "," : headerArgs
|
||||||
|
|
||||||
const frontendHookStr = `
|
const frontendHookStr = `
|
||||||
async (${headerArgs} $__callback__$) => {
|
async (${headerArgs} $__callback__$) => {
|
||||||
@@ -222,7 +222,7 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
|
|||||||
if(r){
|
if(r){
|
||||||
if(r.uuid){
|
if(r.uuid){
|
||||||
$__callback__$['destroy'] = () => {
|
$__callback__$['destroy'] = () => {
|
||||||
this.socket.fire(r.uuid)
|
this.socket.fire('destroy_'+r.uuid)
|
||||||
this.socket.unhook(r.uuid)
|
this.socket.unhook(r.uuid)
|
||||||
}
|
}
|
||||||
this.socket.hook(r.uuid, $__callback__$)
|
this.socket.hook(r.uuid, $__callback__$)
|
||||||
|
|||||||
+15
-7
@@ -2,18 +2,26 @@ import { Socket } from "socket.io"
|
|||||||
import * as U from '../Utils'
|
import * as U from '../Utils'
|
||||||
import * as I from '../Interfaces'
|
import * as I from '../Interfaces'
|
||||||
import * as socketio from 'socket.io-client'
|
import * as socketio from 'socket.io-client'
|
||||||
|
import { ClientConfig } from "../Types"
|
||||||
|
|
||||||
export class PromiseIOClient {
|
export const defaultClientConfig: ClientConfig = {
|
||||||
|
protocol: 'http',
|
||||||
static connect = (port: number, host = "localhost", protocol : 'http' | 'https' = "http"): Promise<I.Socket> => new Promise((res, rej) => {
|
|
||||||
try {
|
|
||||||
const address = `${host}:${port}`
|
|
||||||
const socket = socketio(`${protocol}://${address}`, {
|
|
||||||
reconnectionAttempts: 2,
|
reconnectionAttempts: 2,
|
||||||
reconnectionDelay: 200,
|
reconnectionDelay: 200,
|
||||||
timeout: 450,
|
timeout: 450,
|
||||||
reconnection: false,
|
reconnection: false,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
export class PromiseIOClient {
|
||||||
|
|
||||||
|
static connect = (port: number, host = "localhost", options : ClientConfig = defaultClientConfig): Promise<I.Socket> => new Promise((res, rej) => {
|
||||||
|
try {
|
||||||
|
if(options.path && !options.path.startsWith('/')){
|
||||||
|
options.path = "/"+options.path
|
||||||
|
}
|
||||||
|
|
||||||
|
const address = `${host}:${port}`
|
||||||
|
const socket = socketio(`${options.protocol?options.protocol:'http'}://${address}`, options)
|
||||||
|
|
||||||
socket.on('connect_error', e => {
|
socket.on('connect_error', e => {
|
||||||
sock.emit('error', e)
|
sock.emit('error', e)
|
||||||
|
|||||||
+12
-7
@@ -3,7 +3,12 @@ import { Server as httpServer } from "http"
|
|||||||
import * as U from '../Utils'
|
import * as U from '../Utils'
|
||||||
import * as T from '../Types'
|
import * as T from '../Types'
|
||||||
import socketio = require('socket.io')
|
import socketio = require('socket.io')
|
||||||
|
import middleware = require('socketio-wildcard');
|
||||||
|
|
||||||
|
const defaultConfig : socketio.ServerOptions = {
|
||||||
|
cookie: false,
|
||||||
|
path: '/socket.io',
|
||||||
|
}
|
||||||
|
|
||||||
export class PromiseIO {
|
export class PromiseIO {
|
||||||
io?: Server
|
io?: Server
|
||||||
@@ -13,20 +18,20 @@ export class PromiseIO {
|
|||||||
connect: []
|
connect: []
|
||||||
}
|
}
|
||||||
|
|
||||||
attach(httpServer: httpServer) {
|
attach(httpServer: httpServer, options: socketio.ServerOptions = defaultConfig) {
|
||||||
|
if(options.path && !options.path.startsWith('/')){
|
||||||
|
options.path = "/"+options.path
|
||||||
|
}
|
||||||
this.httpServer = httpServer
|
this.httpServer = httpServer
|
||||||
this.io = socketio(httpServer, { cookie:false })
|
this.io = socketio(httpServer, options)
|
||||||
|
this.io!.use(middleware())
|
||||||
this.io!.on('connection', (clientSocket: Socket) => {
|
this.io!.on('connection', (clientSocket: Socket) => {
|
||||||
clientSocket.use((packet, next) => {
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
clientSocket['address'] = clientSocket.handshake.headers["x-real-ip"] || clientSocket.handshake.address
|
clientSocket['address'] = clientSocket.handshake.headers["x-real-ip"] || clientSocket.handshake.address
|
||||||
const pioSock = U.makePioSocket(clientSocket)
|
const pioSock = U.makePioSocket(clientSocket)
|
||||||
this.listeners['socket'].forEach(listener => listener(pioSock))
|
this.listeners['socket'].forEach(listener => listener(pioSock))
|
||||||
this.listeners['connect'].forEach(listener => listener(pioSock))
|
this.listeners['connect'].forEach(listener => listener(pioSock))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
pioSock.on('error', ()=>console.log('error'));
|
pioSock.on('error', ()=>console.log('error'));
|
||||||
|
|
||||||
|
|||||||
+5
-6
@@ -10,7 +10,7 @@ export type HookFunction = AnyFunction
|
|||||||
export type AccessFilter<InterfaceT extends RPCInterface = RPCInterface> = (sesame:string|undefined, exporter: I.RPCExporter<InterfaceT, keyof InterfaceT>) => Promise<boolean> | boolean
|
export type AccessFilter<InterfaceT extends RPCInterface = RPCInterface> = (sesame:string|undefined, exporter: I.RPCExporter<InterfaceT, keyof InterfaceT>) => Promise<boolean> | boolean
|
||||||
export type Visibility = "127.0.0.1" | "0.0.0.0"
|
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 | PromiseIO, error:any, rpcName: string, args: any[]) => void
|
export type ErrorHandler = (socket:I.Socket, error:any, rpcName: string, args: any[]) => void
|
||||||
export type CloseHandler = (socket:I.Socket) => void
|
export type CloseHandler = (socket:I.Socket) => void
|
||||||
export type SesameFunction = (sesame : string) => boolean
|
export type SesameFunction = (sesame : string) => boolean
|
||||||
export type SesameConf = {
|
export type SesameConf = {
|
||||||
@@ -20,7 +20,9 @@ export type FrontEndHandlerType = {
|
|||||||
'error' : (e: any) => void
|
'error' : (e: any) => void
|
||||||
'close' : () => void
|
'close' : () => void
|
||||||
}
|
}
|
||||||
|
export type ClientConfig = SocketIOClient.ConnectOpts & {
|
||||||
|
protocol?: 'http' | 'https'
|
||||||
|
}
|
||||||
export type ExporterArray<InterfaceT extends RPCInterface = RPCInterface> = I.RPCExporter<RPCInterface<InterfaceT>, keyof InterfaceT>[]
|
export type ExporterArray<InterfaceT extends RPCInterface = RPCInterface> = I.RPCExporter<RPCInterface<InterfaceT>, keyof InterfaceT>[]
|
||||||
|
|
||||||
export type ConnectedSocket<T extends RPCInterface = RPCInterface> = RPCSocket & AsyncIfc<T>
|
export type ConnectedSocket<T extends RPCInterface = RPCInterface> = RPCSocket & AsyncIfc<T>
|
||||||
@@ -30,12 +32,9 @@ export type ServerConf<InterfaceT extends RPCInterface> = {
|
|||||||
connectionHandler?: ConnectionHandler
|
connectionHandler?: ConnectionHandler
|
||||||
errorHandler?: ErrorHandler
|
errorHandler?: ErrorHandler
|
||||||
closeHandler?: CloseHandler
|
closeHandler?: CloseHandler
|
||||||
|
throwOnUnknownRPC?: boolean
|
||||||
} & SesameConf
|
} & SesameConf
|
||||||
|
|
||||||
export type SocketConf = {
|
|
||||||
tls:boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ResponseType = "Subscribe" | "Success" | "Error"
|
export type ResponseType = "Subscribe" | "Success" | "Error"
|
||||||
export type Outcome = "Success" | "Error"
|
export type Outcome = "Success" | "Error"
|
||||||
|
|
||||||
|
|||||||
+12
-10
@@ -56,22 +56,22 @@ RPC did not provide a name.
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility function to apply the RPCs of an {@link RPCExporter}.
|
* Utility function to apply the RPCs of an {@link RPCExporter}.
|
||||||
* @param serverSocket The websocket (implementation: socket.io) to hook on
|
* @param socket The websocket (implementation: socket.io) to hook on
|
||||||
* @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(serverSocket: I.Socket, exporter: I.RPCExporter<any, any>, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction, makeUnique = true): T.ExtendedRpcInfo[] {
|
export function rpcHooker(socket: I.Socket, exporter: I.RPCExporter<any, any>, errorHandler: T.ErrorHandler, sesame?: T.SesameFunction, makeUnique = true): T.ExtendedRpcInfo[] {
|
||||||
const owner = exporter.name
|
const owner = exporter.name
|
||||||
const RPCs = typeof exporter.RPCs === "function" ? exporter.RPCs() : exporter.RPCs
|
const RPCs = typeof exporter.RPCs === "function" ? exporter.RPCs() : exporter.RPCs
|
||||||
|
|
||||||
return RPCs
|
return RPCs
|
||||||
.map(rpc => rpcToRpcinfo(serverSocket, rpc, owner, errorHandler, sesame))
|
.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame))
|
||||||
.map(info => {
|
.map(info => {
|
||||||
const suffix = makeUnique ? "-" + uuidv4().substr(0, 4) : ""
|
const suffix = makeUnique ? "-" + uuidv4().substr(0, 4) : ""
|
||||||
const ret: any = info
|
const ret: any = info
|
||||||
ret.uniqueName = info.name + suffix
|
ret.uniqueName = info.name + suffix
|
||||||
let rpcFunction = info.type === 'Hook' ? info.generator(serverSocket) : info.call
|
let rpcFunction = info.type === 'Hook' ? info.generator(socket) : info.call
|
||||||
serverSocket.hook(ret.uniqueName, callGenerator(info.name, serverSocket, rpcFunction, errorHandler))
|
socket.hook(ret.uniqueName, callGenerator(info.name, socket, rpcFunction, errorHandler))
|
||||||
return ret
|
return ret
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ const callGenerator = (rpcName: string, $__socket__$: I.Socket, rpcFunction: T.A
|
|||||||
try{
|
try{
|
||||||
return await rpcFunction(${argsStr})
|
return await rpcFunction(${argsStr})
|
||||||
}catch(e){
|
}catch(e){
|
||||||
errorHandler($__socket__$)(e, rpcName, [${args}])
|
errorHandler($__socket__$, e, rpcName, [${args}])
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
@@ -128,13 +128,13 @@ const hookGenerator = (rpc: T.HookRPC<any, any>, errorHandler: T.ErrorHandler, s
|
|||||||
${rpc.onCallback ? `rpc.onCallback.apply({}, cbargs)` : ``}
|
${rpc.onCallback ? `rpc.onCallback.apply({}, cbargs)` : ``}
|
||||||
$__socket__$.call.apply($__socket__$, [uuid, ...cbargs])
|
$__socket__$.call.apply($__socket__$, [uuid, ...cbargs])
|
||||||
})
|
})
|
||||||
${rpc.onDestroy ? `$__socket__$.bind(uuid, () => {
|
${rpc.onDestroy ? `$__socket__$.bind('destroy_'+uuid, () => {
|
||||||
rpc.onDestroy(res, rpc)
|
rpc.onDestroy(res, rpc)
|
||||||
})` : ``}
|
})` : ``}
|
||||||
return {'uuid': uuid, 'return': res}
|
return {'uuid': uuid, 'return': res}
|
||||||
}catch(e){
|
}catch(e){
|
||||||
//can throw to pass exception to client or swallow to keep it local
|
//can throw to pass exception to client or swallow to keep it local
|
||||||
errorHandler($__socket__$)(e, ${rpc.name}, [${args}])
|
errorHandler($__socket__$, e, ${rpc.name}, [${args}])
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
@@ -194,7 +194,9 @@ export function fixNames(o: Object): void {
|
|||||||
export const makePioSocket = (socket: any): I.Socket => {
|
export const makePioSocket = (socket: any): I.Socket => {
|
||||||
return <I.Socket>{
|
return <I.Socket>{
|
||||||
id: socket.id,
|
id: socket.id,
|
||||||
bind: (name: string, listener: T.PioBindListener) => socket.on(name, (...args: any) => listener.apply(null, args)),
|
bind: (name: string, listener: T.PioBindListener) => {
|
||||||
|
socket.on(name, (...args: any) => listener.apply(null, args))
|
||||||
|
},
|
||||||
|
|
||||||
hook: (name: string, listener: T.PioHookListener) => {
|
hook: (name: string, listener: T.PioHookListener) => {
|
||||||
const args = extractArgs(listener)
|
const args = extractArgs(listener)
|
||||||
@@ -244,7 +246,7 @@ export const makePioSocket = (socket: any): I.Socket => {
|
|||||||
fire: (name: string, ...args: any) => new Promise((res, rej) => {
|
fire: (name: string, ...args: any) => new Promise((res, rej) => {
|
||||||
const params: any = [name, ...args]
|
const params: any = [name, ...args]
|
||||||
socket.emit.apply(socket, params)
|
socket.emit.apply(socket, params)
|
||||||
res()
|
res(undefined)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
unhook: (name: string, listener?: T.AnyFunction) => {
|
unhook: (name: string, listener?: T.AnyFunction) => {
|
||||||
|
|||||||
+213
-77
@@ -12,7 +12,7 @@ import { PromiseIOClient } from "../src/PromiseIO/Client";
|
|||||||
const noop = (...args) => { }
|
const noop = (...args) => { }
|
||||||
|
|
||||||
const add = (...args: number[]) => { return args.reduce((a, b) => a + b, 0) }
|
const add = (...args: number[]) => { return args.reduce((a, b) => a + b, 0) }
|
||||||
function makeServer(onCallback = noop, connectionHandler = noop, hookCloseHandler = noop, closeHandler = noop, errorHandler = (socket, err) => { throw err }) {
|
function makeServer(onCallback = noop, connectionHandler = noop, hookCloseHandler = noop, closeHandler = noop, errorHandler = noop) {
|
||||||
let subcallback
|
let subcallback
|
||||||
const serv = new RPCServer([{
|
const serv = new RPCServer([{
|
||||||
name: 'test',
|
name: 'test',
|
||||||
@@ -38,7 +38,7 @@ function makeServer(onCallback = noop, connectionHandler = noop, hookCloseHandle
|
|||||||
},
|
},
|
||||||
add,
|
add,
|
||||||
function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) },
|
function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) },
|
||||||
function brokenRPC(){ throw new Error("Intended error") }
|
function brokenRPC() { throw new Error("Intended error") }
|
||||||
]
|
]
|
||||||
}],
|
}],
|
||||||
{
|
{
|
||||||
@@ -57,15 +57,15 @@ describe('PromiseIO', () => {
|
|||||||
const server = new PromiseIO()
|
const server = new PromiseIO()
|
||||||
server.attach(new http.Server())
|
server.attach(new http.Server())
|
||||||
server.on("socket", clientSocket => {
|
server.on("socket", clientSocket => {
|
||||||
clientSocket.bind("test123", (p1,p2) => {
|
clientSocket.bind("test123", (p1, p2) => {
|
||||||
server.close()
|
server.close()
|
||||||
if(p1 === "p1" && p2 === "p2")
|
if (p1 === "p1" && p2 === "p2")
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(21003)
|
server.listen(21003)
|
||||||
PromiseIOClient.connect(21003, "localhost", "http").then(cli => {
|
PromiseIOClient.connect(21003, "localhost", { protocol: 'http' }).then(cli => {
|
||||||
cli.fire("test123", "p1", "p2")
|
cli.fire("test123", "p1", "p2")
|
||||||
cli.close()
|
cli.close()
|
||||||
})
|
})
|
||||||
@@ -75,19 +75,19 @@ describe('PromiseIO', () => {
|
|||||||
const server = new PromiseIO()
|
const server = new PromiseIO()
|
||||||
server.attach(new http.Server())
|
server.attach(new http.Server())
|
||||||
server.on("socket", clientSocket => {
|
server.on("socket", clientSocket => {
|
||||||
clientSocket.hook("test123", (p1,p2) => {
|
clientSocket.hook("test123", (p1, p2) => {
|
||||||
if(p1 === "p1" && p2 === "p2")
|
if (p1 === "p1" && p2 === "p2")
|
||||||
return "OK"
|
return "OK"
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(21003)
|
server.listen(21003)
|
||||||
PromiseIOClient.connect(21003, "localhost", "http").then(cli => {
|
PromiseIOClient.connect(21003, "localhost", { protocol: 'http' }).then(cli => {
|
||||||
cli.call("test123", "p1", "p2").then(resp => {
|
cli.call("test123", "p1", "p2").then(resp => {
|
||||||
cli.close()
|
cli.close()
|
||||||
server.close()
|
server.close()
|
||||||
|
|
||||||
if(resp === "OK")
|
if (resp === "OK")
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -97,15 +97,15 @@ describe('PromiseIO', () => {
|
|||||||
const server = new PromiseIO()
|
const server = new PromiseIO()
|
||||||
server.attach(new http.Server())
|
server.attach(new http.Server())
|
||||||
server.on("socket", clientSocket => {
|
server.on("socket", clientSocket => {
|
||||||
clientSocket.on("test123", (p1,p2) => {
|
clientSocket.on("test123", (p1, p2) => {
|
||||||
server.close()
|
server.close()
|
||||||
if(p1 === "p1" && p2 === "p2")
|
if (p1 === "p1" && p2 === "p2")
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(21003)
|
server.listen(21003)
|
||||||
PromiseIOClient.connect(21003, "localhost", "http").then(cli => {
|
PromiseIOClient.connect(21003, "localhost", { protocol: 'http' }).then(cli => {
|
||||||
cli.emit("test123", "p1", "p2")
|
cli.emit("test123", "p1", "p2")
|
||||||
cli.close()
|
cli.close()
|
||||||
})
|
})
|
||||||
@@ -256,6 +256,194 @@ describe('RPCServer with premade http server', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('should be able to attach to non-standard path', () => {
|
||||||
|
let client: RPCSocket, server: RPCServer
|
||||||
|
const echo = (x) => x
|
||||||
|
|
||||||
|
before(done => {
|
||||||
|
server = new RPCServer([{
|
||||||
|
name: 'HelloWorldRPCGroup',
|
||||||
|
RPCs: () => [
|
||||||
|
echo, //named function variable
|
||||||
|
function echof(x) { return x }, //named function
|
||||||
|
{
|
||||||
|
name: 'echoExplicit', //describing object
|
||||||
|
call: async (x, y, z) => [x, y, z]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}])
|
||||||
|
server.listen(21003, {path: '/test'})
|
||||||
|
client = new RPCSocket(21003, 'localhost', {path: '/test'})
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
after(done => {
|
||||||
|
client.close()
|
||||||
|
server.close()
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be able to use all kinds of RPC definitions', (done) => {
|
||||||
|
client.connect().then(async () => {
|
||||||
|
const r0 = await client['HelloWorldRPCGroup'].echo('Hello')
|
||||||
|
const r1 = await client['HelloWorldRPCGroup'].echof('World')
|
||||||
|
const r2 = await client['HelloWorldRPCGroup'].echoExplicit('R', 'P', 'C!')
|
||||||
|
|
||||||
|
|
||||||
|
if (r0 === 'Hello' && r1 === 'World' && r2.join('') === 'RPC!') {
|
||||||
|
done()
|
||||||
|
} else {
|
||||||
|
done(new Error("Bad response"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
describe('can attach multiple RPCServers to same http server', () => {
|
||||||
|
const echo = (x) => x
|
||||||
|
const RPCs = [
|
||||||
|
echo, //named function variable
|
||||||
|
function echof(x) { return x }, //named function
|
||||||
|
{
|
||||||
|
name: 'echoExplicit', //describing object
|
||||||
|
call: async (x, y, z) => [x, y, z]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const RPCExporters = [
|
||||||
|
{
|
||||||
|
name: 'HelloWorldRPCGroup',
|
||||||
|
RPCs: RPCs,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const RPCExporters2 = [
|
||||||
|
{
|
||||||
|
name: 'Grp2',
|
||||||
|
RPCs: [
|
||||||
|
function test() { return "/test" }
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
let client: RPCSocket, client2: RPCSocket, server: RPCServer, server2: RPCServer
|
||||||
|
|
||||||
|
before(done => {
|
||||||
|
const expressServer = express()
|
||||||
|
const httpServer = new http.Server(expressServer)
|
||||||
|
|
||||||
|
server = new RPCServer(
|
||||||
|
RPCExporters,
|
||||||
|
)
|
||||||
|
server2 = new RPCServer(
|
||||||
|
RPCExporters2
|
||||||
|
)
|
||||||
|
|
||||||
|
server.attach(httpServer)
|
||||||
|
server2.attach(httpServer, {
|
||||||
|
path: "test"
|
||||||
|
})
|
||||||
|
|
||||||
|
httpServer.listen(8080)
|
||||||
|
|
||||||
|
new RPCSocket(8080, 'localhost').connect().then(sock => {
|
||||||
|
client = sock
|
||||||
|
new RPCSocket(8080, 'localhost', { path: "test" }).connect().then(sock2 => {
|
||||||
|
client2 = sock2
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
after(done => {
|
||||||
|
client.close()
|
||||||
|
client2.close()
|
||||||
|
server.close()
|
||||||
|
server2.close()
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('both servers should answer', (done) => {
|
||||||
|
client['HelloWorldRPCGroup'].echo("test").then(res => {
|
||||||
|
if(res != "test"){
|
||||||
|
done(new Error("response was "+res))
|
||||||
|
}else{
|
||||||
|
client2['Grp2'].test().then(res => {
|
||||||
|
if(res != "/test"){
|
||||||
|
done(new Error("response2 was "+res))
|
||||||
|
}else{
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("can attach second RPCServer if first is already running", () => {
|
||||||
|
|
||||||
|
const RPCExporters = [
|
||||||
|
{
|
||||||
|
name: 'HelloWorldRPCGroup',
|
||||||
|
RPCs: [
|
||||||
|
function echo (x) { return x}, //named function variable
|
||||||
|
function echof(x) { return x }, //named function
|
||||||
|
{
|
||||||
|
name: 'echoExplicit', //describing object
|
||||||
|
call: async (x, y, z) => [x, y, z]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const RPCExporters2 = [
|
||||||
|
{
|
||||||
|
name: 'Grp2',
|
||||||
|
RPCs: [
|
||||||
|
function test() { return "/test" }
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
it("attaches correctly", done => {
|
||||||
|
const expressServer = express()
|
||||||
|
const httpServer = new http.Server(expressServer)
|
||||||
|
|
||||||
|
const server = new RPCServer(
|
||||||
|
RPCExporters,
|
||||||
|
)
|
||||||
|
const server2 = new RPCServer(
|
||||||
|
RPCExporters2
|
||||||
|
)
|
||||||
|
|
||||||
|
server.attach(httpServer)
|
||||||
|
|
||||||
|
httpServer.listen(8080)
|
||||||
|
server2.attach(httpServer, {
|
||||||
|
path: "test"
|
||||||
|
})
|
||||||
|
|
||||||
|
new RPCSocket(8080, 'localhost').connect().then(sock => {
|
||||||
|
new RPCSocket(8080, 'localhost', { path: "test" }).connect().then(sock2 => {
|
||||||
|
sock2.Grp2.test().then(resp => {
|
||||||
|
if(resp === "/test")
|
||||||
|
done()
|
||||||
|
else
|
||||||
|
done(new Error("response did not match"))
|
||||||
|
|
||||||
|
server.close()
|
||||||
|
server2.close()
|
||||||
|
sock.close()
|
||||||
|
sock2.close()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('Serverside Triggers', () => {
|
describe('Serverside Triggers', () => {
|
||||||
let server, client
|
let server, client
|
||||||
@@ -273,18 +461,19 @@ describe('Serverside Triggers', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/* testing framework has trouble terminating on this one
|
||||||
it('trigger connectionHandler', (done) => {
|
it('trigger connectionHandler', (done) => {
|
||||||
server = makeServer(undefined, closerFunction(done))
|
server = makeServer(undefined, closerFunction(done))
|
||||||
client = new RPCSocket(21010, "localhost")
|
client = new RPCSocket(21010, "localhost")
|
||||||
client.connect()
|
client.connect()
|
||||||
})
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
it('trigger hook closeHandler', (done) => {
|
it('trigger hook closeHandler', (done) => {
|
||||||
server = makeServer(undefined, undefined, closerFunction(done))
|
server = makeServer(undefined, undefined, closerFunction(done))
|
||||||
client = new RPCSocket(21010, "localhost")
|
client = new RPCSocket(21010, "localhost")
|
||||||
client.connect().then(_ => {
|
client.connect().then(_ => {
|
||||||
client['test'].subscribe(function cb(){
|
client['test'].subscribe(function cb() {
|
||||||
cb['destroy']()
|
cb['destroy']()
|
||||||
}).then(_ => client['test'].triggerCallback())
|
}).then(_ => client['test'].triggerCallback())
|
||||||
})
|
})
|
||||||
@@ -301,8 +490,6 @@ describe('Serverside Triggers', () => {
|
|||||||
client['test'].subscribe(noop).then(_ => client.close())
|
client['test'].subscribe(noop).then(_ => client.close())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('RPCSocket', () => {
|
describe('RPCSocket', () => {
|
||||||
@@ -526,7 +713,7 @@ describe('Sesame should unlock the socket', () => {
|
|||||||
|
|
||||||
it('should not work without sesame', (done) => {
|
it('should not work without sesame', (done) => {
|
||||||
const sock = new RPCSocket(21004, "localhost")
|
const sock = new RPCSocket(21004, "localhost")
|
||||||
sock.connect( /* no sesame */).then(async (cli) => {
|
sock.connect().then(async (cli) => {
|
||||||
if (!cli.test)
|
if (!cli.test)
|
||||||
done()
|
done()
|
||||||
else {
|
else {
|
||||||
@@ -733,7 +920,6 @@ type myExporterIfc = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
describe("Class binding", () => {
|
describe("Class binding", () => {
|
||||||
|
|
||||||
let exporter1: MyExporter
|
let exporter1: MyExporter
|
||||||
@@ -802,32 +988,6 @@ describe("Class binding", () => {
|
|||||||
serv.close()
|
serv.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
/* The server-side socket will enter a 30s timeout if destroyed by a RPC.
|
|
||||||
to mitigate the impact on testing time these are not run.
|
|
||||||
|
|
||||||
it("binds correctly", function(done){
|
|
||||||
this.timeout(1000)
|
|
||||||
sock['MyExporter'].myRPC().then((res) => {
|
|
||||||
done(new Error(res))
|
|
||||||
}).catch(e => {
|
|
||||||
//job will time out because of setExporters
|
|
||||||
allowed = true
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("changes exporters", (done) => {
|
|
||||||
|
|
||||||
sock['MyExporter'].myRPC().then((res) => {
|
|
||||||
if (res === "Hello Borld")
|
|
||||||
done()
|
|
||||||
else
|
|
||||||
done(new Error(res))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
it("use sesameFilter for available", (done) => {
|
it("use sesameFilter for available", (done) => {
|
||||||
if (sock['MyExporter']) {
|
if (sock['MyExporter']) {
|
||||||
allowed = false
|
allowed = false
|
||||||
@@ -842,6 +1002,14 @@ describe("Class binding", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
|
describe('finally', () => {
|
||||||
|
it('print open handles (Ignore `DNSCHANNEL` and `Immediate`)', () => {
|
||||||
|
//log(console)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
describe("attaching handlers before connecting", () => {
|
describe("attaching handlers before connecting", () => {
|
||||||
it("fires error if server is unreachable", (done) => {
|
it("fires error if server is unreachable", (done) => {
|
||||||
@@ -870,17 +1038,8 @@ describe("attaching handlers before connecting", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
|
||||||
* ## 1.11.0 breaking ##
|
|
||||||
*
|
|
||||||
* API change: Move from bsock to socketio changes underlying API for when errors are thrown.
|
|
||||||
* socketio does not throw on unknown listener. This behaviour is considered more consistent with the design
|
|
||||||
* goals of RPClibrary and was thus adopted
|
|
||||||
*
|
|
||||||
|
|
||||||
|
|
||||||
it("fires error if call is unknown", (done) => {
|
it("fires error if call is unknown", (done) => {
|
||||||
const serv = new RPCServer(21004)
|
const serv = new RPCServer().listen(21004)
|
||||||
const sock = new RPCSocket(21004, 'localhost')
|
const sock = new RPCSocket(21004, 'localhost')
|
||||||
|
|
||||||
sock.on('error', (err) => {
|
sock.on('error', (err) => {
|
||||||
@@ -900,27 +1059,4 @@ describe("attaching handlers before connecting", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("demands catch on method invocation if call is unknown", (done) => {
|
|
||||||
const serv = new RPCServer(21004)
|
|
||||||
const sock = new RPCSocket(21004, 'localhost')
|
|
||||||
|
|
||||||
sock.connect().then(_ => {
|
|
||||||
sock.call("unknownRPC123", "AAAAA").catch(e => {
|
|
||||||
sock.close()
|
|
||||||
serv.close()
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
}).catch(e => {
|
|
||||||
console.log("unexpected connect catch clause");
|
|
||||||
done(e)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('finally', () => {
|
|
||||||
it('print open handles (Ignore `DNSCHANNEL` and `Immediate`)', () => {
|
|
||||||
//log(console)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
Reference in New Issue
Block a user