UNPKG

openhim-core

Version:

The OpenHIM core application that provides logging and routing of http requests

1,050 lines (798 loc) 33.7 kB
"use strict"; 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