socket onclose

This commit is contained in:
peter
2019-07-06 18:32:19 +02:00
parent 91d1dcf9f7
commit 0a779fcd34
14 changed files with 381 additions and 451 deletions
@@ -2,28 +2,37 @@
import * as Logger from 'log4js'
import { Plugin, socketioRPC } from 'frontblock-generic/Plugin';
import { ErrorResponse, SuccessResponse } from 'frontblock-generic/Service';
import { ErrorResponse, SuccessResponse } from 'frontblock-generic/Types';
import { FrontblockApiConf } from "frontblock/FrontblockApiClient"
const pluginList = [
'../../paymentmanager/static/Plugin',
'frontblock/FrontblockLib'
type pluginEntry = {pluginPath:string, conf?:any}
const pluginList:pluginEntry[] = [
{ pluginPath: '../../paymentmanager/static/Plugin', conf:<FrontblockApiConf>{ apiHost: /*'localhost'*/'api.testnet.frontblock.me', apiPort: 10001 } },
{ pluginPath: '../../htmlsupplier/static/Plugin' },
{ pluginPath: 'frontblock/FrontblockApiClient', conf:<FrontblockApiConf>{ apiHost: /*'localhost'*/'api.testnet.frontblock.me', apiPort: 10001 } }
]
const express = require('express')
const http = require('http')
const bsock = require('bsock')
const kfs = require("key-file-storage")('conf') //'conf' is a directory that will be generated if it doesn't exist
const kfs = require("key-file-storage").default('conf') //'conf' is a directory that will be generated if it doesn't exist
Logger.configure({
appenders: {
"admin": { type: 'stdout' },
//app: { type: 'file', filename: 'application.log' }
},
categories: {
default: { appenders: [ 'admin' ], level: 'debug' }
}
})
const logger = Logger.getLogger("admin")
const logger = Logger.getLogger() // logs to STDOUT
logger.level = 'debug'
type hookRpc = { type: 'hook', generator: (socket) => Function, unhook:(uid:string)=>Promise<ErrorResponse|SuccessResponse>}
type hookRpc = { type: 'hook', generator: (socket) => Function, unhook:(uid:string)=>Promise<ErrorResponse|SuccessResponse>}
type unhookRPC = { type: 'unhook', fn: Function}
type callRPC = { type: 'call', fn: Function}
type callRPC = { type: 'call', fn: Function}
export type rpcInfo = {
owner: string,
name: string,
args: string,
info: hookRpc | unhookRPC | callRPC
@@ -40,15 +49,17 @@ export type rpcInfo = {
export class FrontblockAdmin{
private plugins: Plugin[]
private hookToUids:{[hookName:string]:string[]} = {}
private express
private httpServer
private io = bsock.createServer()
private wsServer = http.createServer()
constructor(){
if(!('FrontblockService' in kfs)){
if(!('FrontblockAdmin' in kfs)){
logger.warn('No config file found! Generating one')
kfs.FrontblockService = { httpPort: 8080 }
kfs.FrontblockAdmin = { httpPort: 8080 }
}
this.initialize()
@@ -62,16 +73,19 @@ export class FrontblockAdmin{
}
private async loadPlugins(){
const promises = pluginList.map(path => {
return import(path)
this.plugins = []
pluginList.forEach(async (entry:pluginEntry) => {
const clazz = await import(entry.pluginPath)
let obj = new clazz.default(entry.conf)
this.plugins.push(obj)
})
const pluginsClasses = await Promise.all(promises)
this.plugins = pluginsClasses.map(clazz => new clazz.default())
}
private initApis(socket){
//Declare own functions
const rpcInfos:rpcInfo[] = [
{
owner: 'Admin',
name: 'restartWebserver',
args: 'port',
info:{
@@ -79,6 +93,7 @@ export class FrontblockAdmin{
fn: (port:number) => { this.restartWebserver(port) }
}
},{
owner: 'Admin',
name: 'info',
args: '',
info:{
@@ -88,10 +103,16 @@ export class FrontblockAdmin{
}
]
//translate RPCs to socket-bound function metadata
this.plugins.forEach(plugin => {
plugin.exportRPCs().forEach(rpc => rpcInfos.push(this.rpcToRpcInfo(rpc)))
const pluginName = plugin.name
plugin.exportRPCs().forEach(rpc => {
const info = this.rpcToRpcInfo(pluginName, rpc)
rpcInfos.push(info)
})
})
//Hook up all the functions
for(const api of rpcInfos){
switch(api.info.type){
case 'call':
@@ -108,13 +129,29 @@ export class FrontblockAdmin{
}
}
//initialize the lists of open hooks
rpcInfos
.filter(rpc => rpc.info.type === "hook")
.forEach(hook => {
this.hookToUids[hook.name] = []
})
//On close, unhook open hooks
socket.on('close', () => {
logger.info("Client disconnected")
rpcInfos.forEach(rpc => {
socket.unhook(rpc.name)
rpcInfos.forEach((rpc) => {
if(this.hookToUids[rpc.name] == null)
return
this.hookToUids[rpc.name].forEach((uid) => {
if(rpc.info.type === "hook"){
logger.info("Closing consumer `"+uid+"` owned by `"+rpc.name+"`")
rpc.info.unhook(uid)
}
})
})
})
}
private startWebserver(){
@@ -123,13 +160,13 @@ export class FrontblockAdmin{
return
}
let port:number = kfs.FrontblockService.httpPort
let port:number = kfs.FrontblockAdmin.httpPort
this.express = express()
this.express.use(express.static('static'))
this.httpServer = http.Server(this.express)
this.httpServer.listen(port, () => {
logger.info('listening on *'+port)
logger.info('Admin panel listening for HTTP on *'+port)
})
}
@@ -146,7 +183,7 @@ export class FrontblockAdmin{
private restartWebserver(port:number){
this.stopWebserver()
kfs.FrontblockService = { httpPort: port }
kfs.FrontblockAdmin = { httpPort: port }
this.startWebserver()
}
@@ -164,22 +201,22 @@ export class FrontblockAdmin{
socket.on('error', handleError)
this.initApis(socket)
})
logger.info('websocket listening on *20000')
logger.info('Admin websocket listening on *20000')
this.wsServer.listen(20000)
}catch(e){
logger.error(String(e))
}
}
private rpcToRpcInfo(rpc:socketioRPC):rpcInfo{
private rpcToRpcInfo(owner:string, rpc:socketioRPC):rpcInfo{
switch(rpc.type){
case 'hook':
let f = this.hookGenerator(rpc)
return {name: rpc.name, args: this.extractArgs(f(null)), info: { type: 'hook', generator: f, unhook: rpc.unhook } }
return {owner: owner, name: rpc.name, args: this.extractArgs(f(null)), info: { type: 'hook', generator: f, unhook: rpc.unhook } }
case 'unhook':
return {name: rpc.name, args: this.extractArgs(rpc.rpc), info: { type: 'unhook', fn: rpc.rpc } }
return {owner: owner, name: rpc.name, args: this.extractArgs(rpc.rpc), info: { type: 'unhook', fn: rpc.rpc } }
case 'call':
return {name: rpc.name, args: this.extractArgs(rpc.rpc), info: { type: 'call', fn: rpc.rpc } }
return {owner: owner, name: rpc.name, args: this.extractArgs(rpc.rpc), info: { type: 'call', fn: rpc.rpc } }
}
}
@@ -187,24 +224,21 @@ export class FrontblockAdmin{
* Generates RPC hooks which support a callback.
*
* Note callbacks *need* to accept a singular argument and they have to be the last parameter!
*
* TODO: Maybe kill open callbacks when socket closes? See rpc.info.unhook for the appropriate function to call.
*/
hookGenerator = (rpc) => {
hookGenerator = (rpc:socketioRPC) => {
const argsArr = this.extractArgs(rpc.rpc).split(',')
argsArr.pop()
const args = argsArr.join(',')
return eval(`(socket) => async (`+args+`) => {
const res = await rpc.rpc(`+args+(args.length!==0?',':'')+` (x) => {
if(res.uid != null){
console.log("calling "+res.uid)
socket.call(res.uid, x).catch(e => {
rpc.unhook(res.uid)
})
}
socket.call(res.uid, x).catch(e => {
logger.debug(String(e))
})
})
if(res.uid != null){
this.hookToUids[rpc.name].push(res.uid)
}
return res
}`)
}
+5 -4
View File
@@ -1,4 +1,4 @@
import { parseSubResponse, parseResponse } from "frontblock-generic/Service";
import { parseSubResponse, parseResponse } from "frontblock-generic/Types";
var bsock = require('bsock')
/**
@@ -36,9 +36,10 @@ export class FrontblockConfigLib{
f = this.unhookGenerator(i.name, i.args)
break
}
this[i.name] = f
this[i.name].bind(this)
if(this[i.owner] == null)
this[i.owner] = {}
this[i.owner][i.name] = f
this[i.owner][i.name].bind(this)
}
}