Sunday, March 24, 2019

nodejs require('net') cluster server emit connection to express app with http, https modules website chat bot

app.js



var cluster = require('cluster'), net = require('net');
var port = process.env.PORT || 8080;
var num_processes = process.env.WORK || require('os').cpus().length;
var fs = require('fs');
var options = {
pauseOnConnect: true,
key: fs.readFileSync(__dirname + '/private/server.key', 'utf8'),
cert: fs.readFileSync(__dirname + '/private/server.crt', 'utf8'),
requestCert: true,
rejectUnauthorized: false
}
if (cluster.isMaster) {
// This stores our workers. We need to keep them to be able to reference
// them based on source IP address. It's also useful for auto-restart, for example.
var workers = [];

// Helper function for spawning worker at index 'i'.
var spawn = function (i) {
workers[i] = cluster.fork();

// Optional: Restart worker on exit
workers[i].on('exit', function (code, signal) {
console.log('respawning worker', i);
spawn(i);
});
};

// Spawn workers.
for (var i = 0; i < num_processes; i++) {
spawn(i);
}

// Helper function for getting a worker index based on IP address.
// This is a hot path so it should be really fast. The way it works
// is by converting the IP address to a number by removing non numeric
// characters, then compressing it to the number of slots we have.
//
// Compared against "real" hashing (from the sticky-session code) and
// "real" IP number conversion, this function is on par in terms of
// worker index distribution only much faster.
var worker_index = function (ip, len) {
var s = '';
for (var i = 0, _len = ip.length; i < _len; i++) {
if (!isNaN(ip[i])) {
s += ip[i];
}
}
return Number(s) % len;
};

// Create the outside facing server listening on our port.
var server = net.createServer({ pauseOnConnect: true }, function (connection) {
// We received a connection and need to pass it to the appropriate
// worker. Get the worker for this connection's source IP and pass
// it the connection.
var worker = workers[worker_index(connection.remoteAddress, num_processes)];
worker.send('sticky-session:connection', connection);
}).listen(port);

console.log('Master server running on port: ' + port);
} else {
/*nodepro process running...*/
var core = require('./nodepro/core/start');
//if not use options with ssl then ERR_SSL_PROTOCOL_ERROR
var server = core.run(options);

// Listen to messages sent from the master. Ignore everything else.
process.on('message', function (message, connection) {
if (message !== 'sticky-session:connection') {
return;
}
//console.log(message, connection.remoteAddress);

// Emulate a connection event on the server by emitting the
// event with the connection the master sent us.
server.emit('connection', connection);

connection.resume();
});

console.log('ProcId : ' + process.pid + ' is listening to all incoming requests...');
}



nodepro/core/start.js



/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/


exports.run = function (credentials) {
// don't use yet in BOT
//var loader = require('./loader').instance();
// don't use yet
/*
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('socket.io-redis');
var http = app.listen(80, function () {
console.log('ProcId : ' + process.pid + ' is listening to all incoming requests...');
});
*/

// Note we don't use a port here because the master listens on it for us.
var express = require('express'), path = require('path'),
http = require('http'), https = require('https');
//var app = express.createServer(credentials);
var app = new express();
// Don't expose our internal server to the outside.
var server = null;
if (credentials == undefined)
server = http.createServer(app);
else
server = https.createServer(credentials, app);
//var server = app.listen();

/*app use modules[web]*/
//var web_core = loader.module('web');
//web_core.init(express, app);
//or
app.use(express.static('public'));

/*server use modules[chat]*/
/*
if (loader.options.modules.chat.enable) {
var chat = loader.module('chat');
chat.init(server, app);
}
*/
/*server use modules[gamebot]*/
//var bot = loader.module('gamebot');
var bot = require('../modules/gamebot');
bot.init(server, app);

return server;
};



nodepro/modules/web.js


exports.init = function (express, app) {
var loader_package = require('../core/loader');
var loader = loader_package.instance();
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var sys_config = loader.config('sys.config');
var _opt = loader.options;

/*app.configuring*/
//set NODE_ENV=production&&node app.js
function debuggerModule(req, res, next) {
return next();
}
// debugging middleware in development only
if ('development' === app.get('env')) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
app.use(debuggerModule);
}
// process.env['CONSUMER_KEY'] = ""
// process.env['CONSUMER_SECRET'] = ""
// process.env['ACCESS_TOKEN_KEY'] = ""
// process.env['ACCESS_TOKEN_SECRET'] = ""

app.use(bodyParser.urlencoded({ extended: _opt.fw.urlencoded_extended }));// parse application/x-www-form-urlencoded
app.use(bodyParser.json());// parse application/json
app.set('json spaces', 1);
app.use(cookieParser());
app.use(_opt.res.static_path, express.static(loader.static_dir()));

