Created
May 30, 2013 01:49
-
-
Save meltzerj/5675279 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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