var utils = require('./src/utils'); var crossCtl = require('./src/crossCtl'); var app = null; var http = null; var https = null; var fs = require('fs'); var bouncy = require('bouncy'); var crypto = require('crypto'); var tls = require('tls'); const WebSocket = require('ws'); var wss = new WebSocket.Server({ noServer: true }); wss.on('connection', function connection(ws, req) { crossCtl.addWebsockHandle(ws, req); }); var httpProxy = require('http-proxy'); var proxy = httpProxy.createProxyServer({ ws: true }); proxy.on('error', function (err, req, res) { console.log('err=', err); if (typeof res.writeHead == 'function') { res.writeHead(500, { 'Content-Type': 'text/plain', }); } res.end( 'Something went wrong. And we are reporting a custom error message.' ); }); proxy.on('upgrade', function (req, socket, head) { proxy.ws(req, socket, head); }); /* var httpsOptions = { key: fs.readFileSync('./cert/dev2/privkey1.pem'), cert: fs.readFileSync('./cert/dev2/cert1.pem') } */ var express = require('express'); proxy.on('proxyReq', function (proxyReq, req, res, options) { proxyReq.setHeader('x-forwarded-for', utils.getIPFromReq(req)); proxyReq.setHeader('x-forwarded-by', 'bouncy'); }); var ipHashPool = {}; function doProxyWorkWS(req, socket, head) { var targetHost = req.headers.host ? req.headers.host.toLowerCase() : ''; // console.log('targetHost=', targetHost) if (config.bouncy.names.includes(targetHost)) { var ip = utils.getIPFromReq(req); // console.log('fromIP=', ip) var hashedIdx = ipHashPool[ip]; if (hashedIdx === undefined) { hashedIdx = utils.getRandomInt( 0, config.bouncy.destinations[targetHost].hosts ? config.bouncy.destinations[targetHost].hosts.length - 1 : 0 ); console.log('hashedIdx for ', ip, ' is ', hashedIdx); ipHashPool[ip] = hashedIdx; } // var toHost = (config.bouncy.destinations[targetHost].hosts ? utils.pickCandy (config.bouncy.destinations[targetHost].hosts, targetHost) : undefined) var toHost = config.bouncy.destinations[targetHost].hosts ? config.bouncy.destinations[targetHost].hosts[hashedIdx] : undefined; var toPort = config.bouncy.destinations[targetHost].port; var toUrl = config.bouncy.destinations[targetHost].url; var toTarget = toUrl ? toUrl : 'http://' + (toHost ? toHost : utils.getMyPrivateIp()) + ':' + toPort; // console.log('toTarget=', toTarget) proxy.ws(req, socket, head, { target: toTarget }); // res.statusCode = 200; // res.end('ok for now'); } } function doProxyWork(req, res, next) { var targetHost = req.headers.host ? req.headers.host.toLowerCase() : ''; // console.log('targetHost=', targetHost) var fullUrl = req.protocol + '://' + targetHost + req.originalUrl.toLowerCase(); // console.log('fullUrl=', fullUrl) if (config.bouncy.names.includes(targetHost)) { var ip = utils.getIPFromReq(req); // console.log('fromIP=', ip) let protocol = req.headers['x-forwarded-proto'] || req.protocol; let sshDiff = 0; if (protocol == 'https') { sshDiff += config.bouncy.sshDiff; } var hashedIdx = ipHashPool[ip]; if (hashedIdx === undefined) { hashedIdx = utils.getRandomInt( 0, config.bouncy.destinations[targetHost].hosts ? config.bouncy.destinations[targetHost].hosts.length - 1 : 0 ); // console.log("hashedIdx for ", ip, " is ", hashedIdx); ipHashPool[ip] = hashedIdx; } // var toHost = (config.bouncy.destinations[targetHost].hosts ? utils.pickCandy (config.bouncy.destinations[targetHost].hosts, targetHost) : undefined) var toHost = config.bouncy.destinations[targetHost].hosts ? config.bouncy.destinations[targetHost].hosts[hashedIdx] : undefined; var toPort = config.bouncy.destinations[targetHost].port; var toUrl = config.bouncy.destinations[targetHost].url; if (config.bouncy.destinations[targetHost].shareSSL === true) { sshDiff = 0; protocol = 'http'; } var toTarget = toUrl ? toUrl : protocol + '://' + (toHost ? toHost : utils.getMyPrivateIp()) + ':' + (toPort + sshDiff); // console.log("toTarget=", toTarget); proxy.web(req, res, { target: toTarget }); // res.statusCode = 200; // res.end('ok for now'); } else { if ( targetHost === utils.getMyPublicIP() || targetHost === utils.getMyPrivateIp() ) { if (req.url.toLowerCase() === '/ping') { // console.log('pong!') res.statusCode = 200; res.end('pong'); } else if (req.url.toLowerCase() === '/admin/heapdump') { utils.doHeapdump( crossCtl.sConfig.debug, 'heapsnapshot_of_' + crossCtl.sConfig.sid + '_' + utils.getNow().replace(' ', '_') + '.heapsnapshot', function (error, resultPath) { // console.log('heapsnapshot saved...') res.statusCode = 200; res.end('heapsnapshot saved...'); } ); } else { res.statusCode = 404; res.end('Failed host lookup: ' + targetHost); } } else { res.statusCode = 404; res.end('Failed host lookup: ' + targetHost); } } } var server = null; var minimist = require('minimist'); var argv = minimist(process.argv.slice(2)); var configName = argv.config ? argv.config : argv.c; var serviceName = argv.service ? argv.service : argv.s; utils.setServiceName(serviceName); if (configName === undefined) { utils.log('error', 'need configName. use -c or -config flag'); process.exit(1); } else if (serviceName === undefined) { utils.log('error', 'need serviceName. use -s or -service flag'); process.exit(1); } else { var config = require(configName); utils.getLogger(serviceName, config.configPool[serviceName].log); crossCtl.s3.init( { ...config.options.aws, type: config.configPool[serviceName].type, }, function (err) { if (err) { utils.log('error', 's3 init error : ' + err); process.exit(1); } else { crossCtl.boom.init( { ...config.configPool[serviceName], sid: serviceName }, function (err) { if (err) { utils.log('error', 'boom init error : ' + err); process.exit(1); } else { crossCtl.init(config, serviceName, function (err) { if (err) { utils.log( 'error', 'crossCtl init error : ' + err ); process.exit(1); } else { utils.log('info', 'crossCtl init ok'); if ( crossCtl.config.options .bootupReportFlag === true ) { utils.mail.sendMonitorMail( 'bootup report', 'server boot up' ); } var httpPort = normalizePort( crossCtl.config.configPool[serviceName] .port ); var httpsPort = httpPort + config.bouncy.sshDiff; if ( crossCtl.config.configPool[serviceName] .type === 'bouncy' ) { var names = []; Object.keys( config.bouncy.destinations ).forEach(function (name) { names[names.length] = name; var tmpBouncyItem = config.bouncy.destinations[ name ]; if ( config.configPool[ tmpBouncyItem.type + '1' ] !== undefined ) { var creds = { key: fs.readFileSync( config.configPool[ tmpBouncyItem.type + '1' ].certPaths.privateKey, 'utf8' ), cert: fs.readFileSync( config.configPool[ tmpBouncyItem.type + '1' ].certPaths.certificate, 'utf8' ), ca: fs.readFileSync( config.configPool[ tmpBouncyItem.type + '1' ].certPaths.ca, 'utf8' ), }; var ctx = tls.createSecureContext( creds ); tmpBouncyItem.ctx = ctx; } else { var creds = { key: fs.readFileSync( config.configPool[ 'bouncy' ].certPaths.privateKey, 'utf8' ), cert: fs.readFileSync( config.configPool[ 'bouncy' ].certPaths.certificate, 'utf8' ), ca: fs.readFileSync( config.configPool[ 'bouncy' ].certPaths.ca, 'utf8' ), }; var ctx = tls.createSecureContext( creds ); tmpBouncyItem.ctx = ctx; } }); config.bouncy.names = names; http = require('http'); https = require('https'); app = express(); app.use(function (req, res, next) { doProxyWork(req, res, next); }); var serverHttp = http.createServer(app); serverHttp.on( 'upgrade', function upgrade( req, socket, head ) { doProxyWorkWS( req, socket, head ); } ); // doProxyWork(req, res) serverHttp.on('error', onError); serverHttp.listen( httpPort, 'localhost', onListening ); // , '172.26.6.46' serverHttp.listen( 7890, 'localhost', onListening ); serverHttp.listen( httpPort, utils.getMyPrivateIp(), onListening ); var httpsOptions = { SNICallback: function ( servername, cb ) { // console.log("serverHttps(), SNICallback(), servername=",servername); var info = config.bouncy.destinations[ servername ]; // console.log("info =", info); if (info !== undefined) { cb(null, info.ctx.context); } else { cb(new Error('miss'), null); } }, key: fs.readFileSync( config.configPool['bouncy'] .certPaths.privateKey, 'utf8' ), cert: fs.readFileSync( config.configPool['bouncy'] .certPaths.certificate, 'utf8' ), ca: fs.readFileSync( config.configPool['bouncy'] .certPaths.ca, 'utf8' ), }; var serverHttps = https.createServer( httpsOptions, app ); serverHttps.on( 'upgrade', function upgrade( req, socket, head ) { doProxyWorkWS( req, socket, head ); } ); serverHttps.on('error', onError); serverHttps.listen( httpsPort, 'localhost', onListening ); serverHttps.listen( httpsPort, utils.getMyPrivateIp(), onListening ); } else { app = require('./app'); app.set('trust proxy', true); http = require('http'); https = require('https'); app.set('port', httpPort); server = http.createServer(app); server.on('error', onError); server.on('listening', onListening); server.listen( normalizePort( crossCtl.config.configPool[ serviceName ].port ), 'localhost' ); server.listen( normalizePort( crossCtl.config.configPool[ serviceName ].port ), utils.getMyPrivateIp(), onListening ); server.on('upgrade', function upgrade( request, socket, head ) { wss.handleUpgrade( request, socket, head, function done(ws) { wss.emit( 'connection', ws, request ); } ); }); var httpsOptions = { key: fs.readFileSync( config.configPool[serviceName] .certPaths.privateKey, 'utf8' ), cert: fs.readFileSync( config.configPool[serviceName] .certPaths.certificate, 'utf8' ), ca: fs.readFileSync( config.configPool[serviceName] .certPaths.ca, 'utf8' ), }; var httpsServer1 = https .createServer(httpsOptions, app) .listen( httpsPort, 'localhost', function () { console.log( 'Https server listening on port ' + httpsPort ); } ); /* https.createServer(httpsOptions, app).listen(httpsPort, 'localhost', function () { console.log('Https server listening on port ' + httpsPort) }) */ var httpsServer2 = https .createServer(httpsOptions, app) .listen( httpsPort, utils.getMyPrivateIp(), function () { console.log( 'Https server listening on port ' + httpsPort ); } ); httpsServer1.on( 'upgrade', function upgrade( request, socket, head ) { wss.handleUpgrade( request, socket, head, function done(ws) { wss.emit( 'connection', ws, request ); } ); } ); httpsServer2.on( 'upgrade', function upgrade( request, socket, head ) { wss.handleUpgrade( request, socket, head, function done(ws) { wss.emit( 'connection', ws, request ); } ); } ); } } }); } } ); } } ); } function normalizePort(val) { var port = parseInt(val, 10); if (isNaN(port)) { // named pipe return val; } if (port >= 0) { // port number return port; } return false; } /** * Event listener for HTTP server "error" event. */ function onClose() { crossCtl.killGoAccess(); } function onError(error) { if (error.syscall !== 'listen') { throw error; } var bind = typeof crossCtl.config.configPool[serviceName].port === 'string' ? 'Pipe ' + crossCtl.config.configPool[serviceName].port : 'Port ' + crossCtl.config.configPool[serviceName].port; // handle specific listen errors with friendly messages var exitFlag = false; switch (error.code) { case 'EACCES': console.error(bind + ' requires elevated privileges'); exitFlag = true; break; case 'EADDRINUSE': console.error(bind + ' is already in use'); exitFlag = true; break; default: throw error; } if (exitFlag === true) { process.exit(1); } } function onListening() { var addr = this.address(); var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; utils.log('info', 'Listening on ' + bind); }