if (_opt.fw.jade_engine) {
app.engine('jade', require('jade').__express);
app.set('view engine', 'jade');
}
if (_opt.fw.disable_view_cache) {
app.disable('view cache');
} else {
app.set('view cache', true);
}
if (_opt.fw.disable_etag) {
app.disable('etag');
}
if (_opt.fw.use_compression) {
var compression = require('compression');
app.use(compression());
}
if (_opt.fw.use_helmet) {
var helmet = require('helmet');
app.use(helmet());
app.use(helmet.noCache());
app.use(helmet.frameguard());
} else {
app.disable('x-powered-by');
}
if (_opt.fw.use_session) {
var sessionLib = loader.lib('session/' + _opt.fw.session_lib);
sessionLib.configure(express, app, loader);
}

/*app.routing*/
function init_loader_base(area_item, app_area) {
var new_loader = loader_package.instance();
var area_name = new_loader._comm.trim(app_area.mountpath, '/');
var base = {};
base.bag = {};
base.loader = new_loader;
base.area = area_name;
base.area_object = area_item;
base.app = app_area;
new_loader._base = base;
return new_loader._base;
}
function parallel(middlewares) {
var async = require("async");
return function (req, res, next) {
async.each(middlewares, function (mw, cb) {
mw(req, res, cb);
}, next);
};
}
function headers(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
}
function routing(req, res, next) {
console.log('routing..................');
headers(req, res, next);
var base = init_loader_base(undefined, app);
var router_package = base.loader.core('router');
var instance = router_package();
instance.route(base, req, res, next);
}
// Router middleware, mentioned it before defining routes.
/*areas*/
sys_config.areas.forEach(function (area_item) {
var app_area = new express();
app.use(area_item.uri, app_area);
app_area.use(function (req, res, next) {
headers(req, res, next);
var base = init_loader_base(area_item, app_area);
var router_package = base.loader.core('router');
var instance = router_package();
instance.route(base, req, res, next);
});
});
/*main*/
if (_opt.fw.use_parallel) {
app.use(parallel([routing]));
} else {
app.use(routing);
}
};



nodepro/modules/gamebot.js


exports.init = function (server, app) {
//var loader_package = require('../core/loader');
//var loader = loader_package.instance();
var socketio = require('socket.io');
//var socket_redis = require('socket.io-redis');
var io = socketio(server);
//var _opt = loader.options;
// Tell Socket.IO to use the redis adapter. By default, the redis
// server is assumed to be on localhost:6379. You don't have to
// specify them explicitly unless you want to change them.
//io.adapter(socket_redis({ host: 'localhost', port: 6379 }));
var botio = io.of('/bot'/*_opt.modules.gamebot.uri*/);

var uuidv4 = require('uuid/v4');
var rooms = {};
var sockets = {};
botio.on('connection', function (socket) {
//console.log('- io.sockets.server.engine.clientsCount = ', io.sockets.server.engine.clientsCount, ' -> ', sockets, ';');

socket.emit('detect', /* send to client */
{ uuid: uuidv4() }
); // emit an event to the socket
socket.on('context', function (data) { /* */
if (rooms[data.context] == undefined) {
rooms[data.context] = {};
}
rooms[data.context][data.player_id] = socket;
sockets[socket.id] = [data.context, data.player_id];
console.log('Connected:: ', data.context, ', ', data.player_id);
});
socket.on('message', function (data) { /* */
var room = rooms[data.context];
var players = Object.keys(room);
players.forEach(function (player_id) {
var friend_socket = room[player_id];
friend_socket.emit('message', data);
});
//var sockets_keys = Object.keys(sockets);
//console.log('sockets.length is ', sockets_keys.length);
//botio.emit('message', data);
// emit an event to all connected sockets
}); // listen to the event

socket.on('disconnect', function () {
var meta = sockets[socket.id];
console.log('(meta != undefined) is ', (meta != undefined));
if (meta != undefined) {
var context = meta[0];
var player_id = meta[1];
console.log('context.length is ', context, ', ', Object.keys(rooms[context]).length);
delete sockets[socket.id];
delete rooms[context][player_id];
console.log('context.length is ', context, ', ', Object.keys(rooms[context]).length);
}
var sockets_keys = Object.keys(sockets);
console.log('sockets.length is ', sockets_keys.length);
console.log('Disconnected:: ', socket.id);
});
});
};



public/index.html
.

<!DOCTYPE html>
<html>

<head>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
</head>

<body>
<script>
$(function () {
function getOrContextId() {
return 'room1';
}
$('#txt').val(window.location);
var my_id = undefined;
var socket = io(window.location + 'bot');
socket.on('detect', function (data) {
my_id = "ID:" + data.uuid;
socket.emit('context', { context: getOrContextId(), player_id: my_id });
$('ul').append($('<li>').html('Me-' + my_id + ': i connected'));
});
socket.on('message', function (data) {
if (data.text !== undefined) {
if (my_id !== data.player_id)
$('ul').append($('<li>').html('Friend-' + data.player_id + ': ' + data.text));
else
$('ul').append($('<li>').html('Me-' + my_id + ': ' + data.text));
}
});
$('#btn').click(function () {
socket.emit('message', {
context: getOrContextId(),
player_id: my_id,
text: $('#txt').val()
});
});
});
</script>
<input type="text" id="txt">
<a id="btn" href="javascript:void(0);">Send</a>
<ul>
<li></li>
</ul>
</body>

</html>


No comments: