diff --git a/examples/lib/store.js b/examples/helpers/store.js similarity index 100% rename from examples/lib/store.js rename to examples/helpers/store.js diff --git a/lib/node-http-proxy.js b/lib/node-http-proxy.js index 4769312..12bc716 100644 --- a/lib/node-http-proxy.js +++ b/lib/node-http-proxy.js @@ -56,12 +56,14 @@ var HttpProxy = exports.HttpProxy = require('./node-http-proxy/http-proxy' // exports.createServer = function () { var args = Array.prototype.slice.call(arguments), + handlers = [], options = {}, - host, port, - server, proxy, - callback, + message, handler, - silent; + server, + proxy, + host, + port; // // Liberally parse arguments of the form: @@ -75,18 +77,50 @@ exports.createServer = function () { case 'string': host = arg; break; case 'number': port = arg; break; case 'object': options = arg || {}; break; - case 'function': callback = arg; break; + case 'function': handlers.push(arg); break; }; }); - if (!host && !port && !options) { - // - // If `host`, `port` and `options` are all not passed, then - // this server is improperly configured. - // - throw new Error('Cannot proxy without port, host, or router.') - } + // + // Helper function to create intelligent error message(s) + // for the very liberal arguments parsing performed by + // `require('http-proxy').createServer()`. + // + function validArguments() { + var conditions = { + 'port and host': function () { + return port && host; + }, + 'options.target or options.router': function () { + return options && (options.router || + (options.target && options.target.host && options.target.port)); + }, + 'or proxy handlers': function () { + return handlers && handlers.length; + } + } + + var missing = Object.keys(conditions).filter(function (name) { + return !conditions[name](); + }); + + if (missing.length === 3) { + message = 'Cannot proxy without ' + missing.join(', '); + return false; + } + + return true; + } + if (!validArguments()) { + // + // If `host`, `port` and `options` are all not passed (with valid + // options) then this server is improperly configured. + // + throw new Error(message); + return; + } + // // Hoist up any explicit `host` or `port` arguments // that have been passed in to the options we will @@ -96,28 +130,57 @@ exports.createServer = function () { options.target.port = options.target.port || port; options.target.host = options.target.host || host; + if (options.target && options.target.host && options.target.port) { + // + // If an explicit `host` and `port` combination has been passed + // to `.createServer()` then instantiate a hot-path optimized + // `HttpProxy` object and add the "proxy" middleware layer. + // + proxy = new HttpProxy(options); + handlers.push(function (req, res) { + proxy.proxyRequest(req, res); + }); + } + else { + // + // If no explicit `host` or `port` combination has been passed then + // we have to assume that this is a "go-anywhere" Proxy (i.e. a `RoutingProxy`). + // + proxy = new RoutingProxy(options); + + if (options.router) { + // + // If a routing table has been supplied than we assume + // the user intends us to add the "proxy" middleware layer + // for them + // + handlers.push(function (req, res) { + proxy.proxyRequest(req, res); + }); + + proxy.on('routes', function (routes) { + server.emit('routes', routes); + }); + } + } + // // Create the `http[s].Server` instance which will use // an instance of `httpProxy.HttpProxy`. // - proxy = new HttpProxy(options); - handler = callback - ? function (req, res) { callback(req, res, proxy) } - : function (req, res) { proxy.proxyRequest(req, res) }; + handler = handlers.length > 1 + ? exports.stack(handlers, proxy) + : function (req, res) { handlers[0](req, res, proxy) }; server = options.https ? https.createServer(options.https, handler) : http.createServer(handler); - //server.on('close', function () { - // proxy.close(); - //}); - - proxy.on('routes', function (routes) { - server.emit('routes', routes); + server.on('close', function () { + proxy.close(); }); - if (!callback) { + if (handlers.length <= 1) { // // If an explicit callback has not been supplied then // automagically proxy the request using the `HttpProxy` @@ -213,6 +276,13 @@ exports.setMaxSockets = function (value) { // // ### function stack (middlewares, proxy) +// #### @middlewares {Array} Array of functions to stack. +// #### @proxy {HttpProxy|RoutingProxy} Proxy instance to +// Iteratively build up a single handler to the `http.Server` +// `request` event (i.e. `function (req, res)`) by wrapping +// each middleware `layer` into a `child` middleware which +// is in invoked by the parent (i.e. predecessor in the Array). +// // adapted from https://github.com/creationix/stack // exports.stack = function stack (middlewares, proxy) { @@ -235,9 +305,18 @@ exports.stack = function stack (middlewares, proxy) { return; } - child(req, res); - } + if (child) { + child(req, res); + } + }; + // + // Set the prototype of the `next` function to the instance + // of the `proxy` so that in can be used interchangably from + // a `connect` style callback and a true `HttpProxy` object. + // + // e.g. `function (req, res, next)` vs. `function (req, res, proxy)` + // next.__proto__ = proxy; layer(req, res, next); }; diff --git a/lib/node-http-proxy/http-proxy.js b/lib/node-http-proxy/http-proxy.js index babf1ac..e02be9c 100644 --- a/lib/node-http-proxy/http-proxy.js +++ b/lib/node-http-proxy/http-proxy.js @@ -671,7 +671,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) // HttpProxy.prototype.close = function () { [this.forward, this.target].forEach(function (proxy) { - if (proxy.agent) { + if (proxy && proxy.agent) { proxy.agent.sockets.forEach(function (socket) { socket.end(); });