balancer.js | |
---|---|
/*
* proxy.js: Responsible for proxying across all applications available to haibu.
*
* (C) 2010, Nodejitsu Inc.
*
*/
var util = require('util'),
path = require('path'),
fs = require('fs'),
events = require('events'),
async = require('async'),
qs = require('querystring'),
httpProxy = require('http-proxy'),
haibu = require('../../haibu'); | |
@matchersRegular expression parsers for handling different
persistent storage files used by | var matchers = {
app: /^([\w|\-]+)\.package\.json/,
pid: /^([\w|\-]+)\.(\d+)\.json/
}; |
function Balancer (options)@options {Object} Options for this instance.Constructor function for the | var Balancer = exports.Balancer = function (options) {
events.EventEmitter.call(this);
var self = this; |
Setup shared state for the app : {name:...,user:...,drones:...[],domains:...[],proxies:{desiredport:[{droneid:,port:}]}} | this.users = { |
user : apps{ name: app} | };
this.domains = { |
Domain : app | };
this.drones = { |
id : {id: ..., user : ..., app: ..., proxies: ...} | };
this.proxies = {};
this.active = {};
|
Setup the balancing proxy using | this.httpProxy = new httpProxy.HttpProxy(options);
this.httpProxy.on('end', function (req, res) {
var diff = Date.now() - req.ptime;
haibu.emit('balancer:proxy', 'info', {
url: req.url,
method: req.method,
time: diff + 'ms'
});
});
}; |
Inherit from | util.inherits(Balancer, events.EventEmitter); |
function handle (req, res)@req {ServerRequest} Incoming server request to balancer@res {ServerResponse} Outoing server request to write to.Attempts to proxy the incoming request to the specified application
by using the | Balancer.prototype.handle = function (req, res, desiredPort) {
var self = this,
host = req.headers.host.split('.'),
domain = host.slice(-2).join('.').toLowerCase();
var apps = this.domains[domain], app, drone;
if (!apps || !apps.length) {
return this.serveText(req, res, {
code: 400,
message: 'App not found for domain: ' + req.headers.host
});
}
var droneMappings, droneMapping;
for (var i = 0; i < apps.length; i++) {
app = apps[i];
droneMappings = app.proxies[desiredPort];
if (drones) {
droneMapping = droneMappings.shift();
droneMappings.push(droneMapping);
}
}
if (!droneMapping) {
return this.serveText(req, res, {
code: 400,
message: 'Drone not found for : ' + req.headers.host + ':' + port
});
}
var droneId = droneMapping.droneId;
var actualPort = droneMapping.port;
req.ptime = Date.now();
haibu.emit('balancer:incoming', 'info', req.headers);
return this.httpProxy.proxyRequest(req, res, droneMapping);
}; |
function serveText (req, res, data)@req {ServerRequest} Incoming server request@res {ServerResponse} Outoing server request to write to.Writes | Balancer.prototype.serveText = function (req, res, data) {
var text = data.message,
diff = Date.now() - req.ptime;
if (data.meta) {
text = [message, qs.unescape(qs.stringify(data.meta, ', '))].join(' | ');
}
res.writeHead(data.code, {
'Content-Length': text.length,
'Content-Type': 'text/plain'
});
if (req.method !== 'HEAD') {
res.write(text);
}
haibu.emit('balancer:serve', 'info', {
text: text,
code: data.code,
time: diff + 'ms'
});
res.end();
}; |
function close ()Closes this balancer by shutting down the child
| Balancer.prototype.close = function () {
this.httpProxy.close();
};
Balancer.prototype.addApp = function (app, callback) {
var user = app.user;
var userApps = this.users[user] || (this.users[user] = {}); |
Clean the app if it already exists | if (userApps[app.name]) {
destroyApp(user, app, add);
}
else {
add();
}
function add() {
app.drones = [];
userApps[app.name] = app;
if (!app.hasOwnProperty('active')) {
app.active = true;
}
var domain = app.domain;
if (domain) {
var domainApps = this.domains[domain] || (this.domains[domain] = []);
domainApps.push(domain);
}
if (app.domains) {
app.domains.forEach(function (domain) {
var domainApps = this.domains[domain] || (this.domains[domain] = []);
domainApps.push(domain);
})
}
callback();
}
};
Balancer.prototype.deactivateApp = function (user, app, callback) {
var userApps = this.users[user];
if (!userApps) {
return callback('User "' + user + '" does not exists.');
}
userApps[app.name].active = false;
return callback();
};
Balancer.prototype.activateApp = function (app, callback) {
var user = app.user,
userApps = this.users[user],
name = app.name;
if (!userApps) {
return callback('User "' + user + '" does not exists.');
}
app = userApps[name];
if (!app) {
return callback('User "' + user + '" has no app "' + name +'"');
}
app.active = true;
return callback();
};
Balancer.prototype.destroyApp = function (app, callback) {
var user = app.user,
userApps = this.users[user],
name = app.name,
empty = true;
if (!userApps) {
return callback('User "' + user + '" does not exists.');
}
app = userApps[name];
if (!app) {
return callback('User "' + user + '" has no app "' + name +'"');
}
delete userApps[name];
for (var k in userApps) {
empty = false;
break;
}
if (empty) {
delete this.users[user];
}
return callback();
};
Balancer.prototype.addDrone = function (droneid, app, callback) {
var user = app.user,
userApps = this.users[user],
name = app.name,
appDrones,
drone;
if (!userApps) {
return callback('User "' + user + '" does not exists.');
}
app = userApps[name];
if (!app) {
return callback('User "' + user + '" has no app "' + name +'"');
}
appDrones = app.drones || (app.drones = []);
drone = this.drones[droneid] = {
id: droneid,
user: user,
app: app,
proxies: {}
};
appDrones.push(drone);
return callback();
};
Balancer.prototype.destroyDrone = function (droneid, callback) {
var drone = this.drones[droneid],
app = drone.app,
appDrones = app.drones,
l = appDrones.length,
droneProxies = drone.proxies,
appProxies = app.proxies,
mapping;
if (!drone) {
return callback('Drone "' + droneid + '" does not exists.');
}
delete this.drones[droneid];
for (var i = 0; i < l; i++) {
if (appDrones[i] === drone) {
appDrones.splice(i,1);
break;
}
}
for (var desiredPort in droneProxies) {
mappings = appProxies[desiredPort];
if (mappings) {
mappings = mappings.filter(function (droneMapping) {
return droneMapping.droneId !== droneid;
});
if (mappings.length === 0) {
delete appProxies[desiredPort];
}
}
}
return callback();
};
Balancer.prototype.proxies = function (mapping, callback) {
for (var droneId in mapping) {
var portMapping = mapping[droneId];
for (var desiredPort in portMapping) {
this.proxy(droneId, desiredPort, portMapping[desiredPort]);
}
}
return callback();
};
Balancer.prototype.proxy = function (droneid, desiredport, actualport, callback) {
};
|