Comparing version 0.0.1 to 1.0.0
15
index.js
// Requires | ||
var httpProxy = require('http-proxy'); | ||
var utils = require('./lib/utils'); | ||
var server = require('./lib/server'); | ||
var patterns = require('./lib/patterns'); | ||
var middleware = require('./lib/middleware'); | ||
var selectors = require('./lib/selectors/'); | ||
var balancers = require('./lib/balancers/'); | ||
var groupBalancer = require('./lib/groupbalancer'); | ||
@@ -21,5 +21,6 @@ | ||
exports.server = server; | ||
exports.patterns = patterns; | ||
exports.middleware = middleware; | ||
exports.selectors = selectors; | ||
exports.balancers = balancers; | ||
exports.groupBalancer = groupBalancer; | ||
exports.utils = utils; |
// Requires | ||
var _ = require('underscore'); | ||
var patterns = require('./patterns'); | ||
@@ -47,4 +46,10 @@ // Constants | ||
var y = _.clone(x); | ||
y.pattern = patterns.getPattern(x.pattern); | ||
y.backends = that.resolveBackends(y.backends); | ||
y.middleware = y.middleware || []; | ||
// Bind methods to resource | ||
_.bindAll(y); | ||
return y; | ||
@@ -54,2 +59,3 @@ }); | ||
}; | ||
ConfigLoader.prototype.resolve = function() { | ||
@@ -60,3 +66,3 @@ // Resolve patterns and what not | ||
var newConfig = _.clone(this.config); | ||
// Update copied config | ||
@@ -75,25 +81,2 @@ newConfig.resources = resources; | ||
ConfigLoader.prototype.loadPatterns = function() { | ||
var patternPaths = this.config.patternPaths || []; | ||
// Dynamic importing | ||
patternPaths.forEach(function(path) { | ||
var mod = require(path); | ||
var patterns = mod.PATTERNS; | ||
// Skip if no patterns defined | ||
if(_.isUndefined(patterns)) { | ||
console.log('WARNING ', path, ' has not .PATTERNS attribute to laod'); | ||
return; | ||
} | ||
// Register each pattern | ||
for(var key in patterns) { | ||
patterns.register(key, patterns[key]); | ||
} | ||
}); | ||
return; | ||
}; | ||
ConfigLoader.prototype.load = function() { | ||
@@ -109,6 +92,2 @@ // Set defaults | ||
// Load in patterns | ||
// each pattern module should register it's own functions | ||
this.loadPatterns(); | ||
// Resolve patterns etc ... | ||
@@ -115,0 +94,0 @@ return this.resolve(); |
// Requires | ||
var Q = require('q'); | ||
var _ = require('underscore'); | ||
var utils = require('./utils'); | ||
function GroupBalancer(resources) { | ||
// Build our patterns | ||
var patterns = resources.map(function(resource) { | ||
var pattern = resource.pattern; | ||
return new pattern(resource); | ||
}); | ||
function GroupBalancer(config, resources) { | ||
// Bind methods to resource | ||
this.resources = resources; | ||
this.patterns = patterns || []; | ||
this.config = config; | ||
// Bind methods | ||
utils.bindMethods(this); | ||
_.bindAll(this); | ||
} | ||
GroupBalancer.prototype.selectPattern = function(req) { | ||
var filteredPatterns = this.patterns.filter(function(x) { | ||
return x.shouldHandle(req); | ||
GroupBalancer.prototype.selectResource = function(req) { | ||
var resources = this.resources; | ||
return Q.all( | ||
// Array of booleans saying if the resource can handle | ||
this.resources.map(function(resource) { | ||
// Should handle ? | ||
return Q.nfcall(resource.selector, req); | ||
}) | ||
) | ||
.then(function (results) { | ||
// Associate resources with their respective results | ||
return _.first(_.map( | ||
_.filter( | ||
_.zip(resources, results), | ||
_.last | ||
), | ||
_.first | ||
)); | ||
}) | ||
.then(function(resource) { | ||
if(!resource) { | ||
throw new Error("No resource can handle the request"); | ||
} | ||
return resource; | ||
}); | ||
}; | ||
// Error handler | ||
if(filteredPatterns.length < 1) { | ||
return undefined; | ||
} | ||
GroupBalancer.prototype.selectBackend = function(req, resource) { | ||
return Q.nfcall(resource.balancer, resource.backends, req) | ||
.then(function(backend) { | ||
if(!backend) { | ||
throw new Error("Balancer could not give a valid backend to route to"); | ||
} | ||
return [backend, resource]; | ||
}); | ||
}; | ||
// Pick first | ||
var handler = filteredPatterns[0]; | ||
return handler; | ||
// selectResource then selectBackend | ||
GroupBalancer.prototype.grabBackend = function(req) { | ||
return this.selectResource(req) | ||
.then(_.partial(this.selectBackend, req)); | ||
}; | ||
GroupBalancer.prototype.applyMiddleware = function(resource, req, res) { | ||
// Apply a series of middleware | ||
return _.map(resource.middleware, function(middleware) { | ||
return Q.nfcall(middleware, req, res); | ||
}).reduce(Q.when, Q()); | ||
}; | ||
GroupBalancer.prototype.handle = function(req, res, proxy) { | ||
var handler = this.selectPattern(req); | ||
var self = this; | ||
console.log('Handle: ', req.headers.host+req.url); | ||
// Pause request stream | ||
req.pause(); | ||
// Handle no handler | ||
if(_.isUndefined(handler)) { | ||
res.write('ERROR: Loadfire does not know how to handle that request'); | ||
return this.grabBackend(req) | ||
.spread(function(backend, resource) { | ||
// Log | ||
console.log('Handle: ', req.headers.host+req.url); | ||
// Apply middleware | ||
return self.applyMiddleware(resource, req, res) | ||
.then(function() { | ||
console.log('backend =', backend); | ||
// Resume request | ||
req.resume(); | ||
// Handle (actual proxying) | ||
proxy.proxyRequest(req, res, _.clone(backend)); | ||
return backend; | ||
}); | ||
}) | ||
.fail(function(err) { | ||
console.log(err.stack); | ||
res.write(err.stack); | ||
res.write('\nERROR: Loadfire does not know how to handle that request\n'); | ||
res.end(); | ||
return; | ||
} | ||
// Handle | ||
return handler.handle(req, res, proxy); | ||
}); | ||
}; | ||
GroupBalancer.prototype.handleWs = function(req, socket, head) { | ||
var handler = this.selectPattern(req); | ||
return this.grabBackend(req) | ||
.spread(function(backend, resource) { | ||
// Log | ||
console.log('handleWs: ', req.headers.host+req.url); | ||
console.log('handleWs: ', req.headers.host+req.url); | ||
// Build proxy | ||
var proxy = new httpProxy.HttpProxy({ | ||
target: _.clone(backend) | ||
}); | ||
// Buffer from socket | ||
var buffer = httpProxy.buffer(socket); | ||
// Handle no handler | ||
if(_.isUndefined(handler)) { | ||
// Actual proxying | ||
proxy.proxyWebSocketRequest(req, socket, head, buffer); | ||
return backend; | ||
}) | ||
.fail(function(err) { | ||
socket.write('ERROR: Loadfire does not know how to handle that request'); | ||
socket.close(); | ||
return; | ||
} | ||
}); | ||
}; | ||
// Handle | ||
return handler.handleWs(req, socket, head); | ||
// TCP Handler, just keep the connection alive | ||
// do nothing special besides some logging | ||
GroupBalancer.prototype.tcpConnectionHandler = function (connection) { | ||
var remoteAddress = connection.remoteAddress, | ||
remotePort = connection.remotePort, | ||
start = Date.now(); | ||
var getSocketInfo = function () { | ||
return JSON.stringify({ | ||
remoteAddress: remoteAddress, | ||
remotePort: remotePort, | ||
bytesWritten: connection.bytesWritten, | ||
bytesRead: connection.bytesRead, | ||
elapsed: (Date.now() - start) / 1000 | ||
}); | ||
}; | ||
connection.setKeepAlive(false); | ||
connection.setTimeout(this.config.tcpTimeout * 1000); | ||
connection.on('error', function (error) { | ||
console.log('TCP error from ' + getSocketInfo() + '; Error: ' + JSON.stringify(error)); | ||
}); | ||
connection.on('timeout', function () { | ||
console.log('TCP timeout from ' + getSocketInfo()); | ||
connection.destroy(); | ||
}); | ||
}; | ||
@@ -65,0 +156,0 @@ |
// Requires | ||
var http = require('http'); | ||
var _ = require('underscore'); | ||
var httpProxy = require('http-proxy'); | ||
var utils = require('./utils'); | ||
var config = require('./config'); | ||
var GroupBalancer = require('./groupbalancer').GroupBalancer; | ||
function Server(_config) { | ||
@@ -13,3 +15,3 @@ // Validate and Resolve our configuration | ||
// Get the group balancer | ||
this.balancer = new GroupBalancer(this.config.resources); | ||
this.balancer = new GroupBalancer(this.config, this.config.resources); | ||
@@ -20,3 +22,3 @@ // Setup server and the different handlers | ||
// Bind methods | ||
utils.bindMethods(this); | ||
_.bindAll(this); | ||
} | ||
@@ -30,6 +32,3 @@ | ||
// Handle TCP connections | ||
var that = this; | ||
this.server.on('connection', function() { | ||
that.tcpConnectionHandler.apply(that, arguments); | ||
}); | ||
this.server.on('connection', this.balancer.tcpConnectionHandler); | ||
@@ -40,6 +39,7 @@ // Handle websocket upgrades | ||
Server.prototype.run = function () { | ||
// Server listen | ||
Server.prototype.listen = function () { | ||
var args = _.isEmpty(arguments) ? [this.config.port] : _.toArray(arguments); | ||
// List on port | ||
this.server.listen(this.config.port); | ||
this.server.listen.apply(this.server, args); | ||
@@ -49,35 +49,7 @@ return this; | ||
// Alias | ||
Server.prototype.run = Server.prototype.listen; | ||
// TCP Handler, just keep the connection alive | ||
// do nothing special besides some logging | ||
Server.prototype.tcpConnectionHandler = function (connection) { | ||
var remoteAddress = connection.remoteAddress, | ||
remotePort = connection.remotePort, | ||
start = Date.now(); | ||
var getSocketInfo = function () { | ||
return JSON.stringify({ | ||
remoteAddress: remoteAddress, | ||
remotePort: remotePort, | ||
bytesWritten: connection.bytesWritten, | ||
bytesRead: connection.bytesRead, | ||
elapsed: (Date.now() - start) / 1000 | ||
}); | ||
}; | ||
connection.setKeepAlive(false); | ||
connection.setTimeout(this.config.tcpTimeout * 1000); | ||
connection.on('error', function (error) { | ||
console.log('TCP error from ' + getSocketInfo() + '; Error: ' + JSON.stringify(error)); | ||
}); | ||
connection.on('timeout', function () { | ||
console.log('TCP timeout from ' + getSocketInfo()); | ||
connection.destroy(); | ||
}); | ||
}; | ||
// Exports | ||
module.exports.Server = Server; |
{ | ||
"name" : "loadfire", | ||
"version" : "0.0.1", | ||
"version" : "1.0.0", | ||
"description" : "A clean and simple load balancer", | ||
"keywords": [ | ||
"loadfire", | ||
"reverse", | ||
@@ -24,8 +23,5 @@ "proxy", | ||
"http-proxy" : "0.8.4", | ||
"redis" : "0.7.x", | ||
"lru-cache": "2.1.x", | ||
"optimist": "0.3.x", | ||
"underscore": "1.4.2", | ||
"q": "0.8.9", | ||
"async": "0.1.22" | ||
"optimist": "0.3.0", | ||
"underscore": "1.4.4", | ||
"q": "0.9.6" | ||
}, | ||
@@ -36,3 +32,3 @@ "scripts" : { | ||
"engines" : { | ||
"node" : "0.8.x" | ||
"node" : ">=0.8.0" | ||
}, | ||
@@ -42,7 +38,7 @@ "engineStrict": true, | ||
"name" : "Aaron O'Mullan", | ||
"email" : "aaron.omullan@gmail.com", | ||
"url" : "http://friendco.de" | ||
"email" : "aaron.omullan@friendco.de", | ||
"url" : "https://friendco.de" | ||
}, | ||
"maintainer" : [ | ||
"Aaron O'Mullan <aaron.omullan@gmail.com>" | ||
"Aaron O'Mullan <aaron.omullan@friendco.de>" | ||
], | ||
@@ -49,0 +45,0 @@ "repository": { |
@@ -35,3 +35,3 @@ var http = require('http'); | ||
// by default it should be the hostname to match | ||
resource: 'localhost:8000', | ||
selector: loadfire.selectors.host('localhost:8000'), | ||
@@ -44,3 +44,3 @@ // List of backends to hit | ||
// random, roundrobin, sticky | ||
pattern: 'roundrobin' | ||
balancer: loadfire.balancers.roundrobin | ||
} | ||
@@ -47,0 +47,0 @@ ], |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
4
1
0
3
18317
421
+ Addedoptimist@0.3.0(transitive)
+ Addedq@0.9.6(transitive)
+ Addedunderscore@1.4.4(transitive)
- Removedasync@0.1.22
- Removedlru-cache@2.1.x
- Removedredis@0.7.x
- Removedasync@0.1.22(transitive)
- Removedlru-cache@2.1.0(transitive)
- Removedoptimist@0.3.7(transitive)
- Removedq@0.8.9(transitive)
- Removedredis@0.7.3(transitive)
- Removedunderscore@1.4.2(transitive)
Updatedoptimist@0.3.0
Updatedq@0.9.6
Updatedunderscore@1.4.4