openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
1,050 lines (798 loc) • 33.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.setupCertificateWatcher = setupCertificateWatcher;
require("./winston-transport-workaround");
require("babel-polyfill");
var _cluster = _interopRequireDefault(require("cluster"));
var _nconf = _interopRequireDefault(require("nconf"));
var _atnaAudit = _interopRequireDefault(require("atna-audit"));
var _os = _interopRequireDefault(require("os"));
var _chokidar = _interopRequireDefault(require("chokidar"));
var _fs = _interopRequireDefault(require("fs"));
var _http = _interopRequireDefault(require("http"));
var _https = _interopRequireDefault(require("https"));
var _tls = _interopRequireDefault(require("tls"));
var _net = _interopRequireDefault(require("net"));
var _dgram = _interopRequireDefault(require("dgram"));
var _v = _interopRequireDefault(require("uuid/v4"));
var _pem = _interopRequireDefault(require("pem"));
var _winston = _interopRequireDefault(require("winston"));
require("winston-mongodb");
var _agenda = _interopRequireDefault(require("agenda"));
var _mongoose = _interopRequireDefault(require("mongoose"));
var koaMiddleware = _interopRequireWildcard(require("./koaMiddleware"));
var koaApi = _interopRequireWildcard(require("./koaApi"));
var tlsAuthentication = _interopRequireWildcard(require("./middleware/tlsAuthentication"));
var _users = require("./model/users");
var _keystore = require("./model/keystore");
var alerts = _interopRequireWildcard(require("./alerts"));
var reports = _interopRequireWildcard(require("./reports"));
var polling = _interopRequireWildcard(require("./polling"));
var tcpAdapter = _interopRequireWildcard(require("./tcpAdapter"));
var auditing = _interopRequireWildcard(require("./auditing"));
var tasks = _interopRequireWildcard(require("./tasks"));
var upgradeDB = _interopRequireWildcard(require("./upgradeDB"));
var autoRetry = _interopRequireWildcard(require("./autoRetry"));
var bodyCull = _interopRequireWildcard(require("./bodyCull"));
var _config = require("./config");
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
_mongoose.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;
_winston.default.remove(_winston.default.transports.Console);
const winstonLogFormat = _winston.default.format.printf(info => {
return `${info.timestamp} [${info.label}] ${info.level}: ${info.message}`;
});
let clusterArg = _nconf.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 = _chokidar.default.watch([certFile, keyFile], {
usePolling: true
}).on('ready', () => {
_winston.default.info('Certificate/Key watch paths:', watcher.getWatched());
return watcher.on('change', (path, stats) => {
for (const id in _cluster.default.workers) {
const worker = _cluster.default.workers[id];
_winston.default.debug(`Restarting worker ${worker.id}...`);
worker.send({
type: 'restart'
});
}
});
});
return watcher;
}
/* eslint no-inner-declarations: 0 */
// Configure clustering if relevent
if (_cluster.default.isMaster && !module.parent) {
process.title = 'Core'; // configure master logger
let clusterSize;
_winston.default.add(new _winston.default.transports.Console({
format: _winston.default.format.combine(_winston.default.format.label({
label: 'master'
}), _winston.default.format.timestamp(), _winston.default.format.colorize(), winstonLogFormat),
level: _config.config.logger.level
}));
if (_config.config.logger.logToDB === true) {
_winston.default.add(new _winston.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'.`);
}
_winston.default.info(`Running OpenHIM Core JS version ${currentVersion}`);
_winston.default.info(`Clustering the OpenHIM with ${clusterSize} workers...`);
function addWorker() {
let worker = _cluster.default.fork();
return worker.on('message', msg => {
let id;
_winston.default.debug(`Message received from worker ${worker.id}`, msg);
if (msg.type === 'restart-all') {
// restart all workers
_winston.default.debug('Restarting all workers...');
return (() => {
const result = [];
for (id in _cluster.default.workers) {
worker = _cluster.default.workers[id];
_winston.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
_winston.default.info(`Starting TCP channel for channel: ${msg.channelID}`);
return (() => {
const result1 = [];
for (id in _cluster.default.workers) {
worker = _cluster.default.workers[id];
_winston.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
_winston.default.info(`Stopping TCP channel for channel: ${msg.channelID}`);
return (() => {
const result2 = [];
for (id in _cluster.default.workers) {
worker = _cluster.default.workers[id];
_winston.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();
}
_cluster.default.on('exit', (worker, code, signal) => {
_winston.default.warn(`worker ${worker.process.pid} died`);
if (!worker.suicide) {
// respawn
addWorker();
}
});
_cluster.default.on('online', worker => _winston.default.info(`worker with pid ${worker.process.pid} is online`));
return _cluster.default.on('listening', (worker, address) => _winston.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;
_winston.default.add(new _winston.default.transports.Console({
format: _winston.default.format.combine(_winston.default.format.label({
label: (_cluster.default.worker != null ? _cluster.default.worker.id : undefined) != null ? `worker${_cluster.default.worker.id}` : undefined
}), _winston.default.format.timestamp(), _winston.default.format.colorize(), winstonLogFormat),
level: _config.config.logger.level
}));
if (_config.config.logger.logToDB === true && _winston.default.default.transports.mongodb == null) {
_winston.default.add(new _winston.default.transports.MongoDB({
db: _config.config.mongo.url,
options: _config.config.mongoLogger.options,
label: (_cluster.default.worker != null ? _cluster.default.worker.id : undefined) != null ? `worker${_cluster.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 = (0, _v.default)();
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 => _winston.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 _agenda.default({
mongo: _config.connectionAgenda
});
agenda.on('start', job => _winston.default.info(`starting job: ${job.attrs.name}, Last Ran at: ${job.attrs.lastRunAt}`));
agenda.on('fail', (err, job) => _winston.default.error(`Job ${job.attrs.name} failed with ${err.message}`));
agenda.on('complete', job => _winston.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 _winston.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() {
agenda.stop().then(() => {
_winston.default.info('Stopped agenda job scheduler');
});
}
function startHttpServer(httpPort, bindAddress, app) {
const deferred = defer();
httpServer = _http.default.createServer(app.callback()); // set the socket timeout
httpServer.setTimeout(+_config.config.router.timeout, () => _winston.default.info('HTTP socket timeout reached'));
httpServer.listen(httpPort, bindAddress, () => {
_winston.default.info(`HTTP listening on port ${httpPort}`);
return deferred.resolve();
}); // listen for server error
httpServer.on('error', err => _winston.default.error(`An httpServer error occured: ${err}`)); // listen for client error
httpServer.on('clientError', err => _winston.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 = _https.default.createServer(options, app.callback()); // set the socket timeout
httpsServer.setTimeout(+_config.config.router.timeout, () => _winston.default.info('HTTPS socket timeout reached'));
httpsServer.listen(httpsPort, bindAddress, () => {
_winston.default.info(`HTTPS listening on port ${httpsPort}`);
return deferred.resolve();
}); // listen for server error
httpsServer.on('error', err => _winston.default.error(`An httpsServer error occured: ${err}`)); // listen for client error
httpsServer.on('clientError', err => _winston.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) {
_winston.default.error(`Could not save root user: ${err}`);
return callback(err);
}
_winston.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) => _pem.default.readCertificateInfo(cert, (err, certInfo) => {
if (err) {
_winston.default.error(err.stack);
return callback(err);
}
return _pem.default.getFingerprint(cert, (err, fingerprint) => {
if (err) {
_winston.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) {
_winston.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 = _fs.default.readFileSync(certPath);
return getServerCertDetails(cert, certInfo => {
keystore = new _keystore.KeystoreModel({
cert: certInfo,
key: _fs.default.readFileSync(keyPath),
ca: []
});
return keystore.save((err, keystore) => {
if (err) {
_winston.default.error(`Could not save keystore: ${err.stack}`);
return callback(err);
}
_winston.default.info('Default keystore created.');
return callback();
});
});
} else if (_config.config.certificateManagement.watchFSForCert) {
// update cert to latest
cert = _fs.default.readFileSync(_config.config.certificateManagement.certPath);
return getServerCertDetails(cert, certInfo => {
keystore.cert = certInfo;
keystore.key = _fs.default.readFileSync(_config.config.certificateManagement.keyPath);
return keystore.save((err, keystore) => {
if (err) {
_winston.default.error(`Could not save keystore: ${err.stack}`);
return callback(err);
}
_winston.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) {
_winston.default.error(`Could not fetch https server options: ${err}`);
}
apiHttpsServer = _https.default.createServer(options, app.callback());
apiHttpsServer.listen(apiPort, bindAddress, () => {
_winston.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 = _http.default.createServer(app.callback());
tcpHttpReceiver.listen(tcpHttpReceiverPort, _config.config.tcpAdapter.httpReceiver.host, () => {
_winston.default.info(`HTTP receiver for Socket adapter listening on port ${tcpHttpReceiverPort}`);
return tcpAdapter.startupServers(err => {
if (err) {
_winston.default.error(err);
}
return deferred.resolve();
});
});
tcpHttpReceiver.on('connection', socket => trackConnection(activeTcpConnections, socket));
return deferred.promise;
}
function startRerunServer(httpPort, app) {
const deferredHttp = defer();
rerunServer = _http.default.createServer(app.callback());
rerunServer.listen(httpPort, _config.config.rerun.host, () => {
_winston.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 = _http.default.createServer(app.callback());
pollingServer.listen(pollingPort, _config.config.polling.host, err => {
if (err) {
_winston.default.error(err);
}
_winston.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 = _dgram.default.createSocket('udp4');
auditUDPServer.on('listening', () => {
_winston.default.info(`Auditing UDP server listening on port ${auditUDPPort}`);
return deferred.resolve();
});
auditUDPServer.on('message', (msg, rinfo) => {
_winston.default.info(`[Auditing UDP] Received message from ${rinfo.address}:${rinfo.port}`);
return auditing.processAudit(msg, () => _winston.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();
}
_winston.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)) {
_winston.default.info(`[Auditing ${type}] No length supplied`);
sock.destroy();
}
_winston.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)) {
_winston.default.info(`[Auditing ${type}] Received message from ${sock.remoteAddress}`);
auditing.processAudit(message, () => _winston.default.info(`[Auditing ${type}] Processed audit`)); // reset message and length variables
message = '';
length = 0;
}
});
return sock.on('error', err => _winston.default.error(err));
}
if (type === 'TLS') {
tlsAuthentication.getServerOptions(true, (err, options) => {
if (err) {
return deferred.reject(err);
}
auditTlsServer = _tls.default.createServer(options, handler);
return auditTlsServer.listen(auditPort, bindAddress, () => {
_winston.default.info(`Auditing TLS server listening on port ${auditPort}`);
return deferred.resolve();
});
});
} else if (type === 'TCP') {
auditTcpServer = _net.default.createServer(handler);
auditTcpServer.listen(auditPort, bindAddress, () => {
_winston.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');
_winston.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 = _atnaAudit.default.construct.appActivityAudit(true, himSourceID, _os.default.hostname(), 'system');
audit = _atnaAudit.default.construct.wrapInSyslog(audit);
return auditing.sendAuditEvent(audit, err => {
if (err) return done(err);
_winston.default.info('Processed start audit event');
_winston.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(() => {
_winston.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) {
stopAgenda();
}
if (auditTlsServer) {
promises.push(stopServer(auditTlsServer, 'Audit TLS').promise);
}
if (auditTcpServer) {
promises.push(stopServer(auditTcpServer, 'Audit TCP').promise);
}
if (auditUDPServer) {
try {
auditUDPServer.close();
_winston.default.info('Stopped Audit UDP server');
} catch (err) {
_winston.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 = _atnaAudit.default.construct.appActivityAudit(false, himSourceID, _os.default.hostname(), 'system');
audit = _atnaAudit.default.construct.wrapInSyslog(audit);
return auditing.sendAuditEvent(audit, () => {
_winston.default.info('Processed stop audit event');
_winston.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 (_cluster.default.isMaster) {
// restart myself in 2s
setTimeout(() => {
_winston.default.debug('Master restarting itself...');
return exports.restartServer();
}, 2000);
} else {
// notify master to restart all workers in 2s
setTimeout(() => {
_winston.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 (_cluster.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