UNPKG

openhim-core

Version:

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

1,061 lines (851 loc) 33.6 kB
'use strict'; 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