Skip to content

Instantly share code, notes, and snippets.

@meltzerj
Created May 30, 2013 01:49
Show Gist options
  • Save meltzerj/5675279 to your computer and use it in GitHub Desktop.
Save meltzerj/5675279 to your computer and use it in GitHub Desktop.
var net = require('net'),
cluster = require('cluster');
function hash(ip, seed) {
var hash = ip.reduce(function(r, num) {
r += parseInt(num, 10);
r %= 2147483648;
r += (r << 10)
r %= 2147483648;
r ^= r >> 6;
return r;
}, seed);
hash += hash << 3;
hash %= 2147483648;
hash ^= hash >> 11;
hash += hash << 15;
hash %= 2147483648;
return hash >>> 0;
}
function bufferContainsXRealIP(buffers) {
var charactersAfterXRealIP = 20,
httpHeaderString = Buffer.concat(buffers).toString(),
httpHeaderArray = httpHeaderString.split('X-Real-IP');
if (httpHeaderArray.length > 1 && httpHeaderArray[1].length >= charactersAfterXRealIP) {
return httpHeaderArray[1].match(/[\d\.]+/)[0];
} else {
return false;
}
}
module.exports = function sticky(num, callback) {
var server;
// `num` argument is optional
if (typeof num !== 'number') {
callback = num;
num = require('os').cpus().length;
}
// Master will spawn `num` workers
if (cluster.isMaster) {
var workers = [];
for (var i = 0; i < num; i++) {
!function spawn(i) {
workers[i] = cluster.fork();
// Restart worker on exit
workers[i].on('exit', function() {
console.log('sticky-session: worker died');
spawn(i);
});
}(i);
}
var seed = ~~(Math.random() * 1e9);
server = net.createServer(function(socket){
var worker, buffers = [], ipAddress, ipHash;
console.log('request');
socket.on('data', function(chunk){
console.log('chunk');
buffers.push(chunk);
if (ipAddress = bufferContainsXRealIP(buffers)) {
console.log(ipAddress);
socket.pause();
ipHash = hash((ipAddress || '').split(/\./g), seed);
worker = workers[ipHash % workers.length];
worker.send(Buffer.concat(buffers).toString(), socket._handle);
socket._handle.close();
}
});
});
// server = net.createServer(function(c) {
// // Get int31 hash of ip
// var worker,
//
// // Pause socket (so we won't loose any data)
// c.pause();
// // Pass connection to worker
// worker = workers[ipHash % workers.length];
// worker.send('sticky-session:connection', c._handle);
// // And detach socket from master process
// c._handle.close();
// });
} else {
server = typeof callback === 'function' ? callback() : callback;
// Worker process
process.on('message', function(data, handle) {
//if (msg !== 'sticky-session:connection') return;
var socket = new net.Socket({ handle: handle });
// Socket is non-writable by default
socket.readable = socket.writable = true;
// instead of passing in a node server object, I passed in my own object with the node server as the handle attribute
server.handle.emit('connection', socket);
server.handle.emit('data', new Buffer(data));
// Unpause it
socket.pause();
socket.resume();
});
if (!server) throw new Error('Worker hasn\'t created server!');
// Monkey patch server to do not bind to port
var oldListen = server.handle.listen;
server.handle.listen = function listen() {
var lastArg = arguments[arguments.length - 1];
if (typeof lastArg === 'function') lastArg();
return oldListen.call(this, null);
};
}
return server;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment