From b054cdb4e9bb9d9b0f95aa6a2acc3e66889f588e Mon Sep 17 00:00:00 2001 From: Hadrien Jouet Date: Sun, 27 Jan 2013 13:05:16 -0800 Subject: [PATCH] Manual proxy routes are now persistent and proxies will fail gracefully if port is in use --- index.js | 3 +++ lib/proxy.js | 69 +++++++++++++++++++++++++++++++++++++++---------- models/drone.js | 14 +++++++--- models/proxy.js | 41 +++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 18 deletions(-) create mode 100644 models/proxy.js diff --git a/index.js b/index.js index 613cc2b..7f48a7d 100644 --- a/index.js +++ b/index.js @@ -93,6 +93,9 @@ var http_proxy = require('./lib/proxy').Proxy, //start default proxy proxy.start(app.config.get('public-port')); +//load persistent proxy routes +proxy.autoload(); + //define routes require('./lib/ishiki')(app, haibu, path, fs, drone, proxy); diff --git a/lib/proxy.js b/lib/proxy.js index 9e89cf1..0d7ec25 100644 --- a/lib/proxy.js +++ b/lib/proxy.js @@ -1,4 +1,5 @@ -var http_proxy = require('http-proxy'); +var http_proxy = require('http-proxy'), + proxyModel = require('../models/proxy'); var Proxy = exports.Proxy = function(app, haibu) { var self = this; @@ -44,7 +45,7 @@ var Proxy = exports.Proxy = function(app, haibu) { var route_message = this.req.body.domain + ':' + port + ' > ' + pkg.host + ':' + pkg.port; - if (self.set(port, pkg, this.req.body.domain)) + if (self.set(port, pkg, this.req.body.domain, true)) haibu.sendResponse(this.res, 200, {message: 'Proxy route added: ' + route_message}); else haibu.sendResponse(this.res, 500, {message: 'Could not create route: ' + route_message}); @@ -130,7 +131,15 @@ Proxy.prototype.start = function(port) { //forward request to target proxy.proxyRequest(req, res, self.proxies[port].routes[host]); } - }).listen(port); + }); + + this.proxies[port].proxy.on('error', function(err) { + //if proxy failed to start, remove it + console.log('Could not start proxy on ' + port, err); + delete self.proxies[port]; + }); + + this.proxies[port].proxy.listen(port); return true; } @@ -141,7 +150,7 @@ Proxy.prototype.start = function(port) { //stops proxy on given port Proxy.prototype.stop = function(port) { if (this.proxies[port]) { - this.proxies[port].close(); + this.proxies[port].proxy.close(); return true; } @@ -161,16 +170,25 @@ Proxy.prototype.clean = function(port) { }; //sets domain target -Proxy.prototype.set = function(port, pkg, domain) { - if (domain.trim() != '' && this.proxies[port] && pkg.host && pkg.port) { - this.proxies[port].routes[domain] = { - host: pkg.host, - port: (pkg.env.PORT), - user: pkg.user, - appid: pkg.name - }; - - return true; +Proxy.prototype.set = function(port, pkg, domain, persist) { + if (domain.trim() != '' && this.proxies[port] && pkg.host) { + var target_port = (pkg.env ? pkg.env.PORT : null) || pkg.port; + + if (target_port) { + this.proxies[port].routes[domain] = { + host: pkg.host, + port: target_port, + user: pkg.user, + appid: pkg.name, + persist: persist + }; + + //save to db if persistent + if (persist) + proxyModel.createOrUpdate({host: domain, port: port}, this.proxies[port].routes[domain], function(){}); + + return true; + } } return false; @@ -213,6 +231,10 @@ Proxy.prototype.getBy = function(port, match) { //delete domain target Proxy.prototype.delete = function(port, domain) { if (this.proxies[port] && this.proxies[port].routes[domain]) { + //remove from db if persistent + if (this.proxies[port].routes[domain].persist) + proxyModel.deleteBy({source: {host: domain, port: port}}, console.log); + delete this.proxies[port].routes[domain]; return true; @@ -260,3 +282,22 @@ Proxy.prototype.load = function(port, pkg) { return false; }; +//load all persistent routes from db +Proxy.prototype.autoload = function() { + var self = this; + + proxyModel.get(function(err, routes) { + if (err) + return console.log(err); + + if (routes.length > 0) { + routes.forEach(function(route) { + //create proxy if it doesn't exist + if (!self.proxies[route.source.port]) + self.start(route.source.port); + + self.set(route.source.port, route.target, route.source.host, true); + }); + } + }); +}; \ No newline at end of file diff --git a/models/drone.js b/models/drone.js index c8a7942..700f54c 100644 --- a/models/drone.js +++ b/models/drone.js @@ -5,9 +5,15 @@ var BaseModel = require('./_base').BaseModel, var illegal_chars = ['/', '\\', '.', ' ', '"', '*', '<', '>', ':', '|', '?'], escape_with = '__'; +var DroneModel = function() { + DroneModel.super_.apply(this, arguments); +}; + +util.inherits(DroneModel, BaseModel); + //processes mongodb illegal characters in keys //pre to true preprocesses, otherwise postprocesses -BaseModel.prototype.process = function(data, pre) { +DroneModel.prototype.process = function(data, pre) { var self = this; Object.keys(data).forEach(function(key) { @@ -35,7 +41,7 @@ BaseModel.prototype.process = function(data, pre) { return data; }; -BaseModel.prototype.getProcessed = function(filter, callback) { +DroneModel.prototype.getProcessed = function(filter, callback) { var self = this; this.get(filter, function(err, result) { @@ -50,7 +56,7 @@ BaseModel.prototype.getProcessed = function(filter, callback) { }); }; -BaseModel.prototype.createOrUpdate = function(pkg, callback) { +DroneModel.prototype.createOrUpdate = function(pkg, callback) { var self = this; if (pkg._id) delete pkg._id; @@ -79,4 +85,4 @@ BaseModel.prototype.createOrUpdate = function(pkg, callback) { }); }; -module.exports = new BaseModel({collection: 'drone'}); \ No newline at end of file +module.exports = new DroneModel({collection: 'drone'}); \ No newline at end of file diff --git a/models/proxy.js b/models/proxy.js new file mode 100644 index 0000000..9696bb8 --- /dev/null +++ b/models/proxy.js @@ -0,0 +1,41 @@ +var BaseModel = require('./_base').BaseModel, + util = require('util'); + +var ProxyModel = function() { + ProxyModel.super_.apply(this, arguments); +}; + +util.inherits(ProxyModel, BaseModel); + +ProxyModel.prototype.createOrUpdate = function(source, target, callback) { + var self = this; + + this.get({source: source}, function(err, result) { + if (err) + return callback(err); + + if (result.length == 1) + self.edit(result[0]._id.toString(), {$set: {target: target}}, callback); + else + self.add({source: source, target: target}, callback); + }); +}; + +ProxyModel.prototype.deleteBy = function(filter, callback) { + var self = this; + + this.get(filter, function(err, result) { + if (err) + return callback(err); + + if (result.length > 0) { + result.forEach(function(route) { + self.delete(route._id.toString(), function() {}); + }); + + callback(null); + } + }); +}; + +module.exports = new ProxyModel({collection: 'proxy'}); \ No newline at end of file