openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
1,061 lines (851 loc) • 33.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.setupCertificateWatcher = setupCertificateWatcher;
require('babel-polyfill');
var _cluster = require('cluster');
var _cluster2 = _interopRequireDefault(_cluster);
var _nconf = require('nconf');
var _nconf2 = _interopRequireDefault(_nconf);
var _atnaAudit = require('atna-audit');
var _atnaAudit2 = _interopRequireDefault(_atnaAudit);
var _os = require('os');
var _os2 = _interopRequireDefault(_os);
var _chokidar = require('chokidar');
var _chokidar2 = _interopRequireDefault(_chokidar);
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _http = require('http');
var _http2 = _interopRequireDefault(_http);
var _https = require('https');
var _https2 = _interopRequireDefault(_https);
var _tls = require('tls');
var _tls2 = _interopRequireDefault(_tls);
var _net = require('net');
var _net2 = _interopRequireDefault(_net);
var _dgram = require('dgram');
var _dgram2 = _interopRequireDefault(_dgram);
var _nodeUuid = require('node-uuid');
var _nodeUuid2 = _interopRequireDefault(_nodeUuid);
var _pem = require('pem');
var _pem2 = _interopRequireDefault(_pem);
var _winston = require('winston');
var _winston2 = _interopRequireDefault(_winston);
require('winston-mongodb');
var _agenda = require('agenda');
var _agenda2 = _interopRequireDefault(_agenda);
var _mongoose = require('mongoose');
var _mongoose2 = _interopRequireDefault(_mongoose);
var _koaMiddleware = require('./koaMiddleware');
var koaMiddleware = _interopRequireWildcard(_koaMiddleware);
var _koaApi = require('./koaApi');
var koaApi = _interopRequireWildcard(_koaApi);
var _tlsAuthentication = require('./middleware/tlsAuthentication');
var tlsAuthentication = _interopRequireWildcard(_tlsAuthentication);
var _users = require('./model/users');
var _keystore = require('./model/keystore');
var _alerts = require('./alerts');
var alerts = _interopRequireWildcard(_alerts);
var _reports = require('./reports');
var reports = _interopRequireWildcard(_reports);
var _polling = require('./polling');
var polling = _interopRequireWildcard(_polling);
var _tcpAdapter = require('./tcpAdapter');
var tcpAdapter = _interopRequireWildcard(_tcpAdapter);
var _auditing = require('./auditing');
var auditing = _interopRequireWildcard(_auditing);
var _tasks = require('./tasks');
var tasks = _interopRequireWildcard(_tasks);
var _upgradeDB = require('./upgradeDB');
var upgradeDB = _interopRequireWildcard(_upgradeDB);
var _autoRetry = require('./autoRetry');
var autoRetry = _interopRequireWildcard(_autoRetry);
var _bodyCull = require('./bodyCull');
var bodyCull = _interopRequireWildcard(_bodyCull);
var _config = require('./config');
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
_mongoose2.default.Promise = Promise;
_config.config.mongo = _config.config.get('mongo');
_config.config.authentication = _config.config.get('authentication');
_config.config.router = _config.config.get('router');
_config.config.api = _config.config.get('api');
_config.config.rerun = _config.config.get('rerun');
_config.config.tcpAdapter = _config.config.get('tcpAdapter');
_config.config.logger = _config.config.get('logger');
_config.config.mongoLogger = _config.config.get('mongoLogger');
_config.config.alerts = _config.config.get('alerts');
_config.config.polling = _config.config.get('polling');
_config.config.reports = _config.config.get('reports');
_config.config.auditing = _config.config.get('auditing');
_config.config.agenda = _config.config.get('agenda');
_config.config.certificateManagement = _config.config.get('certificateManagement');
_config.config.bodyCull = _config.config.get('bodyCull');
const himSourceID = _config.config.get('auditing').auditEvents.auditSourceID;
const currentVersion = require('../package.json').version;
const numCPUs = require('os').cpus().length;
let ensureKeystore;
_winston2.default.remove(_winston2.default.transports.Console);
let clusterArg = _nconf2.default.get('cluster');
function defer() {
const deferred = {
promise: null,
resolve: null,
reject: null
};
deferred.promise = new Promise((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
}
function setupCertificateWatcher() {
const certFile = _config.config.certificateManagement.certPath;
const keyFile = _config.config.certificateManagement.keyPath;
const watcher = _chokidar2.default.watch([certFile, keyFile], {
usePolling: true
}).on('ready', () => {
_winston2.default.info('Certificate/Key watch paths:', watcher.getWatched());
return watcher.on('change', (path, stats) => {
for (const id in _cluster2.default.workers) {
const worker = _cluster2.default.workers[id];
_winston2.default.debug(`Restarting worker ${worker.id}...`);
worker.send({
type: 'restart'
});
}
});
});
return watcher;
}
/* eslint no-inner-declarations: 0 */
// Configure clustering if relevent
if (_cluster2.default.isMaster && !module.parent) {
process.title = 'Core';
// configure master logger
let clusterSize;
_winston2.default.add(_winston2.default.transports.Console, {
colorize: true,
timestamp: true,
label: 'master',
level: _config.config.logger.level
});
if (_config.config.logger.logToDB === true) {
_winston2.default.add(_winston2.default.transports.MongoDB, {
db: _config.config.mongo.url,
label: 'master',
options: _config.config.mongoLogger.options,
level: 'debug',
capped: _config.config.logger.capDBLogs,
cappedSize: _config.config.logger.capSize
});
}
if (clusterArg == null) {
clusterArg = 1;
}
if (clusterArg === 'auto') {
clusterSize = numCPUs;
} else {
clusterSize = clusterArg;
}
if (typeof clusterSize !== 'number' || clusterSize % 1 !== 0 || clusterSize < 1) {
throw new Error(`invalid --cluster argument entered: ${clusterArg}. Please enter a positive number or 'auto'.`);
}
_winston2.default.info(`Running OpenHIM Core JS version ${currentVersion}`);
_winston2.default.info(`Clustering the OpenHIM with ${clusterSize} workers...`);
function addWorker() {
let worker = _cluster2.default.fork();
return worker.on('message', msg => {
let id;
_winston2.default.debug(`Message received from worker ${worker.id}`, msg);
if (msg.type === 'restart-all') {
// restart all workers
_winston2.default.debug('Restarting all workers...');
return (() => {
const result = [];
for (id in _cluster2.default.workers) {
worker = _cluster2.default.workers[id];
_winston2.default.debug(`Restarting worker ${worker.id}...`);
result.push(worker.send({
type: 'restart'
}));
}
return result;
})();
} else if (msg.type === 'start-tcp-channel') {
// start tcp channel on all workers
_winston2.default.info(`Starting TCP channel for channel: ${msg.channelID}`);
return (() => {
const result1 = [];
for (id in _cluster2.default.workers) {
worker = _cluster2.default.workers[id];
_winston2.default.debug(`Starting TCP channel on worker ${worker.id}...`);
result1.push(worker.send(msg));
}
return result1;
})();
} else if (msg.type === 'stop-tcp-channel') {
// stop tcp channel on all workers
_winston2.default.info(`Stopping TCP channel for channel: ${msg.channelID}`);
return (() => {
const result2 = [];
for (id in _cluster2.default.workers) {
worker = _cluster2.default.workers[id];
_winston2.default.debug(`Stopping TCP channel on worker ${worker.id}...`);
result2.push(worker.send(msg));
}
return result2;
})();
} else if (msg.type === 'get-uptime') {
// send response back to worker requesting uptime
return worker.send({
type: 'get-uptime',
masterUptime: process.uptime()
});
}
return [];
});
}
// upgrade the database if needed
upgradeDB.upgradeDb(() => {
// start all workers
for (let i = 1, end = clusterSize, asc = end >= 1; asc ? i <= end : i >= end; asc ? i++ : i--) {
addWorker();
}
_cluster2.default.on('exit', (worker, code, signal) => {
_winston2.default.warn(`worker ${worker.process.pid} died`);
if (!worker.suicide) {
// respawn
addWorker();
}
});
_cluster2.default.on('online', worker => _winston2.default.info(`worker with pid ${worker.process.pid} is online`));
return _cluster2.default.on('listening', (worker, address) => _winston2.default.debug(`worker ${worker.id} is now connected to ${address.address}:${address.port}`));
});
// setup watcher if watchFSForCert is enabled
if (_config.config.certificateManagement.watchFSForCert) {
exports.setupCertificateWatcher();
}
} else {
/* Setup Worker */
// configure worker logger
let stop;
_winston2.default.add(_winston2.default.transports.Console, {
colorize: true,
timestamp: true,
label: (_cluster2.default.worker != null ? _cluster2.default.worker.id : undefined) != null ? `worker${_cluster2.default.worker.id}` : undefined,
level: _config.config.logger.level
});
if (_config.config.logger.logToDB === true && _winston2.default.default.transports.mongodb == null) {
_winston2.default.add(_winston2.default.transports.MongoDB, {
db: _config.config.mongo.url,
options: _config.config.mongoLogger.options,
label: (_cluster2.default.worker != null ? _cluster2.default.worker.id : undefined) != null ? `worker${_cluster2.default.worker.id}` : undefined,
level: 'debug',
capped: _config.config.logger.capDBLogs,
cappedSize: _config.config.logger.capSize
});
}
let httpServer = null;
let httpsServer = null;
let apiHttpsServer = null;
let rerunServer = null;
let tcpHttpReceiver = null;
let pollingServer = null;
let auditUDPServer = null;
let auditTlsServer = null;
let auditTcpServer = null;
const activeHttpConnections = {};
const activeHttpsConnections = {};
const activeApiConnections = {};
const activeRerunConnections = {};
const activeTcpConnections = {};
const activePollingConnections = {};
function trackConnection(map, socket) {
// save active socket
const id = _nodeUuid2.default.v4();
map[id] = socket;
// remove socket once it closes
socket.on('close', () => {
map[id] = null;
return delete map[id];
});
// log any socket errors
return socket.on('error', err => _winston2.default.error(err));
}
exports.isTcpHttpReceiverRunning = () => tcpHttpReceiver != null;
const rootUser = {
firstname: 'Super',
surname: 'User',
email: 'root@openhim.org',
passwordAlgorithm: 'sha512',
passwordHash: '943a856bba65aad6c639d5c8d4a11fc8bb7fe9de62ae307aec8cf6ae6c1faab722127964c71db4bdd2ea2cdf60c6e4094dcad54d4522ab2839b65ae98100d0fb',
passwordSalt: 'd9bcb40e-ae65-478f-962e-5e5e5e7d0a01',
groups: ['admin']
// password = 'openhim-password'
// Job scheduler
};let agenda = null;
function startAgenda() {
const deferred = defer();
agenda = new _agenda2.default({
db: {
address: _config.config.mongo.url
}
});
agenda.on('start', job => _winston2.default.info(`starting job: ${job.attrs.name}, Last Ran at: ${job.attrs.lastRunAt}`));
agenda.on('fail', (err, job) => _winston2.default.error(`Job ${job.attrs.name} failed with ${err.message}`));
agenda.on('complete', job => _winston2.default.info(`Job ${job.attrs.name} has completed`));
agenda.on('ready', () => {
if (_config.config.alerts.enableAlerts) {
alerts.setupAgenda(agenda);
}
if (_config.config.reports.enableReports) {
reports.setupAgenda(agenda);
}
if (_config.config.bodyCull.enabled) {
bodyCull.setupAgenda(agenda);
}
autoRetry.setupAgenda(agenda);
if (_config.config.polling.enabled) {
return polling.setupAgenda(agenda, () =>
// give workers a change to setup agenda tasks
setTimeout(() => {
agenda.start();
deferred.resolve();
return _winston2.default.info('Started agenda job scheduler');
}, _config.config.agenda.startupDelay));
}
// Start agenda anyway for the other servers
agenda.start();
return deferred.resolve();
});
return deferred.promise;
}
function stopAgenda() {
const deferred = defer();
agenda.stop(() => {
deferred.resolve();
return _winston2.default.info('Stopped agenda job scheduler');
});
return deferred.promise;
}
function startHttpServer(httpPort, bindAddress, app) {
const deferred = defer();
httpServer = _http2.default.createServer(app.callback());
// set the socket timeout
httpServer.setTimeout(+_config.config.router.timeout, () => _winston2.default.info('HTTP socket timeout reached'));
httpServer.listen(httpPort, bindAddress, () => {
_winston2.default.info(`HTTP listening on port ${httpPort}`);
return deferred.resolve();
});
// listen for server error
httpServer.on('error', err => _winston2.default.error(`An httpServer error occured: ${err}`));
// listen for client error
httpServer.on('clientError', err => _winston2.default.error(`An httpServer clientError occured: ${err}`));
httpServer.on('connection', socket => trackConnection(activeHttpConnections, socket));
return deferred.promise;
}
function startHttpsServer(httpsPort, bindAddress, app) {
const deferred = defer();
const mutualTLS = _config.config.authentication.enableMutualTLSAuthentication;
tlsAuthentication.getServerOptions(mutualTLS, (err, options) => {
if (err) {
return deferred.reject(err);
}
httpsServer = _https2.default.createServer(options, app.callback());
// set the socket timeout
httpsServer.setTimeout(+_config.config.router.timeout, () => _winston2.default.info('HTTPS socket timeout reached'));
httpsServer.listen(httpsPort, bindAddress, () => {
_winston2.default.info(`HTTPS listening on port ${httpsPort}`);
return deferred.resolve();
});
// listen for server error
httpsServer.on('error', err => _winston2.default.error(`An httpsServer error occured: ${err}`));
// listen for client error
httpsServer.on('clientError', err => _winston2.default.error(`An httpsServer clientError occured: ${err}`));
return httpsServer.on('secureConnection', socket => trackConnection(activeHttpsConnections, socket));
});
return deferred.promise;
}
// Ensure that a root user always exists
const ensureRootUser = callback => _users.UserModel.findOne({ email: 'root@openhim.org' }, (err, user) => {
if (err) {
return callback(err);
}
if (!user) {
user = new _users.UserModel(rootUser);
return user.save(err => {
if (err) {
_winston2.default.error(`Could not save root user: ${err}`);
return callback(err);
}
_winston2.default.info('Root user created.');
return callback();
});
}
return callback();
});
// Ensure that a default keystore always exists and is up to date
ensureKeystore = function (callback) {
const getServerCertDetails = (cert, callback) => _pem2.default.readCertificateInfo(cert, (err, certInfo) => {
if (err) {
_winston2.default.error(err.stack);
return callback(err);
}
return _pem2.default.getFingerprint(cert, (err, fingerprint) => {
if (err) {
_winston2.default.error(err.stack);
return callback(err);
}
certInfo.data = cert;
certInfo.fingerprint = fingerprint.fingerprint;
return callback(certInfo);
});
});
return _keystore.KeystoreModel.findOne({}, (err, keystore) => {
let cert;
let certPath;
let keyPath;
if (err) {
_winston2.default.error(err.stack);
return callback(err);
}
if (keystore == null) {
// set default keystore
if (_config.config.certificateManagement.watchFSForCert) {
// use cert from filesystem
({ certPath } = _config.config.certificateManagement);
({ keyPath } = _config.config.certificateManagement);
} else {
// use default self-signed certs
certPath = `${_config.appRoot}/resources/certs/default/cert.pem`;
keyPath = `${_config.appRoot}/resources/certs/default/key.pem`;
}
cert = _fs2.default.readFileSync(certPath);
return getServerCertDetails(cert, certInfo => {
keystore = new _keystore.KeystoreModel({
cert: certInfo,
key: _fs2.default.readFileSync(keyPath),
ca: []
});
return keystore.save((err, keystore) => {
if (err) {
_winston2.default.error(`Could not save keystore: ${err.stack}`);
return callback(err);
}
_winston2.default.info('Default keystore created.');
return callback();
});
});
} else if (_config.config.certificateManagement.watchFSForCert) {
// update cert to latest
cert = _fs2.default.readFileSync(_config.config.certificateManagement.certPath);
return getServerCertDetails(cert, certInfo => {
keystore.cert = certInfo;
keystore.key = _fs2.default.readFileSync(_config.config.certificateManagement.keyPath);
return keystore.save((err, keystore) => {
if (err) {
_winston2.default.error(`Could not save keystore: ${err.stack}`);
return callback(err);
}
_winston2.default.info('Updated keystore with cert and key from filesystem.');
return callback();
});
});
}
return callback();
});
};
function startApiServer(apiPort, bindAddress, app) {
const deferred = defer();
// mutualTLS not applicable for the API - set false
const mutualTLS = false;
tlsAuthentication.getServerOptions(mutualTLS, (err, options) => {
if (err) {
_winston2.default.error(`Could not fetch https server options: ${err}`);
}
apiHttpsServer = _https2.default.createServer(options, app.callback());
apiHttpsServer.listen(apiPort, bindAddress, () => {
_winston2.default.info(`API HTTPS listening on port ${apiPort}`);
return ensureRootUser(() => deferred.resolve());
});
return apiHttpsServer.on('secureConnection', socket => trackConnection(activeApiConnections, socket));
});
return deferred.promise;
}
function startTCPServersAndHttpReceiver(tcpHttpReceiverPort, app) {
const deferred = defer();
tcpHttpReceiver = _http2.default.createServer(app.callback());
tcpHttpReceiver.listen(tcpHttpReceiverPort, _config.config.tcpAdapter.httpReceiver.host, () => {
_winston2.default.info(`HTTP receiver for Socket adapter listening on port ${tcpHttpReceiverPort}`);
return tcpAdapter.startupServers(err => {
if (err) {
_winston2.default.error(err);
}
return deferred.resolve();
});
});
tcpHttpReceiver.on('connection', socket => trackConnection(activeTcpConnections, socket));
return deferred.promise;
}
function startRerunServer(httpPort, app) {
const deferredHttp = defer();
rerunServer = _http2.default.createServer(app.callback());
rerunServer.listen(httpPort, _config.config.rerun.host, () => {
_winston2.default.info(`Transaction Rerun HTTP listening on port ${httpPort}`);
return deferredHttp.resolve();
});
rerunServer.on('connection', socket => trackConnection(activeRerunConnections, socket));
return deferredHttp.promise;
}
function startPollingServer(pollingPort, app) {
const deferred = defer();
pollingServer = _http2.default.createServer(app.callback());
pollingServer.listen(pollingPort, _config.config.polling.host, err => {
if (err) {
_winston2.default.error(err);
}
_winston2.default.info(`Polling port listening on port ${pollingPort}`);
return deferred.resolve();
});
pollingServer.on('connection', socket => trackConnection(activePollingConnections, socket));
return deferred.promise;
}
function startAuditUDPServer(auditUDPPort, bindAddress) {
const deferred = defer();
auditUDPServer = _dgram2.default.createSocket('udp4');
auditUDPServer.on('listening', () => {
_winston2.default.info(`Auditing UDP server listening on port ${auditUDPPort}`);
return deferred.resolve();
});
auditUDPServer.on('message', (msg, rinfo) => {
_winston2.default.info(`[Auditing UDP] Received message from ${rinfo.address}:${rinfo.port}`);
return auditing.processAudit(msg, () => _winston2.default.info('[Auditing UDP] Processed audit'));
});
auditUDPServer.on('error', err => {
if (err.code === 'EADDRINUSE') {
// ignore to allow only 1 worker to bind (workaround for: https://github.com/joyent/node/issues/9261)
return deferred.resolve();
}
_winston2.default.error(`UDP Audit server error: ${err}`, err);
return deferred.reject(err);
});
auditUDPServer.bind({
port: auditUDPPort,
address: bindAddress,
exclusive: true
}); // workaround for: https://github.com/joyent/node/issues/9261
return deferred.promise;
}
// function to start the TCP/TLS Audit server
function startAuditTcpTlsServer(type, auditPort, bindAddress) {
const deferred = defer();
// data handler
function handler(sock) {
let message = '';
let length = 0;
sock.on('data', data => {
// convert to string and concatenate
message += data.toString();
// check if length is is still zero and first occurannce of space
if (length === 0 && message.indexOf(' ') !== -1) {
// get index of end of message length
const lengthIndex = message.indexOf(' ');
// source message length
const lengthValue = message.substr(0, lengthIndex);
// remove white spaces
length = parseInt(lengthValue.trim(), 10);
// update message to remove length - add one extra character to remove the space
message = message.substr(lengthIndex + 1);
}
if (isNaN(length)) {
_winston2.default.info(`[Auditing ${type}] No length supplied`);
sock.destroy();
}
_winston2.default.debug(`Length prefix is: ${length} and message length so far is ${Buffer.byteLength(message)}`);
// if sourced length equals message length then full message received
if (length === Buffer.byteLength(message)) {
_winston2.default.info(`[Auditing ${type}] Received message from ${sock.remoteAddress}`);
auditing.processAudit(message, () => _winston2.default.info(`[Auditing ${type}] Processed audit`));
// reset message and length variables
message = '';
length = 0;
}
});
return sock.on('error', err => _winston2.default.error(err));
}
if (type === 'TLS') {
tlsAuthentication.getServerOptions(true, (err, options) => {
if (err) {
return deferred.reject(err);
}
auditTlsServer = _tls2.default.createServer(options, handler);
return auditTlsServer.listen(auditPort, bindAddress, () => {
_winston2.default.info(`Auditing TLS server listening on port ${auditPort}`);
return deferred.resolve();
});
});
} else if (type === 'TCP') {
auditTcpServer = _net2.default.createServer(handler);
auditTcpServer.listen(auditPort, bindAddress, () => {
_winston2.default.info(`Auditing TCP server listening on port ${auditPort}`);
return deferred.resolve();
});
}
return deferred.promise;
}
exports.start = function (ports, done) {
const bindAddress = _config.config.get('bindAddress');
_winston2.default.info(`Starting OpenHIM server on ${bindAddress}...`);
const promises = [];
return ensureKeystore(() => {
if (ports.httpPort || ports.httpsPort) {
koaMiddleware.setupApp(app => {
if (ports.httpPort) {
promises.push(startHttpServer(ports.httpPort, bindAddress, app));
}
if (ports.httpsPort) {
promises.push(startHttpsServer(ports.httpsPort, bindAddress, app));
}
return promises;
});
}
if (ports.apiPort && _config.config.api.enabled) {
koaApi.setupApp(app => promises.push(startApiServer(ports.apiPort, bindAddress, app)));
}
if (ports.rerunHttpPort) {
koaMiddleware.rerunApp(app => promises.push(startRerunServer(ports.rerunHttpPort, app)));
if (_config.config.rerun.processor.enabled) {
const deferred = defer();
tasks.start(() => deferred.resolve());
promises.push(deferred.promise);
}
}
if (ports.tcpHttpReceiverPort) {
koaMiddleware.tcpApp(app => promises.push(startTCPServersAndHttpReceiver(ports.tcpHttpReceiverPort, app)));
}
if (ports.pollingPort) {
koaMiddleware.pollingApp(app => promises.push(startPollingServer(ports.pollingPort, app)));
}
if (ports.auditUDPPort) {
promises.push(startAuditUDPServer(ports.auditUDPPort, bindAddress));
}
if (ports.auditTlsPort) {
promises.push(startAuditTcpTlsServer('TLS', ports.auditTlsPort, bindAddress));
}
if (ports.auditTcpPort) {
promises.push(startAuditTcpTlsServer('TCP', ports.auditTcpPort, bindAddress));
}
promises.push(startAgenda());
return Promise.all(promises).then(() => {
let audit = _atnaAudit2.default.construct.appActivityAudit(true, himSourceID, _os2.default.hostname(), 'system');
audit = _atnaAudit2.default.construct.wrapInSyslog(audit);
return auditing.sendAuditEvent(audit, err => {
if (err) return done(err);
_winston2.default.info('Processed start audit event');
_winston2.default.info(`OpenHIM server started: ${new Date()}`);
return done();
});
}).catch(done);
});
};
// wait for any running tasks before trying to stop anything
function stopTasksProcessor(callback) {
if (tasks.isRunning()) {
return tasks.stop(callback);
}
return callback();
}
exports.stop = stop = done => stopTasksProcessor(() => {
if (typeof done !== 'function') {
done = () => {};
}
let socket;
const promises = [];
function stopServer(server, serverType) {
const deferred = defer();
server.close(() => {
_winston2.default.info(`Stopped ${serverType} server`);
return deferred.resolve();
});
return deferred.promise;
}
if (httpServer) {
promises.push(stopServer(httpServer, 'HTTP'));
}
if (httpsServer) {
promises.push(stopServer(httpsServer, 'HTTPS'));
}
if (apiHttpsServer) {
promises.push(stopServer(apiHttpsServer, 'API HTTP'));
}
if (rerunServer) {
promises.push(stopServer(rerunServer, 'Rerun HTTP'));
}
if (pollingServer) {
promises.push(stopServer(pollingServer, 'Polling HTTP'));
}
if (agenda) {
promises.push(stopAgenda());
}
if (auditTlsServer) {
promises.push(stopServer(auditTlsServer, 'Audit TLS').promise);
}
if (auditTcpServer) {
promises.push(stopServer(auditTcpServer, 'Audit TCP').promise);
}
if (auditUDPServer) {
try {
auditUDPServer.close();
_winston2.default.info('Stopped Audit UDP server');
} catch (err) {
_winston2.default.error('Failed to stop auditUDServer with err:', err);
}
}
// ignore errors when shutting down the server, sometimes its already stopped
if (tcpHttpReceiver) {
promises.push(stopServer(tcpHttpReceiver, 'TCP HTTP Receiver'));
const deferred = defer();
tcpAdapter.stopServers(err => {
if (err) {
return deferred.reject(err);
}
deferred.resolve();
});
promises.push(deferred.promise);
}
// close active connection so that servers can stop
for (const key in activeHttpConnections) {
socket = activeHttpConnections[key];
socket.destroy();
}
for (const key in activeHttpsConnections) {
socket = activeHttpsConnections[key];
socket.destroy();
}
for (const key in activeApiConnections) {
socket = activeApiConnections[key];
socket.destroy();
}
for (const key in activeRerunConnections) {
socket = activeRerunConnections[key];
socket.destroy();
}
for (const key in activeTcpConnections) {
socket = activeTcpConnections[key];
socket.destroy();
}
for (const key in activePollingConnections) {
socket = activePollingConnections[key];
socket.destroy();
}
return Promise.all(promises).then(() => {
httpServer = null;
httpsServer = null;
apiHttpsServer = null;
rerunServer = null;
tcpHttpReceiver = null;
pollingServer = null;
auditUDPServer = null;
auditTlsServer = null;
auditTcpServer = null;
agenda = null;
let audit = _atnaAudit2.default.construct.appActivityAudit(false, himSourceID, _os2.default.hostname(), 'system');
audit = _atnaAudit2.default.construct.wrapInSyslog(audit);
return auditing.sendAuditEvent(audit, () => {
_winston2.default.info('Processed stop audit event');
_winston2.default.info('Server shutdown complete.');
return done();
});
});
});
const lookupServerPorts = () => ({
httpPort: _config.config.router.httpPort,
httpsPort: _config.config.router.httpsPort,
apiPort: _config.config.api.httpsPort,
rerunHttpPort: _config.config.rerun.httpPort,
tcpHttpReceiverPort: _config.config.tcpAdapter.httpReceiver.httpPort,
pollingPort: _config.config.polling.pollingPort,
auditUDPPort: _config.config.auditing.servers.udp.enabled ? _config.config.auditing.servers.udp.port : undefined,
auditTlsPort: _config.config.auditing.servers.tls.enabled ? _config.config.auditing.servers.tls.port : undefined,
auditTcpPort: _config.config.auditing.servers.tcp.enabled ? _config.config.auditing.servers.tcp.port : undefined
});
if (!module.parent) {
// start the server
const ports = lookupServerPorts();
exports.start(ports, () => {
// setup shutdown listeners
process.on('exit', stop);
// interrupt signal, e.g. ctrl-c
process.on('SIGINT', () => stop(process.exit));
// terminate signal
process.on('SIGTERM', () => stop(process.exit));
// restart on message
return process.on('message', msg => {
if (msg.type === 'restart') {
exports.restartServer();
}
});
});
}
exports.restartServer = function (ports, done) {
if (typeof ports === 'function') {
done = ports;
ports = null;
}
if (typeof port === 'undefined' || ports === null) {
ports = lookupServerPorts();
}
return exports.stop(() => exports.start(ports, () => {
if (done) {
done();
}
}));
};
exports.startRestartServerTimeout = function (done) {
if (_cluster2.default.isMaster) {
// restart myself in 2s
setTimeout(() => {
_winston2.default.debug('Master restarting itself...');
return exports.restartServer();
}, 2000);
} else {
// notify master to restart all workers in 2s
setTimeout(() => {
_winston2.default.debug('Sending restart cluster message...');
return process.send({
type: 'restart-all'
});
}, 2000);
}
return done();
};
// function to return process uptimes
exports.getUptime = function (callback) {
if (_cluster2.default.isMaster) {
// send reponse back to API request
const uptime = { master: process.uptime() };
return callback(null, uptime);
}
// send request to master
process.send({
type: 'get-uptime'
});
const processEvent = function (uptime) {
if (uptime.type === 'get-uptime') {
uptime = { master: uptime.masterUptime
// remove eventListner
};process.removeListener('message', processEvent);
// send reponse back to API request
callback(null, uptime);
}
};
// listen for response from master
return process.on('message', processEvent);
};
}
if (process.env.NODE_ENV === 'test') {
exports.ensureKeystore = ensureKeystore;
}
//# sourceMappingURL=server.js.map