648 lines
28 KiB
JavaScript
648 lines
28 KiB
JavaScript
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);
|
|
}
|