clean up type system, remove subres
This commit is contained in:
Generated
+55
-35
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rpclibrary",
|
"name": "rpclibrary",
|
||||||
"version": "1.6.1",
|
"version": "1.9.2",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -467,9 +467,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "6.3.0",
|
"version": "6.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
|
||||||
"integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==",
|
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"aggregate-error": {
|
"aggregate-error": {
|
||||||
@@ -753,9 +753,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"version": "3.7.1",
|
"version": "3.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||||
"integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==",
|
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"bn.js": {
|
"bn.js": {
|
||||||
@@ -952,9 +952,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.5",
|
"version": "7.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||||
"integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==",
|
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
@@ -1062,9 +1062,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chownr": {
|
"chownr": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||||
"integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==",
|
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"chrome-trace-event": {
|
"chrome-trace-event": {
|
||||||
@@ -1331,6 +1331,11 @@
|
|||||||
"randomfill": "^1.0.3"
|
"randomfill": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"crypto-js": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
|
||||||
|
},
|
||||||
"cyclist": {
|
"cyclist": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
|
||||||
@@ -2661,9 +2666,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"handlebars": {
|
"handlebars": {
|
||||||
"version": "4.5.1",
|
"version": "4.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.3.tgz",
|
||||||
"integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==",
|
"integrity": "sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"neo-async": "^2.6.0",
|
"neo-async": "^2.6.0",
|
||||||
@@ -3388,9 +3393,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"kind-of": {
|
"kind-of": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"lcid": {
|
"lcid": {
|
||||||
@@ -4707,9 +4712,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"serialize-javascript": {
|
"serialize-javascript": {
|
||||||
"version": "1.9.1",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
|
||||||
"integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
|
"integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"set-blocking": {
|
"set-blocking": {
|
||||||
@@ -5039,6 +5044,12 @@
|
|||||||
"figgy-pudding": "^3.5.1"
|
"figgy-pudding": "^3.5.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"stackback": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
||||||
|
"integrity": "sha1-Gsig2Ug4SNFpXkGLbQMaPDzmjjs=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"static-extend": {
|
"static-extend": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
|
||||||
@@ -5094,9 +5105,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"stream-shift": {
|
"stream-shift": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
|
||||||
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
|
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"string-width": {
|
"string-width": {
|
||||||
@@ -5182,9 +5193,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"terser": {
|
"terser": {
|
||||||
"version": "4.3.9",
|
"version": "4.6.6",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz",
|
||||||
"integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==",
|
"integrity": "sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"commander": "^2.20.0",
|
"commander": "^2.20.0",
|
||||||
@@ -5201,16 +5212,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"terser-webpack-plugin": {
|
"terser-webpack-plugin": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
|
||||||
"integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==",
|
"integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"cacache": "^12.0.2",
|
"cacache": "^12.0.2",
|
||||||
"find-cache-dir": "^2.1.0",
|
"find-cache-dir": "^2.1.0",
|
||||||
"is-wsl": "^1.1.0",
|
"is-wsl": "^1.1.0",
|
||||||
"schema-utils": "^1.0.0",
|
"schema-utils": "^1.0.0",
|
||||||
"serialize-javascript": "^1.7.0",
|
"serialize-javascript": "^2.1.2",
|
||||||
"source-map": "^0.6.1",
|
"source-map": "^0.6.1",
|
||||||
"terser": "^4.1.2",
|
"terser": "^4.1.2",
|
||||||
"webpack-sources": "^1.4.0",
|
"webpack-sources": "^1.4.0",
|
||||||
@@ -5488,9 +5499,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uglify-js": {
|
"uglify-js": {
|
||||||
"version": "3.6.7",
|
"version": "3.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.7.tgz",
|
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.0.tgz",
|
||||||
"integrity": "sha512-4sXQDzmdnoXiO+xvmTzQsfIiwrjUCSA95rSP4SEd8tDb51W2TiDOlL76Hl+Kw0Ie42PSItCW8/t6pBNCF2R48A==",
|
"integrity": "sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -5857,6 +5868,15 @@
|
|||||||
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
|
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"why-is-node-running": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-TwUeoRNMAWy8jAD8oFLtgmYKecZkH3yCtbQ17CYVCxd1WaPJAEB6oqkNgm0o+wIzxJi1oHUkOxMk0M/t5jCGeA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"stackback": "0.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"wide-align": {
|
"wide-align": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||||
|
|||||||
+4
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rpclibrary",
|
"name": "rpclibrary",
|
||||||
"version": "1.9.2",
|
"version": "1.10.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": {
|
||||||
@@ -46,10 +46,12 @@
|
|||||||
"typedoc-plugin-markdown": "^2.2.6",
|
"typedoc-plugin-markdown": "^2.2.6",
|
||||||
"typescript": "^3.5.3",
|
"typescript": "^3.5.3",
|
||||||
"webpack": "^4.40.2",
|
"webpack": "^4.40.2",
|
||||||
"webpack-cli": "^3.3.9"
|
"webpack-cli": "^3.3.9",
|
||||||
|
"why-is-node-running": "^2.1.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bsock": "^0.1.9",
|
"bsock": "^0.1.9",
|
||||||
|
"crypto-js": "^4.0.0",
|
||||||
"http": "0.0.0",
|
"http": "0.0.0",
|
||||||
"uuid": "^3.3.3"
|
"uuid": "^3.3.3"
|
||||||
},
|
},
|
||||||
|
|||||||
+31
-35
@@ -2,30 +2,26 @@
|
|||||||
|
|
||||||
import http = require('http');
|
import http = require('http');
|
||||||
import bsock = require('bsock');
|
import bsock = require('bsock');
|
||||||
|
|
||||||
import * as T from './Types';
|
import * as T from './Types';
|
||||||
import * as U from './Utils';
|
import * as U from './Utils';
|
||||||
import * as I from './Interfaces';
|
import * as I from './Interfaces';
|
||||||
import { Socket } from 'dgram';
|
|
||||||
|
|
||||||
type Exporters<InterfaceT extends T.RPCInterface = T.RPCInterface, SubResType = {}> = I.RPCExporter<T.RPCInterface<InterfaceT>, keyof InterfaceT, SubResType>[]
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Websocket-server-on-steroids with built-in RPC capabilities
|
* A Websocket-server-on-steroids with built-in RPC capabilities
|
||||||
*/
|
*/
|
||||||
export class RPCServer<
|
export class RPCServer<
|
||||||
SubResType = {},
|
|
||||||
InterfaceT extends T.RPCInterface = T.RPCInterface,
|
InterfaceT extends T.RPCInterface = T.RPCInterface,
|
||||||
> implements I.Destroyable{
|
> implements I.Destroyable {
|
||||||
|
|
||||||
private ws = http.createServer()
|
private ws = http.createServer()
|
||||||
private io = bsock.createServer()
|
private io = bsock.createServer()
|
||||||
private visibility:T.Visibility
|
private visibility: T.Visibility
|
||||||
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
|
private sesame?: T.SesameFunction
|
||||||
private accessFilter: T.AccessFilter<InterfaceT, SubResType>
|
private accessFilter: T.AccessFilter<InterfaceT>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws On RPC with no name
|
* @throws On RPC with no name
|
||||||
@@ -34,71 +30,70 @@ export class RPCServer<
|
|||||||
* @param conf A {@link SocketConf} object with optional settings
|
* @param conf A {@link SocketConf} object with optional settings
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private port:number,
|
private port: number,
|
||||||
private exporters: Exporters<InterfaceT, SubResType> = [],
|
private exporters: T.ExporterArray<InterfaceT> = [],
|
||||||
conf: T.ServerConf<InterfaceT, SubResType> = {}
|
conf: T.ServerConf<InterfaceT> = {}
|
||||||
){
|
) {
|
||||||
if(!conf.visibility) this.visibility = "0.0.0.0"
|
if (!conf.visibility) this.visibility = "0.0.0.0"
|
||||||
|
|
||||||
|
if (conf.sesame) {
|
||||||
if(conf.sesame){
|
|
||||||
this.sesame = U.makeSesameFunction(conf.sesame)
|
this.sesame = U.makeSesameFunction(conf.sesame)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.accessFilter = conf.accessFilter || (async (sesame) => {
|
this.accessFilter = conf.accessFilter || (async (sesame) => {
|
||||||
if(!this.sesame) return true
|
if (!this.sesame) return true
|
||||||
return this.sesame!(sesame!)
|
return this.sesame!(sesame!)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
this.errorHandler = (socket:I.Socket) => (error:any, rpcName:string, args: any[]) => {
|
this.errorHandler = (socket: I.Socket) => (error: any, rpcName: string, args: any[]) => {
|
||||||
if(conf.errorHandler) conf.errorHandler(socket, error, rpcName, args)
|
if (conf.errorHandler) conf.errorHandler(socket, error, rpcName, args)
|
||||||
else throw error
|
else throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closeHandler = (socket:I.Socket) => {
|
this.closeHandler = (socket: I.Socket) => {
|
||||||
if(conf.closeHandler) conf.closeHandler(socket)
|
if (conf.closeHandler) conf.closeHandler(socket)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connectionHandler = (socket:I.Socket) => {
|
this.connectionHandler = (socket: I.Socket) => {
|
||||||
if(conf.connectionHandler) conf.connectionHandler(socket)
|
if (conf.connectionHandler) conf.connectionHandler(socket)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 => ex.exportRPCs()).find(rpc => !rpc.name)
|
let badRPC = exporters.flatMap(ex => ex.exportRPCs()).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.
|
||||||
\nUse 'funtion name(..){ .. }' syntax instead.
|
\nUse 'funtion name(..){ .. }' syntax instead.
|
||||||
\n
|
\n
|
||||||
\n<------------OFFENDING RPC:
|
\n<------------OFFENDING RPC:
|
||||||
\n`+badRPC.toString()+`
|
\n`+ badRPC.toString() + `
|
||||||
\n>------------OFFENDING RPC`)
|
\n>------------OFFENDING RPC`)
|
||||||
}
|
}
|
||||||
this.startWebsocket()
|
this.startWebsocket()
|
||||||
}
|
}
|
||||||
|
|
||||||
private startWebsocket(){
|
private startWebsocket() {
|
||||||
try{
|
try {
|
||||||
this.io.attach(this.ws)
|
this.io.attach(this.ws)
|
||||||
this.io.on('socket', (socket:I.Socket) => {
|
this.io.on('socket', (socket: I.Socket) => {
|
||||||
socket.on('error', (err) => this.errorHandler(socket, err, "system", []))
|
socket.on('error', (err) => this.errorHandler(socket, err, "system", []))
|
||||||
socket.on('close', () => this.closeHandler(socket))
|
socket.on('close', () => this.closeHandler(socket))
|
||||||
this.connectionHandler(socket)
|
this.connectionHandler(socket)
|
||||||
this.initRPCs(socket)
|
this.initRPCs(socket)
|
||||||
})
|
})
|
||||||
this.ws.listen(this.port, this.visibility)
|
this.ws = this.ws.listen(this.port, this.visibility)
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
this.errorHandler(this.io, e, 'system', [])
|
this.errorHandler(this.io, e, 'system', [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected initRPCs(socket:I.Socket){
|
protected initRPCs(socket: I.Socket) {
|
||||||
socket.hook('info', async (sesame? : string) => {
|
socket.hook('info', async (sesame?: string) => {
|
||||||
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 []
|
||||||
return U.rpcHooker(socket, exp, this.errorHandler, this.sesame)
|
return U.rpcHooker(socket, exp, this.errorHandler, this.sesame)
|
||||||
}))
|
}))
|
||||||
return rpcs.flat()
|
return rpcs.flat()
|
||||||
@@ -108,8 +103,7 @@ export class RPCServer<
|
|||||||
/**
|
/**
|
||||||
* Publishes a new list of Exporters. This destroys and restarts the socket
|
* Publishes a new list of Exporters. This destroys and restarts the socket
|
||||||
* @param exporters the exporters to publish
|
* @param exporters the exporters to publish
|
||||||
*/
|
public setExporters(exporters: T.ExporterArray<InterfaceT>): any {
|
||||||
public setExporters(exporters: Exporters<InterfaceT, SubResType>):any{
|
|
||||||
exporters.forEach(U.fixNames)
|
exporters.forEach(U.fixNames)
|
||||||
this.destroy()
|
this.destroy()
|
||||||
this.ws = http.createServer()
|
this.ws = http.createServer()
|
||||||
@@ -117,6 +111,8 @@ export class RPCServer<
|
|||||||
this.exporters = exporters
|
this.exporters = exporters
|
||||||
this.startWebsocket()
|
this.startWebsocket()
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.io.close()
|
this.io.close()
|
||||||
|
|||||||
+15
-6
@@ -7,12 +7,12 @@ import * as I from './Interfaces';
|
|||||||
import { stripAfterEquals, appendComma } from './Utils';
|
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 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<RPCSocket & T> {
|
|
||||||
|
static async makeSocket<T extends T.RPCInterface = T.RPCInterface>(port:number, server: string, sesame?:string, conf?:T.SocketConf): Promise<T.ConnectedSocket<T>> {
|
||||||
const socket = new RPCSocket(port, server, conf)
|
const socket = new RPCSocket(port, server, conf)
|
||||||
return await socket.connect<T>(sesame)
|
return await socket.connect<T>(sesame)
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ 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>( sesame?: string ) : Promise<RPCSocket & T>{
|
public async connect<T extends T.RPCInterface= Ifc>( sesame?: string ) : Promise<T.ConnectedSocket<T>> {
|
||||||
this.socket = await bsock.connect(this.port, this.server, this.conf.tls?this.conf.tls:false)
|
this.socket = await bsock.connect(this.port, this.server, this.conf.tls?this.conf.tls:false)
|
||||||
this.errorHandlers.forEach(h => this.socket.on('error', h))
|
this.errorHandlers.forEach(h => this.socket.on('error', h))
|
||||||
this.closeHandlers.forEach(h => this.socket.on('close', h))
|
this.closeHandlers.forEach(h => this.socket.on('close', h))
|
||||||
@@ -197,10 +197,19 @@ export class RPCSocket implements I.Socket{
|
|||||||
return eval( `
|
return eval( `
|
||||||
async (${headerArgs} callback) => {
|
async (${headerArgs} callback) => {
|
||||||
const r = await this.call("${fnName}", ${sesame} ${argParams})
|
const r = await this.call("${fnName}", ${sesame} ${argParams})
|
||||||
if(r && r.result === 'Success'){
|
try{
|
||||||
|
if(r){
|
||||||
|
if(r.uuid){
|
||||||
|
callback['destroy'] = () => { this.socket.unhook(r.uuid) }
|
||||||
this.socket.hook(r.uuid, callback)
|
this.socket.hook(r.uuid, callback)
|
||||||
}
|
}
|
||||||
return r
|
return r.return
|
||||||
|
}else{
|
||||||
|
throw new Error("Empty response")
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}`)
|
}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-5
@@ -4,13 +4,12 @@ import * as I from "./Interfaces"
|
|||||||
/**
|
/**
|
||||||
* Interface for all classes that export RPCs
|
* Interface for all classes that export RPCs
|
||||||
*/
|
*/
|
||||||
export interface RPCExporter<
|
export type RPCExporter<
|
||||||
Ifc extends T.RPCInterface = T.RPCInterface,
|
Ifc extends T.RPCInterface = T.RPCInterface,
|
||||||
Name extends keyof Ifc = keyof Ifc,
|
Name extends keyof Ifc = string,
|
||||||
SubresT = {}
|
> = {
|
||||||
>{
|
|
||||||
name: Name
|
name: Name
|
||||||
exportRPCs() : T.RPCInterfaceArray<Ifc, SubresT>[Name]
|
exportRPCs() : T.RPCDefinitions<Ifc>[Name]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+26
-18
@@ -1,8 +1,9 @@
|
|||||||
import * as I from "./Interfaces";
|
import * as I from "./Interfaces";
|
||||||
|
import { RPCSocket } from "./Frontend";
|
||||||
|
|
||||||
export type AnyFunction = (...args:any) => any
|
export type AnyFunction = (...args:any) => any
|
||||||
export type HookFunction<F extends AnyFunction = AnyFunction, SubresT = {}> = (...args: Parameters<F>) => Promise<SubscriptionResponse<SubresT> | ErrorResponse>
|
export type HookFunction = AnyFunction
|
||||||
export type AccessFilter<InterfaceT extends RPCInterface, SubresT> = (sesame:string|undefined, exporter: I.RPCExporter<RPCInterface<InterfaceT>, keyof InterfaceT, SubresT>) => Promise<boolean>
|
export type AccessFilter<InterfaceT extends RPCInterface = RPCInterface> = (sesame:string|undefined, exporter: I.RPCExporter<InterfaceT, keyof InterfaceT>) => Promise<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, error:any, rpcName: string, args: any[]) => void
|
export type ErrorHandler = (socket:I.Socket, error:any, rpcName: string, args: any[]) => void
|
||||||
@@ -17,8 +18,12 @@ export type FrontEndHandlerType = {
|
|||||||
'close' : () => void
|
'close' : () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ServerConf<InterfaceT extends RPCInterface, SubresT> = {
|
export type ExporterArray<InterfaceT extends RPCInterface = RPCInterface> = I.RPCExporter<RPCInterface<InterfaceT>, keyof InterfaceT>[]
|
||||||
accessFilter?: AccessFilter<InterfaceT, SubresT>
|
|
||||||
|
export type ConnectedSocket<T extends RPCInterface = RPCInterface> = RPCSocket & T
|
||||||
|
|
||||||
|
export type ServerConf<InterfaceT extends RPCInterface> = {
|
||||||
|
accessFilter?: AccessFilter<InterfaceT>
|
||||||
connectionHandler?: ConnectionHandler
|
connectionHandler?: ConnectionHandler
|
||||||
errorHandler?: ErrorHandler
|
errorHandler?: ErrorHandler
|
||||||
closeHandler?: CloseHandler
|
closeHandler?: CloseHandler
|
||||||
@@ -36,23 +41,23 @@ export type Outcome = "Success" | "Error"
|
|||||||
export type Respose<T> = T & { result: Outcome }
|
export type Respose<T> = T & { result: Outcome }
|
||||||
export type SuccessResponse<T = {}> = Respose<T> & { result: "Success" }
|
export type SuccessResponse<T = {}> = Respose<T> & { result: "Success" }
|
||||||
export type ErrorResponse<T = {}> = Respose<T> & { result: "Error", message?:string }
|
export type ErrorResponse<T = {}> = Respose<T> & { result: "Error", message?:string }
|
||||||
export type SubscriptionResponse<T = {}> = Respose<T> & { result: "Success"; uuid: string }
|
|
||||||
|
|
||||||
export type RPCType = 'Hook' | 'Unhook' | 'Call'
|
export type RPCType = 'Hook' | 'Unhook' | 'Call'
|
||||||
|
|
||||||
export type CallRPC<N, F> = {
|
export type CallRPC<Name, Func extends AnyFunction> = {
|
||||||
name: N
|
name: Name
|
||||||
call: F
|
call: Func
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HookRPC<N, F extends AnyFunction, SubresT = {}> = {
|
|
||||||
name: N
|
export type HookRPC<Name, Func extends AnyFunction> = {
|
||||||
hook: HookFunction<F, SubresT>
|
name: Name
|
||||||
|
hook: Func
|
||||||
onCallback?: AnyFunction
|
onCallback?: AnyFunction
|
||||||
onClose?: HookCloseFunction<SubresT>
|
onClose?: HookCloseFunction<ReturnType<Func> extends Promise<infer T> ? T : ReturnType<Func>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RPC<N, F extends AnyFunction, SubresT = {}> = HookRPC<N, F, SubresT> | CallRPC<N,F> | F
|
export type RPC<Name, Func extends AnyFunction> = HookRPC<Name, Func> | CallRPC<Name,Func> | Func
|
||||||
|
|
||||||
export type RPCInterface<Impl extends RPCInterface = {}> = {
|
export type RPCInterface<Impl extends RPCInterface = {}> = {
|
||||||
[grp in string] : {
|
[grp in string] : {
|
||||||
@@ -60,10 +65,13 @@ export type RPCInterface<Impl extends RPCInterface = {}> = {
|
|||||||
}
|
}
|
||||||
} & Impl
|
} & Impl
|
||||||
|
|
||||||
export type RPCInterfaceArray<Itfc extends RPCInterface, SubresT = {}> = {
|
export type exportT = {
|
||||||
[grp in keyof Itfc]: Array<
|
[group in string]: {}
|
||||||
{ [rpc in keyof Itfc[grp]]: RPC<rpc, Itfc[grp][rpc], SubresT> }[keyof Itfc[grp]]
|
}
|
||||||
>
|
|
||||||
|
//This probably has lots of issues
|
||||||
|
export type RPCDefinitions<Ifc extends RPCInterface> = {
|
||||||
|
[grp in keyof Ifc]:( { [rpc in keyof Ifc[grp]]: RPC<rpc, Ifc[grp][rpc]> }[keyof Ifc[grp]] )[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BaseInfo = {
|
export type BaseInfo = {
|
||||||
@@ -86,4 +94,4 @@ export type RpcInfo = HookInfo | CallInfo
|
|||||||
export type ExtendedRpcInfo = RpcInfo & { uniqueName: string }
|
export type ExtendedRpcInfo = RpcInfo & { uniqueName: string }
|
||||||
|
|
||||||
export type OnFunction = <T extends "error" | "close">(type: T, f: FrontEndHandlerType[T]) => void
|
export type OnFunction = <T extends "error" | "close">(type: T, f: FrontEndHandlerType[T]) => void
|
||||||
export type HookCloseFunction<T = {}> = (res:SubscriptionResponse<T>, rpc:HookRPC<any, any, T>) => any
|
export type HookCloseFunction<T> = (res: T, rpc:HookRPC<any, any>) => any
|
||||||
|
|||||||
+17
-24
@@ -2,7 +2,6 @@ import * as uuidv4 from "uuid/v4"
|
|||||||
|
|
||||||
import * as T from "./Types";
|
import * as T from "./Types";
|
||||||
import * as I from "./Interfaces";
|
import * as I from "./Interfaces";
|
||||||
import { SubscriptionResponse } from "./Types";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate an RPC to RPCInfo for serialization.
|
* Translate an RPC to RPCInfo for serialization.
|
||||||
@@ -12,7 +11,7 @@ import { SubscriptionResponse } from "./Types";
|
|||||||
* @param sesame optional sesame phrase to prepend before all RPC arguments
|
* @param sesame optional sesame phrase to prepend before all RPC arguments
|
||||||
* @throws Error on RPC without name property
|
* @throws Error on RPC without name property
|
||||||
*/
|
*/
|
||||||
export const rpcToRpcinfo = <SubResT = {}>(socket: I.Socket, rpc : T.RPC<any, any, SubResT>, owner: string, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction):T.RpcInfo => {
|
export const rpcToRpcinfo = (socket: I.Socket, rpc : T.RPC<any, any>, owner: string, errorHandler: T.ErrorHandler, sesame?:T.SesameFunction):T.RpcInfo => {
|
||||||
switch (typeof rpc){
|
switch (typeof rpc){
|
||||||
case "object":
|
case "object":
|
||||||
if(rpc['call']){
|
if(rpc['call']){
|
||||||
@@ -24,7 +23,7 @@ export const rpcToRpcinfo = <SubResT = {}>(socket: I.Socket, rpc : T.RPC<any, an
|
|||||||
call: sesame?async (_sesame, ...args) => {if(sesame(_sesame)) return await rpc['call'].apply({}, args); socket.destroy()}:rpc['call'], // check & remove sesame
|
call: sesame?async (_sesame, ...args) => {if(sesame(_sesame)) return await rpc['call'].apply({}, args); socket.destroy()}:rpc['call'], // check & remove sesame
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
const generator = hookGenerator(<T.HookRPC<any, any, any>>rpc, errorHandler, sesame)
|
const generator = hookGenerator(<T.HookRPC<any, any>>rpc, errorHandler, sesame)
|
||||||
return {
|
return {
|
||||||
owner: owner,
|
owner: owner,
|
||||||
argNames: extractArgs(generator(undefined)),
|
argNames: extractArgs(generator(undefined)),
|
||||||
@@ -58,10 +57,9 @@ 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>, 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 = [...exporter.exportRPCs()]
|
const RPCs = exporter.exportRPCs()
|
||||||
|
|
||||||
|
|
||||||
return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame))
|
return RPCs.map(rpc => rpcToRpcinfo(socket, rpc, owner, errorHandler, sesame))
|
||||||
.map(info => {
|
.map(info => {
|
||||||
@@ -107,7 +105,7 @@ export function stripAfterEquals(str:string):string{
|
|||||||
* @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>, errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => {
|
const hookGenerator = (rpc:T.HookRPC<any, any>, /*not unused!*/ errorHandler: T.ErrorHandler, sesameFn?: T.SesameFunction): T.HookInfo['generator'] => {
|
||||||
let argsArr = extractArgs(rpc.hook)
|
let argsArr = extractArgs(rpc.hook)
|
||||||
argsArr.pop() //remove 'callback' from the end
|
argsArr.pop() //remove 'callback' from the end
|
||||||
let callArgs = argsArr.join(',')
|
let callArgs = argsArr.join(',')
|
||||||
@@ -119,16 +117,23 @@ const hookGenerator = (rpc:T.HookRPC<any, any, any>, errorHandler: T.ErrorHandle
|
|||||||
|
|
||||||
//note rpc.hook is the associated RPC, not a socket.hook
|
//note rpc.hook is the associated RPC, not a socket.hook
|
||||||
return eval(`
|
return eval(`
|
||||||
(socket) => async (${args}) => {
|
(clientSocket) => async (${args}) => {
|
||||||
try{
|
try{
|
||||||
if(sesameFn && !sesameFn(sesame)) return
|
if(sesameFn && !sesameFn(sesame)) return
|
||||||
|
const uuid = uuidv4()
|
||||||
const res = await rpc.hook(${callArgs} (...cbargs) => {
|
const res = await rpc.hook(${callArgs} (...cbargs) => {
|
||||||
if(rpc.onCallback) rpc.onCallback.apply({}, cbargs)
|
if(rpc.onCallback){
|
||||||
socket.call.apply(socket, [res.uuid, ...cbargs])
|
rpc.onCallback.apply({}, cbargs)
|
||||||
|
}
|
||||||
|
clientSocket.call.apply(clientSocket, [uuid, ...cbargs])
|
||||||
})
|
})
|
||||||
return res
|
if(rpc.onClose){
|
||||||
|
clientSocket.on('close', () => rpc.onClose(res, rpc))
|
||||||
|
}
|
||||||
|
return {'uuid': uuid, 'return': res}
|
||||||
}catch(e){
|
}catch(e){
|
||||||
errorHandler(socket)(e, ${rpc.name}, [${args}])
|
//can throw to pass exception to client or swallow to keep it local
|
||||||
|
errorHandler(clientSocket)(e, ${rpc.name}, [${args}])
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
}
|
}
|
||||||
@@ -147,18 +152,6 @@ const extractArgs = (f:Function):string[] => {
|
|||||||
return fn!==""?fn.split(',') : []
|
return fn!==""?fn.split(',') : []
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple utility function to create basic {@link SubscriptionResponse}
|
|
||||||
* @param uuid optional uuid to use, otherwise defaults to uuid/v4
|
|
||||||
*/
|
|
||||||
export function makeSubResponse<T extends {} = {}>(extension:T):SubscriptionResponse & T{
|
|
||||||
return {
|
|
||||||
result: "Success",
|
|
||||||
uuid: uuidv4(),
|
|
||||||
...extension
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function makeSesameFunction (sesame : T.SesameFunction | string) : T.SesameFunction {
|
export function makeSesameFunction (sesame : T.SesameFunction | string) : T.SesameFunction {
|
||||||
if(typeof sesame === 'function'){
|
if(typeof sesame === 'function'){
|
||||||
|
|||||||
+231
-188
@@ -1,44 +1,43 @@
|
|||||||
import { describe, it, Func } from "mocha";
|
import { describe, it } from "mocha";
|
||||||
|
import { RPCServer, RPCSocket } from '../Index'
|
||||||
import { RPCServer, RPCSocket, SubscriptionResponse, makeSubResponse } from '../Index'
|
|
||||||
import * as uuidv4 from "uuid/v4"
|
|
||||||
import { doesNotReject } from "assert";
|
|
||||||
import { Socket } from "dgram";
|
|
||||||
import { RPCExporter } from "../src/Interfaces";
|
import { RPCExporter } from "../src/Interfaces";
|
||||||
|
import { ConnectedSocket } from "../src/Types";
|
||||||
|
import * as log from 'why-is-node-running';
|
||||||
|
|
||||||
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(){
|
function makeServer() {
|
||||||
let subcallback
|
let subcallback
|
||||||
return new RPCServer<{ topic: string }>(21010, [{
|
return new RPCServer(21010, [{
|
||||||
name: "test",
|
name: 'test',
|
||||||
exportRPCs: () => [
|
exportRPCs: () => [
|
||||||
{
|
{
|
||||||
name: 'echo',
|
name: 'echo',
|
||||||
call: async (s:string) => s,
|
call: async (s: string) => s,
|
||||||
},{
|
}, {
|
||||||
name: 'simpleSubscribe',
|
name: 'simpleSubscribe',
|
||||||
hook: async(callback) => {
|
hook: async (callback) => {
|
||||||
subcallback = callback
|
subcallback = callback
|
||||||
return makeSubResponse<{topic: string}>({topic: "test"})
|
return { topic: "test" }
|
||||||
}
|
},
|
||||||
},{
|
onClose: (res) => { }
|
||||||
|
}, {
|
||||||
name: 'subscribe',
|
name: 'subscribe',
|
||||||
hook: async (callback) => {
|
hook: async (callback) => {
|
||||||
subcallback = callback
|
subcallback = callback
|
||||||
return makeSubResponse<{topic: string}>({topic: "test"})
|
return { topic: "test" }
|
||||||
},
|
},
|
||||||
onClose: (res, rpc) => {
|
onClose: (res, rpc) => {
|
||||||
console.log("onClose", rpc.name === 'subscribe' && res?"OK":"")
|
console.log("onClose", rpc.name === 'subscribe' && res ? "OK" : "")
|
||||||
subcallback = null
|
subcallback = null
|
||||||
},
|
},
|
||||||
onCallback: (...args:any) => {
|
onCallback: (...args: any) => {
|
||||||
console.log("onCallback", args[0] === "test" && args[1] === "callback"?"OK":"")
|
console.log("onCallback", args[0] === "test" && args[1] === "callback" ? "OK" : "")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
add,
|
add,
|
||||||
function triggerCallback(...messages:any[]):number {return subcallback.apply({}, messages)},
|
function triggerCallback(...messages: any[]): number { return subcallback.apply({}, messages) },
|
||||||
]
|
]
|
||||||
}],{
|
}], {
|
||||||
connectionHandler: (socket) => { },
|
connectionHandler: (socket) => { },
|
||||||
closeHandler: (socket) => { },
|
closeHandler: (socket) => { },
|
||||||
errorHandler: (socket, err) => { throw err }
|
errorHandler: (socket, err) => { throw err }
|
||||||
@@ -47,57 +46,58 @@ function makeServer(){
|
|||||||
|
|
||||||
|
|
||||||
describe('RPCServer', () => {
|
describe('RPCServer', () => {
|
||||||
let server: RPCServer<{ topic: string }, any>
|
let client, server
|
||||||
|
|
||||||
before(() => {
|
|
||||||
server = makeServer()
|
|
||||||
})
|
|
||||||
|
|
||||||
after(() => {
|
|
||||||
server.destroy()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should be able to use all kinds of RPC definitions', (done) => {
|
|
||||||
|
|
||||||
const echo = (x) => x
|
const echo = (x) => x
|
||||||
|
|
||||||
const server = new RPCServer(21003, [{
|
before(done => {
|
||||||
|
server = new RPCServer(21003, [{
|
||||||
name: 'HelloWorldRPCGroup',
|
name: 'HelloWorldRPCGroup',
|
||||||
exportRPCs: () => [
|
exportRPCs: () => [
|
||||||
echo, //named function variable
|
echo, //named function variable
|
||||||
function echof(x){ return x }, //named function
|
function echof(x) { return x }, //named function
|
||||||
{
|
{
|
||||||
name: 'echoExplicit', //describing object
|
name: 'echoExplicit', //describing object
|
||||||
call: async (x,y,z) => [x,y,z]
|
call: async (x, y, z) => [x, y, z]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}])
|
}])
|
||||||
|
|
||||||
const client = new RPCSocket(21003, 'localhost')
|
client = new RPCSocket(21003, 'localhost')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
after(done => {
|
||||||
|
client.destroy()
|
||||||
|
server.destroy()
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be able to use all kinds of RPC definitions', (done) => {
|
||||||
client.connect().then(async () => {
|
client.connect().then(async () => {
|
||||||
const r0 = await client['HelloWorldRPCGroup'].echo('Hello')
|
const r0 = await client['HelloWorldRPCGroup'].echo('Hello')
|
||||||
const r1 = await client['HelloWorldRPCGroup'].echof('World')
|
const r1 = await client['HelloWorldRPCGroup'].echof('World')
|
||||||
const r2 = await client['HelloWorldRPCGroup'].echoExplicit('R','P','C!')
|
const r2 = await client['HelloWorldRPCGroup'].echoExplicit('R', 'P', 'C!')
|
||||||
|
|
||||||
if(r0 === 'Hello' && r1 === 'World' && r2.join('') ==='RPC!'){
|
|
||||||
client.destroy()
|
if (r0 === 'Hello' && r1 === 'World' && r2.join('') === 'RPC!') {
|
||||||
server.destroy()
|
|
||||||
done()
|
done()
|
||||||
|
}else{
|
||||||
|
done(new Error("Bad response"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('new RPCServer() should fail on bad RPC', (done) => {
|
it('new RPCServer() should fail on bad RPC', (done) => {
|
||||||
try{
|
try {
|
||||||
new RPCServer(20001, [{
|
new RPCServer(20001, [{
|
||||||
name: "bad",
|
name: 'bad',
|
||||||
exportRPCs: () => [
|
exportRPCs: () => [
|
||||||
(aaa,bbb,ccc) => { return aaa+bbb+ccc }
|
(aaa, bbb, ccc) => { return aaa + bbb + ccc }
|
||||||
]
|
]
|
||||||
}])
|
}])
|
||||||
done(new Error("Didn't fail with bad RPC"))
|
done(new Error("Didn't fail with bad RPC"))
|
||||||
}catch(badRPCError){
|
} catch (badRPCError) {
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -106,9 +106,9 @@ describe('RPCServer', () => {
|
|||||||
|
|
||||||
describe('RPCSocket', () => {
|
describe('RPCSocket', () => {
|
||||||
let client: RPCSocket
|
let client: RPCSocket
|
||||||
let server: RPCServer<{topic: string}>
|
let server: RPCServer
|
||||||
|
|
||||||
before(async() => {
|
before(async () => {
|
||||||
server = makeServer()
|
server = makeServer()
|
||||||
client = new RPCSocket(21010, "localhost")
|
client = new RPCSocket(21010, "localhost")
|
||||||
return await client.connect()
|
return await client.connect()
|
||||||
@@ -122,7 +122,7 @@ describe('RPCSocket', () => {
|
|||||||
|
|
||||||
it('should have rpc echo', (done) => {
|
it('should have rpc echo', (done) => {
|
||||||
client['test'].echo("x").then(x => {
|
client['test'].echo("x").then(x => {
|
||||||
if(x === 'x')
|
if (x === 'x')
|
||||||
done()
|
done()
|
||||||
else
|
else
|
||||||
done(new Error('echo RPC response did not match'))
|
done(new Error('echo RPC response did not match'))
|
||||||
@@ -130,8 +130,8 @@ describe('RPCSocket', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should add up to 6', (done) => {
|
it('should add up to 6', (done) => {
|
||||||
client['test'].add(1,2,3).then(x => {
|
client['test'].add(1, 2, 3).then(x => {
|
||||||
if(x === 6)
|
if (x === 6)
|
||||||
done()
|
done()
|
||||||
else
|
else
|
||||||
done(new Error('add RPC response did not match'))
|
done(new Error('add RPC response did not match'))
|
||||||
@@ -140,9 +140,9 @@ describe('RPCSocket', () => {
|
|||||||
|
|
||||||
it('should subscribe with success', (done) => {
|
it('should subscribe with success', (done) => {
|
||||||
client['test'].simpleSubscribe(console.log).then(res => {
|
client['test'].simpleSubscribe(console.log).then(res => {
|
||||||
if(res.result === 'Success'){
|
if (res.topic === 'test') {
|
||||||
done()
|
done()
|
||||||
}else{
|
} else {
|
||||||
console.error(res)
|
console.error(res)
|
||||||
done(new Error('Subscribe did not return success'))
|
done(new Error('Subscribe did not return success'))
|
||||||
}
|
}
|
||||||
@@ -151,72 +151,66 @@ describe('RPCSocket', () => {
|
|||||||
|
|
||||||
it('subscribe should call back', (done) => {
|
it('subscribe should call back', (done) => {
|
||||||
client['test'].subscribe((...args: any) => {
|
client['test'].subscribe((...args: any) => {
|
||||||
if(args[0] === "test" && args[1] === "callback")
|
if (args[0] === "test" && args[1] === "callback")
|
||||||
done()
|
done()
|
||||||
else
|
else
|
||||||
done(new Error("Bad callback value "+ args))
|
done(new Error("Bad callback value " + args))
|
||||||
}).then( async () => {
|
}).then(async () => {
|
||||||
await client['test'].triggerCallback("test", "callback")
|
await client['test'].triggerCallback("test", "callback")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('simpleSubscribe should call back', (done) => {
|
it('simpleSubscribe should call back', (done) => {
|
||||||
client['test'].simpleSubscribe((...args: any) => {
|
client['test'].simpleSubscribe((...args: any) => {
|
||||||
if(args[0] === "test_" && args[1] === "callback_")
|
if (args[0] === "test_" && args[1] === "callback_")
|
||||||
done()
|
done()
|
||||||
else
|
else
|
||||||
done(new Error("Bad callback value "+ args))
|
done(new Error("Bad callback value " + args))
|
||||||
}).then( async () => {
|
}).then(async () => {
|
||||||
await client['test'].triggerCallback("test_", "callback_")
|
await client['test'].triggerCallback("test_", "callback_")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('It should do unhook', () => {
|
describe('It should do unhook', () => {
|
||||||
let candy = "OK"
|
const yesCandy = "OK"
|
||||||
|
const noCandy = "stolen"
|
||||||
|
let candy = yesCandy
|
||||||
let cb: Function
|
let cb: Function
|
||||||
let cb2: Function
|
let cb2: Function
|
||||||
let client: RPCSocket
|
let client: RPCSocket
|
||||||
let server: RPCServer<{topic: string}>
|
let server: RPCServer
|
||||||
|
|
||||||
before(async() => {
|
before(async () => {
|
||||||
server = new RPCServer<{ topic: string }>(21010, [{
|
server = new RPCServer(21010, [{
|
||||||
name: "test",
|
name: "test",
|
||||||
exportRPCs: () => [{
|
exportRPCs: () => [{
|
||||||
name: 'subscribe',
|
name: 'subscribe',
|
||||||
hook: async(callback):Promise<SubscriptionResponse<{topic:string}>> => {
|
hook: async (callback): Promise<void> => {
|
||||||
cb = <Function> callback
|
cb = <Function>callback
|
||||||
return {
|
return
|
||||||
result: "Success",
|
|
||||||
uuid: uuidv4(),
|
|
||||||
topic: "test"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'subscribeWithParam',
|
name: 'subscribeWithParam',
|
||||||
hook: async(param, callback):Promise<SubscriptionResponse<{topic:string}>> => {
|
hook: async (param, callback): Promise<{ uuid: string }> => {
|
||||||
|
|
||||||
if(param != "OK"){
|
if (param != "OK") {
|
||||||
console.log("param was"+ param);
|
console.log("param was" + param);
|
||||||
return {
|
return {
|
||||||
result: "Success",
|
|
||||||
uuid: "no",
|
uuid: "no",
|
||||||
topic: "test"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cb2 = <Function> callback
|
cb2 = <Function>callback
|
||||||
return {
|
return {
|
||||||
result: "Success",
|
|
||||||
uuid: "OK",
|
uuid: "OK",
|
||||||
topic: "test"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function checkCandy():string { cb(candy); return candy },
|
function publish(): string { cb(candy); return candy },
|
||||||
function stealCandy():string { candy = "_OK"; cb(candy); cb = () => {}; return candy }
|
function unsubscribe(): string { candy = noCandy; cb(candy); cb = () => { }; return candy }
|
||||||
]
|
]
|
||||||
}],{
|
}], {
|
||||||
connectionHandler: (socket) => { },
|
connectionHandler: (socket) => { },
|
||||||
closeHandler: (socket) => { },
|
closeHandler: (socket) => { },
|
||||||
errorHandler: (socket, err) => { throw err }
|
errorHandler: (socket, err) => { throw err }
|
||||||
@@ -231,67 +225,88 @@ describe('It should do unhook', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('Subscribe with param', (done) => {
|
it('Subscribe with param', (done) => {
|
||||||
client['test'].subscribeWithParam("OK", c => {}).then( async (res: SubscriptionResponse) => {
|
client['test'].subscribeWithParam("OK", c => { }).then(async (res) => {
|
||||||
if(res.uuid === "OK"){
|
if (res.uuid === candy) {
|
||||||
done()
|
done()
|
||||||
}else
|
} else
|
||||||
done(new Error("Results did not match "+res.uuid))
|
done(new Error("Results did not match " + res.uuid))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let run = 0
|
||||||
|
const expected = [yesCandy, noCandy, noCandy, noCandy]
|
||||||
|
|
||||||
it('Unhook+unsubscribe should stop callbacks', (done) => {
|
it('Unhook+unsubscribe should stop callbacks', (done) => {
|
||||||
client['test'].subscribe(c => {}).then( async (res: SubscriptionResponse) => {
|
|
||||||
const r1 = await client['test'].checkCandy()
|
|
||||||
const r3 = await client['test'].stealCandy()
|
|
||||||
client.unhook(res.uuid)
|
|
||||||
const r2 = await client['test'].checkCandy()
|
|
||||||
const r4 = await client['test'].checkCandy()
|
|
||||||
|
|
||||||
if(r1 === "OK" && r3 === "_OK" && r2 === "_OK" && r4 === "_OK")
|
client['test'].subscribe(function myCallback(c){
|
||||||
|
if(run == 1)
|
||||||
|
(myCallback as any).destroy()
|
||||||
|
|
||||||
|
if (c !== expected[run++]) {
|
||||||
|
done(new Error(`Wrong candy '${c}' in iteration '${run - 1}'`))
|
||||||
|
}
|
||||||
|
}).then(async function(res){
|
||||||
|
const r1 = await client['test'].publish()
|
||||||
|
const r3 = await client['test'].unsubscribe()
|
||||||
|
const r2 = await client['test'].publish()
|
||||||
|
const r4 = await client['test'].publish()
|
||||||
|
|
||||||
|
if (r1 === yesCandy && r3 === noCandy && r2 === noCandy && r4 === noCandy)
|
||||||
done()
|
done()
|
||||||
else
|
else
|
||||||
done(new Error("Results did not match: "+[r1,r2,r3,r4]))
|
done(new Error("Results did not match: " + [r1, r2, r3, r4]))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
type topicDTO = { topic: string; }
|
||||||
|
|
||||||
type SesameTestIfc = {
|
type SesameTestIfc = {
|
||||||
test: {
|
test: {
|
||||||
checkCandy: ()=>Promise<string>
|
checkCandy: () => Promise<string>
|
||||||
subscribe: (callback) => Promise<SubscriptionResponse<{ topic: string; }>>
|
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', () => {
|
describe('Sesame should unlock the socket', () => {
|
||||||
let candy = "OK"
|
let candy = "OK"
|
||||||
let client: RPCSocket & SesameTestIfc
|
let client: ConnectedSocket<SesameTestIfc>
|
||||||
let server: RPCServer
|
let server: RPCServer<SesameTestIfc>
|
||||||
let cb = (...args) => {}
|
let cb: Function = (...args) => { }
|
||||||
|
|
||||||
before((done) => {
|
before((done) => {
|
||||||
server = new RPCServer(21004, [{
|
server = new RPCServer<SesameTestIfc>(21004, [{
|
||||||
name: "test",
|
name: "test",
|
||||||
exportRPCs: () => [
|
exportRPCs: () => [
|
||||||
{
|
{
|
||||||
name: 'subscribe',
|
name: 'subscribe',
|
||||||
hook: async(callback) => {
|
hook: async (callback) => {
|
||||||
cb = callback
|
cb = callback
|
||||||
return <SubscriptionResponse>{
|
return {
|
||||||
result: "Success",
|
|
||||||
uuid: uuidv4(),
|
|
||||||
topic: 'test'
|
topic: 'test'
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
async function checkCandy():Promise<string> { cb(candy); cb=()=>{}; return candy },
|
onClose: (a) => { }
|
||||||
async function manyParams(a,b,c,d) {return [a,b,c,d]}
|
},
|
||||||
]}
|
async function checkCandy() { cb(candy); cb = () => { }; return candy },
|
||||||
],{
|
async function manyParams(a, b, c, d) { return [a, b, c, d] }
|
||||||
|
],
|
||||||
|
},{
|
||||||
|
name: 'other',
|
||||||
|
exportRPCs: () => [
|
||||||
|
async function echo(x){return x}
|
||||||
|
]
|
||||||
|
|
||||||
|
}], {
|
||||||
sesame: (_sesame) => _sesame === 'sesame!'
|
sesame: (_sesame) => _sesame === 'sesame!'
|
||||||
})
|
})
|
||||||
const sock = new RPCSocket(21004, "localhost")
|
const sock = new RPCSocket<SesameTestIfc>(21004, "localhost")
|
||||||
sock.connect<SesameTestIfc>('sesame!').then(cli => {
|
sock.connect('sesame!').then(cli => {
|
||||||
client = cli
|
client = cli
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
@@ -307,8 +322,8 @@ describe('Sesame should unlock the socket', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should work with multiple params', (done) => {
|
it('should work with multiple params', (done) => {
|
||||||
client.test['manyParams']('a','b','c','d').then(c => {
|
client.test['manyParams']('a', 'b', 'c', 'd').then(c => {
|
||||||
if(c[0] == 'a' && c[1] === 'b' && c[2] === 'c' && c[3] === 'd')
|
if (c[0] == 'a' && c[1] === 'b' && c[2] === 'c' && c[3] === 'd')
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -316,9 +331,9 @@ 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<SesameTestIfc>( /* no sesame */).then(async (cli) => {
|
sock.connect<SesameTestIfc>( /* no sesame */).then(async (cli) => {
|
||||||
if(!cli.test)
|
if (!cli.test)
|
||||||
done()
|
done()
|
||||||
else{
|
else {
|
||||||
done(new Error("Function supposed to be removed without sesame"))
|
done(new Error("Function supposed to be removed without sesame"))
|
||||||
}
|
}
|
||||||
cli.destroy()
|
cli.destroy()
|
||||||
@@ -329,9 +344,9 @@ describe('Sesame should unlock the socket', () => {
|
|||||||
it('should fail with wrong sesame', (done) => {
|
it('should fail with wrong sesame', (done) => {
|
||||||
const sock = new RPCSocket(21004, "localhost")
|
const sock = new RPCSocket(21004, "localhost")
|
||||||
sock.connect<SesameTestIfc>('abasd').then(async (cli) => {
|
sock.connect<SesameTestIfc>('abasd').then(async (cli) => {
|
||||||
if(!cli.test)
|
if (!cli.test)
|
||||||
done()
|
done()
|
||||||
else{
|
else {
|
||||||
done(new Error("Function supposed to be removed without sesame"))
|
done(new Error("Function supposed to be removed without sesame"))
|
||||||
}
|
}
|
||||||
cli.destroy()
|
cli.destroy()
|
||||||
@@ -341,12 +356,12 @@ describe('Sesame should unlock the socket', () => {
|
|||||||
|
|
||||||
it('callback should work with sesame', (done) => {
|
it('callback should work with sesame', (done) => {
|
||||||
client.test.subscribe((c) => {
|
client.test.subscribe((c) => {
|
||||||
if(c === candy){
|
if (c === candy) {
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
}).then(d => {
|
}).then(d => {
|
||||||
if(d.result !== 'Success')
|
if (d.topic !== 'test')
|
||||||
done('unexpected valid response')
|
done('unexpected invalid response')
|
||||||
|
|
||||||
client.test.checkCandy()
|
client.test.checkCandy()
|
||||||
})
|
})
|
||||||
@@ -354,35 +369,39 @@ describe('Sesame should unlock the socket', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
describe('Error handling', ()=>{
|
describe('Error handling', () => {
|
||||||
|
const errtxt = "BAD BAD BAD"
|
||||||
|
|
||||||
let createUser = async( user: {a:any,b:any}) => {
|
let createUser = async (user: { a: any, b: any }) => {
|
||||||
throw new Error("BAD BAD BAD")
|
throw new Error(errtxt)
|
||||||
}
|
}
|
||||||
|
|
||||||
it("RPC throws on client without handler", (done)=>{
|
it("RPC throws on client without handler", (done) => {
|
||||||
let server = new RPCServer(21004, [ {
|
let server = new RPCServer(21004, [{
|
||||||
name: 'createUser' as 'createUser',
|
name: "createUser",
|
||||||
exportRPCs: () => [{
|
exportRPCs: () => [{
|
||||||
name: 'createUser' as 'createUser',
|
name: 'createUser' as 'createUser',
|
||||||
call: createUser
|
call: createUser
|
||||||
}]}], {
|
}]
|
||||||
|
}], {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
let sock = new RPCSocket(21004, 'localhost')
|
let sock = new RPCSocket(21004, 'localhost')
|
||||||
sock.connect().then((cli) => {
|
sock.connect().then((cli) => {
|
||||||
cli["createUser"]["createUser"]({
|
cli["createUser"]["createUser"]({
|
||||||
a:'a',
|
a: 'a',
|
||||||
b:'b'
|
b: 'b'
|
||||||
})
|
})
|
||||||
.then(r => {
|
.then(r => {
|
||||||
if(r != null)
|
if (r != null)
|
||||||
done("UNEXPECTED RESULT " + r)
|
done(new Error("UNEXPECTED RESULT " + r))
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
//console.log("EXPECTED CLIENT EXCEPTION", String(e));
|
if (e.message === errtxt)
|
||||||
done()
|
done()
|
||||||
|
else
|
||||||
|
done(e)
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
cli.destroy()
|
cli.destroy()
|
||||||
@@ -392,13 +411,14 @@ describe('Error handling', ()=>{
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("RPC throws on server with handler", (done)=>{
|
it("RPC throws on server with handler", (done) => {
|
||||||
let server = new RPCServer(21004, [ {
|
let server = new RPCServer(21004, [{
|
||||||
name: 'createUser' as 'createUser',
|
name: "createUser",
|
||||||
exportRPCs: () => [{
|
exportRPCs: () => [{
|
||||||
name: 'createUser' as 'createUser',
|
name: 'createUser' as 'createUser',
|
||||||
call: createUser
|
call: createUser
|
||||||
}]}], {
|
}]
|
||||||
|
}], {
|
||||||
errorHandler: (socket, e, rpcName, args) => {
|
errorHandler: (socket, e, rpcName, args) => {
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
@@ -407,11 +427,11 @@ describe('Error handling', ()=>{
|
|||||||
let sock = new RPCSocket(21004, 'localhost')
|
let sock = new RPCSocket(21004, 'localhost')
|
||||||
sock.connect().then((cli) => {
|
sock.connect().then((cli) => {
|
||||||
cli["createUser"]["createUser"]({
|
cli["createUser"]["createUser"]({
|
||||||
a:'a',
|
a: 'a',
|
||||||
b:'b'
|
b: 'b'
|
||||||
})
|
})
|
||||||
.then(r => {
|
.then(r => {
|
||||||
if(r != null)
|
if (r != null)
|
||||||
done("UNEXPECTED RESULT " + r)
|
done("UNEXPECTED RESULT " + r)
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
@@ -428,20 +448,23 @@ describe('Error handling', ()=>{
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
describe("Errorhandler functionality", ()=>{
|
describe("Errorhandler functionality", () => {
|
||||||
let createUser = async( user: {a:any,b:any}) => {
|
const errtxt = "BAD BAD BAD"
|
||||||
throw new Error("BAD BAD BAD")
|
|
||||||
|
let createUser = async (user: { a: any, b: any }) => {
|
||||||
|
throw new Error(errtxt)
|
||||||
}
|
}
|
||||||
|
|
||||||
it("correct values are passed to the handler", (done)=>{
|
it("correct values are passed to the handler", (done) => {
|
||||||
let server = new RPCServer(21004, [ {
|
let server = new RPCServer(21004, [{
|
||||||
name: 'createUser' as 'createUser',
|
name: "createUser",
|
||||||
exportRPCs: () => [{
|
exportRPCs: () => [{
|
||||||
name: 'createUser' as 'createUser',
|
name: 'createUser' as 'createUser',
|
||||||
call: createUser
|
call: createUser
|
||||||
}]}], {
|
}]
|
||||||
|
}], {
|
||||||
errorHandler: (socket, e, rpcName, args) => {
|
errorHandler: (socket, e, rpcName, args) => {
|
||||||
if(e.message === "BAD BAD BAD" && rpcName === "createUser" && args[0]['a'] === 'a' && args[0]['b'] === 'b')
|
if (e.message === errtxt && rpcName === "createUser" && args[0]['a'] === 'a' && args[0]['b'] === 'b')
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -449,16 +472,15 @@ describe("Errorhandler functionality", ()=>{
|
|||||||
let sock = new RPCSocket(21004, 'localhost')
|
let sock = new RPCSocket(21004, 'localhost')
|
||||||
sock.connect().then((cli) => {
|
sock.connect().then((cli) => {
|
||||||
cli["createUser"]["createUser"]({
|
cli["createUser"]["createUser"]({
|
||||||
a:'a',
|
a: 'a',
|
||||||
b:'b'
|
b: 'b'
|
||||||
})
|
})
|
||||||
.then(r => {
|
.then(r => {
|
||||||
if(r != null)
|
if (r != null)
|
||||||
done("UNEXPECTED RESULT " + r)
|
done("UNEXPECTED RESULT " + r)
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
done("UNEXPECTED CLIENT ERROR " + e)
|
done(new Error("UNEXPECTED CLIENT ERROR " + e.message))
|
||||||
done(e)
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
cli.destroy()
|
cli.destroy()
|
||||||
@@ -468,17 +490,18 @@ describe("Errorhandler functionality", ()=>{
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("handler sees sesame", (done)=>{
|
it("handler sees sesame", (done) => {
|
||||||
let sesame = "AAAAAAAAAAAAAAA"
|
let sesame = "AAAAAAAAAAAAAAA"
|
||||||
let server = new RPCServer(21004, [ {
|
let server = new RPCServer(21004, [{
|
||||||
name: 'createUser' as 'createUser',
|
name: "createUser" as "createUser",
|
||||||
exportRPCs: () => [{
|
exportRPCs: () => [{
|
||||||
name: 'createUser' as 'createUser',
|
name: 'createUser' as 'createUser',
|
||||||
call: createUser
|
call: createUser
|
||||||
}]}], {
|
}]
|
||||||
|
}], {
|
||||||
sesame: sesame,
|
sesame: sesame,
|
||||||
errorHandler: (socket, e, rpcName, args) => {
|
errorHandler: (socket, e, rpcName, args) => {
|
||||||
if(e.message === "BAD BAD BAD" && rpcName === "createUser" && args[0] === sesame && args[1]['a'] === 'a' && args[1]['b'] === 'b')
|
if (e.message === errtxt && rpcName === "createUser" && args[0] === sesame && args[1]['a'] === 'a' && args[1]['b'] === 'b')
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,11 +510,11 @@ describe("Errorhandler functionality", ()=>{
|
|||||||
let sock = new RPCSocket(21004, 'localhost')
|
let sock = new RPCSocket(21004, 'localhost')
|
||||||
sock.connect(sesame).then((cli) => {
|
sock.connect(sesame).then((cli) => {
|
||||||
cli["createUser"]["createUser"]({
|
cli["createUser"]["createUser"]({
|
||||||
a:'a',
|
a: 'a',
|
||||||
b:'b'
|
b: 'b'
|
||||||
})
|
})
|
||||||
.then(r => {
|
.then(r => {
|
||||||
if(r != null)
|
if (r != null)
|
||||||
done("UNEXPECTED RESULT " + r)
|
done("UNEXPECTED RESULT " + r)
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
@@ -509,32 +532,32 @@ describe("Errorhandler functionality", ()=>{
|
|||||||
|
|
||||||
type myExporterIfc = {
|
type myExporterIfc = {
|
||||||
MyExporter: {
|
MyExporter: {
|
||||||
myRPC: ()=>Promise<string>
|
myRPC: () => Promise<string>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
describe("Class binding", ()=>{
|
describe("Class binding", () => {
|
||||||
|
|
||||||
let exporter1 : MyExporter
|
let exporter1: MyExporter
|
||||||
let serv : RPCServer<{}, myExporterIfc>
|
let serv: RPCServer<myExporterIfc>
|
||||||
let sock: RPCSocket & myExporterIfc
|
let sock: RPCSocket & myExporterIfc
|
||||||
let allowed = true
|
let allowed = true
|
||||||
|
|
||||||
class MyExporter implements RPCExporter<myExporterIfc>{
|
class MyExporter implements RPCExporter<myExporterIfc>{
|
||||||
name = "MyExporter" as "MyExporter";
|
name = "MyExporter" as "MyExporter"
|
||||||
exportRPCs = () => [
|
exportRPCs = () => [
|
||||||
this.myRPC
|
this.myRPC
|
||||||
]
|
]
|
||||||
|
|
||||||
myRPC = async () => {
|
myRPC = async () => {
|
||||||
serv.setExporters([new MyOtherExporter])
|
//serv.setExporters([new MyOtherExporter])
|
||||||
return "Hello World"
|
return "Hello World"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyOtherExporter implements RPCExporter<myExporterIfc>{
|
class MyOtherExporter implements RPCExporter<myExporterIfc>{
|
||||||
name = "MyExporter" as "MyExporter";
|
name = "MyExporter" as "MyExporter"
|
||||||
exportRPCs = () => [
|
exportRPCs = () => [
|
||||||
this.myRPC
|
this.myRPC
|
||||||
]
|
]
|
||||||
@@ -547,14 +570,13 @@ describe("Class binding", ()=>{
|
|||||||
|
|
||||||
before(done => {
|
before(done => {
|
||||||
exporter1 = new MyExporter()
|
exporter1 = new MyExporter()
|
||||||
serv = new RPCServer<{}, myExporterIfc>(21004, [exporter1], {
|
serv = new RPCServer<myExporterIfc>(21004, [exporter1], {
|
||||||
accessFilter: async (sesame,exporter) => {
|
accessFilter: async (sesame, exporter) => {
|
||||||
switch(exporter.name){
|
if(exporter.name === 'MyExporter'){
|
||||||
case "MyExporter":
|
if (!allowed) return false
|
||||||
if(!allowed) return false
|
|
||||||
allowed = false
|
allowed = false
|
||||||
return sesame==='xxx';
|
return sesame === 'xxx';
|
||||||
default:
|
}else{
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -563,8 +585,7 @@ describe("Class binding", ()=>{
|
|||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach((done)=>{
|
beforeEach((done) => {
|
||||||
|
|
||||||
const s = new RPCSocket(21004, 'localhost')
|
const s = new RPCSocket(21004, 'localhost')
|
||||||
s.connect<myExporterIfc>("xxx").then(conn => {
|
s.connect<myExporterIfc>("xxx").then(conn => {
|
||||||
sock = conn
|
sock = conn
|
||||||
@@ -572,7 +593,7 @@ describe("Class binding", ()=>{
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(done => {
|
afterEach((done) => {
|
||||||
sock.destroy()
|
sock.destroy()
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
@@ -581,7 +602,11 @@ describe("Class binding", ()=>{
|
|||||||
serv.destroy()
|
serv.destroy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("binds correctly", (done)=>{
|
/* 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) => {
|
sock['MyExporter'].myRPC().then((res) => {
|
||||||
done(new Error(res))
|
done(new Error(res))
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
@@ -592,31 +617,42 @@ describe("Class binding", ()=>{
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("changes exporters", (done) => {
|
it("changes exporters", (done) => {
|
||||||
|
|
||||||
sock['MyExporter'].myRPC().then((res) => {
|
sock['MyExporter'].myRPC().then((res) => {
|
||||||
if(res === "Hello Borld")
|
if (res === "Hello Borld")
|
||||||
done()
|
done()
|
||||||
else
|
else
|
||||||
done(new Error(res))
|
done(new Error(res))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
it("use sesameFilter for available", (done) => {
|
||||||
|
if (sock['MyExporter']){
|
||||||
|
allowed = false
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
else done(new Error("RPC supposed to be here"))
|
||||||
|
})
|
||||||
|
|
||||||
it("use sesameFilter", (done) => {
|
it("use sesameFilter", (done) => {
|
||||||
if(!sock['MyExporter']) done()
|
if (!sock['MyExporter']) done()
|
||||||
else done(new Error("RPC supposed to be gone"))
|
else done(new Error("RPC supposed to be gone"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
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) => {
|
||||||
const sock = new RPCSocket(21004, 'localhost')
|
const sock = new RPCSocket(21004, 'localhost')
|
||||||
let errorHandleCount = 0
|
let errorHandleCount = 0
|
||||||
|
|
||||||
sock.on('error', (err) => {
|
sock.on('error', (err) => {
|
||||||
//attached listener fires first
|
//attached listener fires first
|
||||||
if(errorHandleCount != 0){
|
if (errorHandleCount != 0) {
|
||||||
console.log("Error handler didn't fire first");
|
console.log("Error handler didn't fire first");
|
||||||
}else{
|
} else {
|
||||||
errorHandleCount++
|
errorHandleCount++
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -625,16 +661,16 @@ describe("attaching handlers before connecting", ()=>{
|
|||||||
console.log("Unexpected successful connect")
|
console.log("Unexpected successful connect")
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
//catch clause fires second
|
//catch clause fires second
|
||||||
if(errorHandleCount != 1){
|
if (errorHandleCount != 1) {
|
||||||
console.log("catch clause didn't fire second");
|
console.log("catch clause didn't fire second");
|
||||||
}else{
|
} else {
|
||||||
sock.destroy()
|
sock.destroy()
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
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(21004)
|
||||||
const sock = new RPCSocket(21004, 'localhost')
|
const sock = new RPCSocket(21004, 'localhost')
|
||||||
|
|
||||||
@@ -645,14 +681,14 @@ describe("attaching handlers before connecting", ()=>{
|
|||||||
})
|
})
|
||||||
|
|
||||||
sock.connect().then(_ => {
|
sock.connect().then(_ => {
|
||||||
sock.call("unknownRPC123", "AAAAA").catch(e => { /* ignore */})
|
sock.call("unknownRPC123", "AAAAA").catch(e => { /* ignore */ })
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
console.log("unexpected connect catch clause");
|
console.log("unexpected connect catch clause");
|
||||||
done(e)
|
done(e)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("demands catch on method invocation if call is unknown", (done)=>{
|
it("demands catch on method invocation if call is unknown", (done) => {
|
||||||
const serv = new RPCServer(21004)
|
const serv = new RPCServer(21004)
|
||||||
const sock = new RPCSocket(21004, 'localhost')
|
const sock = new RPCSocket(21004, 'localhost')
|
||||||
|
|
||||||
@@ -667,4 +703,11 @@ describe("attaching handlers before connecting", ()=>{
|
|||||||
done(e)
|
done(e)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('finally', () => {
|
||||||
|
it('print open handles (Ignore `DNSCHANNEL` and `Immediate`)', () => {
|
||||||
|
log()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
import { RPCServer } from "../src/Backend";
|
|
||||||
import { SubscriptionResponse, RPCInterface } from "../src/Types";
|
|
||||||
import { RPCSocket } from "../src/Frontend";
|
|
||||||
import { makeSubResponse } from "../src/Utils";
|
|
||||||
|
|
||||||
type SubresExtension = {a:string}
|
|
||||||
|
|
||||||
type MyInterface = {
|
|
||||||
Group1: {
|
|
||||||
triggerCallbacks: (...args:any[]) => Promise<void>,
|
|
||||||
subscribe: (param:string, callback:Function) => Promise<SubscriptionResponse<SubresExtension>>,
|
|
||||||
unsubscribe: (uuid:string) => Promise<void>
|
|
||||||
},
|
|
||||||
Group2: {
|
|
||||||
echo: (x:string) => Promise<string>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new RPCServer<SubresExtension, MyInterface>(20000,
|
|
||||||
[{
|
|
||||||
name: 'Group1',
|
|
||||||
exportRPCs: () => [{
|
|
||||||
name: 'triggerCallbacks',
|
|
||||||
call: async () => { /*...*/ }
|
|
||||||
},{
|
|
||||||
name: 'subscribe',
|
|
||||||
hook: async (param, callback) => { return makeSubResponse<SubresExtension>({a: "test"}) }
|
|
||||||
},{
|
|
||||||
name: 'unsubscribe',
|
|
||||||
call: async(uuid) => { }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
name: 'Group2',
|
|
||||||
exportRPCs: () => [{
|
|
||||||
name: 'echo',
|
|
||||||
call: async (x) => "..."
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
)
|
|
||||||
|
|
||||||
RPCSocket.makeSocket<MyInterface>(20000, 'localhost').then((async (client) => {
|
|
||||||
console.log(client)
|
|
||||||
const res = await client.Group1.subscribe('test', async (...args:any) => {
|
|
||||||
console.log.apply(console, args)
|
|
||||||
|
|
||||||
/* close the callbacks once you're done */
|
|
||||||
await client.Group1.unsubscribe(res.uuid)
|
|
||||||
client.unhook(res.uuid)
|
|
||||||
})
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user