UNPKG

openhim-core

Version:

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

182 lines (143 loc) 5.48 kB
http = require 'http' net = require 'net' tls = require 'tls' config = require "./config/config" config.tcpAdapter = config.get('tcpAdapter') logger = require "winston" Channels = require('./model/channels') Channel = Channels.Channel Q = require "q" tlsAuthentication = require "./middleware/tlsAuthentication" authorisation = require "./middleware/authorisation" tcpServers = [] newKey = 0 datastore = {} process.on 'message', (msg) -> if msg.type is 'start-tcp-channel' logger.debug "Recieved message to start tcp channel: #{msg.channelID}" exports.startupTCPServer msg.channelID, -> else if msg.type is 'stop-tcp-channel' logger.debug "Recieved message to stop tcp channel: #{msg.channelID}" exports.stopServerForChannel msg.channelID, -> exports.popTransaction = (key) -> res = datastore["#{key}"] delete datastore["#{key}"] return res startListening = (channel, tcpServer, host, port, callback) -> tcpServer.listen port, host, -> tcpServers.push { channelID: channel._id, server: tcpServer } callback null tcpServer.on 'error', (err) -> logger.error err + ' Host: ' + host + ' Port: ' + port exports.notifyMasterToStartTCPServer = (channelID, callback) -> logger.debug "Sending message to master to start tcp channel: #{channelID}" process.send type: 'start-tcp-channel' channelID: channelID exports.startupTCPServer = (channelID, callback) -> for existingServer in tcpServers # server already running for channel return callback null if existingServer.channelID.equals channelID handler = (sock) -> Channel.findById channelID, (err, channel) -> return logger.error err if err sock.on 'data', (data) -> adaptSocketRequest channel, sock, "#{data}" sock.on 'error', (err) -> logger.error err Channel.findById channelID, (err, channel) -> host = channel.tcpHost or '0.0.0.0' port = channel.tcpPort return callback "Channel #{channel.name} (#{channel._id}): TCP port not defined" if not port if channel.type is 'tls' tlsAuthentication.getServerOptions true, (err, options) -> return callback err if err tcpServer = tls.createServer options, handler startListening channel, tcpServer, host, port, (err) -> if err callback err else logger.info "Channel #{channel.name} (#{channel._id}): TLS server listening on port #{port}" callback null else if channel.type is 'tcp' tcpServer = net.createServer handler startListening channel, tcpServer, host, port, (err) -> if err callback err else logger.info "Channel #{channel.name} (#{channel._id}): TCP server listening on port #{port}" callback null else return callback "Cannot handle #{channel.type} channels" # Startup a TCP server for each TCP channel exports.startupServers = (callback) -> Channel.find { $or: [ {type: 'tcp'}, {type: 'tls'} ] }, (err, channels) -> return callback err if err promises = [] for channel in channels do (channel) -> if Channels.isChannelEnabled channel defer = Q.defer() exports.startupTCPServer channel._id, (err) -> return callback err if err defer.resolve() promises.push defer.promise (Q.all promises).then -> callback null adaptSocketRequest = (channel, sock, socketData) -> options = hostname: config.tcpAdapter.httpReceiver.host port: config.tcpAdapter.httpReceiver.httpPort path: '/' method: 'POST' req = http.request options, (res) -> response = '' res.on 'data', (data) -> response += data res.on 'end', -> if sock.writable sock.write response req.on "error", (err) -> logger.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 newKey = 0 if newKey is Number.MAX_VALUE req.end() stopTCPServers = (servers, callback) -> promises = [] for server in servers do (server) -> defer = Q.defer() server.server.close (err) -> if err logger.error "Could not close tcp server: #{err}" defer.reject err else logger.info "Channel #{server.channelID}: Stopped TCP/TLS server" defer.resolve() promises.push defer.promise (Q.all promises).then -> callback() exports.stopServers = (callback) -> stopTCPServers tcpServers, -> tcpServers = [] callback() exports.notifyMasterToStopTCPServer = (channelID, callback) -> logger.debug "Sending message to master to stop tcp channel: #{channelID}" process.send type: 'stop-tcp-channel' channelID: channelID exports.stopServerForChannel = (channelID, callback) -> server = null notStoppedTcpServers = [] for serverDetails in tcpServers if serverDetails.channelID.equals channelID server = serverDetails else # push all except the server we're stopping notStoppedTcpServers.push serverDetails return callback "Server for channel #{channelID} not running" if not server tcpServers = notStoppedTcpServers stopTCPServers [server], callback if process.env.NODE_ENV == "test" exports.tcpServers = tcpServers