openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
275 lines (223 loc) • 8.39 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.popTransaction = popTransaction;
exports.notifyMasterToStartTCPServer = notifyMasterToStartTCPServer;
exports.startupTCPServer = startupTCPServer;
exports.startupServers = startupServers;
exports.stopServers = stopServers;
exports.notifyMasterToStopTCPServer = notifyMasterToStopTCPServer;
exports.stopServerForChannel = stopServerForChannel;
var _http = _interopRequireDefault(require("http"));
var _net = _interopRequireDefault(require("net"));
var _tls = _interopRequireDefault(require("tls"));
var _winston = _interopRequireDefault(require("winston"));
var Channels = _interopRequireWildcard(require("./model/channels"));
var tlsAuthentication = _interopRequireWildcard(require("./middleware/tlsAuthentication"));
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 }; }
_config.config.tcpAdapter = _config.config.get('tcpAdapter');
const {
ChannelModel
} = Channels;
let tcpServers = [];
let newKey = 0;
const datastore = {};
process.on('message', msg => {
if (msg.type === 'start-tcp-channel') {
_winston.default.debug(`Recieved message to start tcp channel: ${msg.channelID}`);
return exports.startupTCPServer(msg.channelID, () => {});
} else if (msg.type === 'stop-tcp-channel') {
_winston.default.debug(`Recieved message to stop tcp channel: ${msg.channelID}`);
return exports.stopServerForChannel(msg.channelID, () => {});
}
});
function popTransaction(key) {
const res = datastore[`${key}`];
delete datastore[`${key}`];
return res;
}
function startListening(channel, tcpServer, host, port, callback) {
tcpServer.listen(port, host, () => {
tcpServers.push({
channelID: channel._id,
server: tcpServer
});
return callback(null);
});
return tcpServer.on('error', err => _winston.default.error(`${err} Host: ${host} Port: ${port}`));
}
function notifyMasterToStartTCPServer(channelID, callback) {
_winston.default.debug(`Sending message to master to start tcp channel: ${channelID}`);
return process.send({
type: 'start-tcp-channel',
channelID
});
}
function startupTCPServer(channelID, callback) {
for (const existingServer of Array.from(tcpServers)) {
// server already running for channel
if (existingServer.channelID.equals(channelID)) {
return callback(null);
}
}
const handler = sock => ChannelModel.findById(channelID, (err, channel) => {
if (err) {
return _winston.default.error(err);
}
sock.on('data', data => adaptSocketRequest(channel, sock, `${data}`));
return sock.on('error', err => _winston.default.error(err));
});
return ChannelModel.findById(channelID, (err, channel) => {
if (err) {
return callback(err);
}
const host = channel.tcpHost || '0.0.0.0';
const port = channel.tcpPort;
if (!port) {
return callback(new Error(`Channel ${channel.name} (${channel._id}): TCP port not defined`));
}
if (channel.type === 'tls') {
return tlsAuthentication.getServerOptions(true, (err, options) => {
if (err) {
return callback(err);
}
const tcpServer = _tls.default.createServer(options, handler);
return startListening(channel, tcpServer, host, port, err => {
if (err) {
return callback(err);
} else {
_winston.default.info(`Channel ${channel.name} (${channel._id}): TLS server listening on port ${port}`);
return callback(null);
}
});
});
} else if (channel.type === 'tcp') {
const tcpServer = _net.default.createServer(handler);
return startListening(channel, tcpServer, host, port, err => {
if (err) {
return callback(err);
} else {
_winston.default.info(`Channel ${channel.name} (${channel._id}): TCP server listening on port ${port}`);
return callback(null);
}
});
} else {
return callback(new Error(`Cannot handle ${channel.type} channels`));
}
});
} // Startup a TCP server for each TCP channel
function startupServers(callback) {
return ChannelModel.find({
$or: [{
type: 'tcp'
}, {
type: 'tls'
}]
}, (err, channels) => {
if (err) {
return callback(err);
}
const promises = [];
Array.from(channels).forEach(channel => {
if (Channels.isChannelEnabled(channel)) {
const promise = new Promise((resolve, reject) => {
exports.startupTCPServer(channel._id, err => {
if (err) {
return reject(err);
}
return resolve();
});
});
return promises.push(promise);
}
});
return Promise.all(promises).then(() => callback(null)).catch(callback);
});
}
function adaptSocketRequest(channel, sock, socketData) {
const options = {
hostname: _config.config.tcpAdapter.httpReceiver.host,
port: _config.config.tcpAdapter.httpReceiver.httpPort,
path: '/',
method: 'POST'
};
const req = _http.default.request(options, res => {
let response = '';
res.on('data', data => {
response += data;
});
return res.on('end', () => {
if (sock.writable) {
return sock.write(response);
}
});
});
req.on('error', err => _winston.default.error(err)); // don't write the actual data to the http receiver
// instead send a reference through (see popTransaction)
datastore[`${newKey}`] = {};
datastore[`${newKey}`].data = socketData;
datastore[`${newKey}`].channel = channel;
req.write(`${newKey}`);
newKey++; // in case we've been running for a couple thousand years
if (newKey === Number.MAX_VALUE) {
newKey = 0;
}
return req.end();
}
function stopTCPServers(servers, callback) {
const promises = Array.from(servers).map(server => {
return new Promise((resolve, reject) => {
server.server.close(err => {
if (err) {
return reject(err);
} else {
_winston.default.info(`Channel ${server.channelID}: Stopped TCP/TLS server`);
return resolve();
}
});
});
});
Promise.all(promises).then(() => callback()).catch(callback);
}
function stopServers(callback) {
stopTCPServers(tcpServers, err => {
if (err) {
_winston.default.error('Could not close tcp server', err);
return callback(err);
}
tcpServers = [];
return callback();
});
}
function notifyMasterToStopTCPServer(channelID, callback) {
_winston.default.debug(`Sending message to master to stop tcp channel: ${channelID}`);
return process.send({
type: 'stop-tcp-channel',
channelID
});
}
function stopServerForChannel(channelID, callback) {
let server = null;
const notStoppedTcpServers = [];
for (const serverDetails of Array.from(tcpServers)) {
if (serverDetails.channelID.equals(channelID)) {
server = serverDetails;
} else {
// push all except the server we're stopping
notStoppedTcpServers.push(serverDetails);
}
}
if (!server) {
return callback(new Error(`Server for channel ${channelID} not running`));
}
tcpServers = notStoppedTcpServers;
return stopTCPServers([server], callback);
}
if (process.env.NODE_ENV === 'test') {
exports.tcpServers = tcpServers;
}
//# sourceMappingURL=tcpAdapter.js.map