sepro
Version:
Sepro is a http proxy which discovers where to proxy a request.
150 lines (114 loc) • 3.13 kB
JavaScript
var EventEmitter = require('events').EventEmitter
, inherits = require('util').inherits
, RoutingProxy = require('http-proxy').RoutingProxy
, debug = require('debug')('sepro:dispatcher')
, http = require('http')
module.exports = Sepro
function Sepro(options) {
if (!(this instanceof Sepro)) return new Sepro(options)
RoutingProxy.call(this, options)
this.routers = []
this.reducers = []
}
inherits(Sepro, RoutingProxy)
Sepro.prototype.use = function (layer) {
debug('use %s', layer.constructor && layer.constructor.name || layer.name || 'anonymous')
if (layer.attach) {
layer.attach(this)
}
if (layer.getProxyLocation) {
this.routers.push(layer)
}
if (layer.reduce) {
this.reducers.push(layer)
}
if (layer.close) {
this.once('close', function () {
layer.close()
})
}
if (layer instanceof EventEmitter) {
layer.on('error', this.emit.bind(this, 'error'))
}
return this
}
Sepro.prototype.getProxyLocation = function (target, req) {
var index
, targets
, layer
, reduced
if (typeof target === 'object' && target.headers) {
req = target
target = target.headers.host || ''
}
target = target.split(':')[0]
index = 0
while(layer = this.routers[index++]) {
targets = layer.getProxyLocation(target, req)
if (targets) break
}
if (!targets || !Array.isArray(targets)) return targets || null
if (req) {
index = 0
while ((layer = this.reducers[index++]) && targets.length > 1) {
reduced = layer.reduce(req, targets)
if (reduced) targets = reduced
}
}
return targets.length > 1
? targets[ Math.floor( Math.random() * targets.length ) ]
: targets[0]
|| null
}
Sepro.prototype.handleRequest = function (req, res, next) {
var target = this.getProxyLocation(req)
if (!target) {
if (next) return next()
res.writeHead(404)
res.end()
return
}
debug('proxy request', target)
this.proxyRequest(req, res, target)
}
// Connect compatibility
Sepro.prototype.handle = Sepro.prototype.handleRequest
Sepro.prototype.handleWebSocketRequest = function (req, socket, head, next) {
var target = this.getProxyLocation(req)
if (!target) {
if (next) return next()
socket.destroy()
return
}
this.proxyWebSocketRequest(req, socket, head, target)
}
Sepro.prototype.attach = function (server, options) {
var self = this
server.on('request', function (req, res) {
self.handleRequest(req, res)
})
server.on('upgrade', function (req, socket, head) {
self.handleWebSocketRequest(req, socket, head)
})
}
Sepro.prototype.listen = function (port, options, cb) {
if (typeof options === 'function') {
cb = options
options = {}
}
var server = http.createServer()
this.attach(server, options)
this.once('close', function () {
try {
server.close()
} catch (err) {
if (err.message !== 'Not running') this.emit('error', err)
}
})
server.listen(port, cb)
return server
}
Sepro.prototype.close = function() {
this.emit('close')
RoutingProxy.prototype.close.call(this)
}