message-relay-services
Version:
A simple server to route message to other services, such as email, admin, etc.
214 lines (176 loc) • 6.21 kB
JavaScript
'use strict';
const Communicator = require('./common_communicator')
const {spawn} = require('child_process')
const DEFAULT_MAX_RECONNECT = 20
const DEFAULT_RECONNECT_WAIT = 5
// ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
function spawn_child(proc_name) {
let cproc = false
if ( Array.isArray(proc_name) ) {
let proc = proc_name.shift()
cproc = spawn(proc,proc_name,{ "stdio" : ["pipe","pipe","pipe","ipc"]})
} else {
cproc = spawn('node',[proc_name],{ "stdio" : ["pipe","pipe","pipe","ipc"]})
}
if ( cproc ) {
cproc.stderr.on('data',(data) => {
console.log(data.toString())
})
cproc.stdout.on('data',(data) => {
console.log(data.toString())
})
}
return cproc
}
const EventEmitter = require('events')
class ProcWriter extends EventEmitter {
constructor(a_proc) {
super()
// this is a child process sending generically to the parent the process.send
this._proc = a_proc
}
//
write(message) {
let msg = {
"pid" : process.pid,
"msg" : message
}
this._proc.send(msg)
}
}
// ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
class Client extends Communicator {
constructor(conf,wrapper) {
super(conf,wrapper)
}
// ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
_init_members(conf) {
//
this._proc = false
this.c_pid = false
this.conf = conf
//---- //---- //---- //---- //---- //----
this.send_on_reconnect = conf ? conf.send_on_reconnect || false : false
//
this.attempt_reconnect = false
this.reconnect_wait = DEFAULT_RECONNECT_WAIT
this.max_reconnect = DEFAULT_MAX_RECONNECT
this.reconnect_count = 0
}
// ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
_create_connection(conf) {
if ( this.files_only ) {
(async () => { await this._setup_file_output(conf) })()
} else {
this.attempt_reconnect = (conf.attempt_reconnect !== undefined) ? conf.attempt_reconnect : false
if ( this.attempt_reconnect ) {
this._configure_reconnect(conf)
}
this._connect()
this._setup_connection_handlers(this,conf)
}
}
//
_init(conf) {
if ( conf === undefined ) {
console.log("message relay client: cannot initialize -- no configuration")
return;
}
this._init_members(conf)
this._create_connection(conf)
}
// ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
// // // // // //
_connection_handler() {
if ( this._proc ) {
this.writer = new ProcWriter(this._proc)
this.wrap_event(this.c_pid)
this.reconnect_count = 0
console.log(`Client connected to: ${this.c_pid} : A Child Process`);
if ( this.files_going ) { // then shunting had to be set to true.. file_only has to be false
super.restore_send(this.send_on_reconnect)
}
this.emit('client-ready',this.c_pid,this.port)
} else {
throw new Error("relay ipc:: sub process not specified correctly")
}
}
// CONNECTION
_connect() {
// PUBLIC
if ( !(this._proc) ) {
this._proc = spawn_child(this.conf.proc_name)
this.c_pid = this._proc.pid
this._connection_handler()
}
//
}
// SET UP CONNECTION AND HANDLERS on('close'...) on('data'...) on('error'...)
// _setup_connection_handlers
//
_setup_connection_handlers(client,conf) {
//
// HANDLERS
let c_proc = client._proc
if ( c_proc ) {
c_proc.on('close', (onErr) => {
if ( onErr ) {
console.log(`got a closing error on ${c_proc.pid} a child process went down`)
}
this.unwrap_event(client.c_pid)
console.log('Client closed');
if ( client.attempt_reconnect ) {
client._attempt_reconnect(conf)
}
})
//
// this process is the parent process of c_proc
// when the child sends a message back, a message will be unlocked and then resolved
// when it unsolicite, it will attempt to satisfy a subscription
c_proc.on('message',(data) => {
client.client_add_data_and_react(data.msg)
});
//
c_proc.on('error',async (err) => {
this.unwrap_event(client.c_pid)
console.log(__filename)
console.log(err);
if ( client.attempt_reconnect ) {
if ( client.reconnect_count < client.max_reconnect ) {
return;
}
}
if ( client.file_shunting ) {
await client._start_file_shunting(conf)
}
})
//
}
}
// RECONNECTION ATTEMPTS
_configure_reconnect(conf) {
this.max_reconnect = (conf.max_reconnect !== undefined) ? conf.max_reconnect : this.max_reconnect
this.reconnect_wait = (conf.reconnect_wait !== undefined) ? conf.reconnect_wait : this.reconnect_wait
if ( typeof this.reconnect_wait === "string" ) {
this.reconnect_wait = parseInt(this.reconnect_wait)
}
this.reconnect_wait = this.reconnect_wait*1000
this.reconnect_count = 0
}
//
_attempt_reconnect(conf) {
this.reconnect_count++
if ( this.reconnect_count < this.max_reconnect ) {
this._connect()
this._setup_connection_handlers(this,conf)
}
}
closeAll() {
if ( this._proc ) {
this._proc.kill('SIGINT')
}
}
}
//
module.exports = Client;
module.exports.Communicator = Communicator