Commit 973f19fd authored by indexzero's avatar indexzero

[doc] Added more documentation

parent d8c54063
......@@ -39,7 +39,6 @@ var welcome = '\
# # # # # # # # #### # # # \n';
util.puts(welcome.rainbow.bold);
//
// Basic Http Proxy Server
//
......
......@@ -32,7 +32,13 @@ var util = require('util'),
ProxyTable = require('./proxy-table').ProxyTable,
maxSockets = 100;
//
// ### function _getAgent (host, port)
// #### @host {string} Host of the agent to get
// #### @port {number} Port of the agent to get
// Retreives an agent from the `http` module
// and sets the `maxSockets` property appropriately.
//
function _getAgent (host, port) {
//
// TODO (indexzero): Make this configurable for http / https
......@@ -42,17 +48,33 @@ function _getAgent (host, port) {
return agent;
}
//
// ### function getMaxSockets ()
// Returns the maximum number of sockets
// allowed on __every__ outgoing request
// made by __all__ instances of `HttpProxy`
//
exports.getMaxSockets = function () {
return maxSockets;
};
//
// ### function setMaxSockets ()
// Sets the maximum number of sockets
// allowed on __every__ outgoing request
// made by __all__ instances of `HttpProxy`
//
exports.setMaxSockets = function (value) {
maxSockets = value;
};
//
// ### function createServer ([port, host, options], handler)
//
//
exports.createServer = function () {
var args, callback, port, host, forward,
silent, proxyTable, options = {};
silent, options, proxy, server;
args = Array.prototype.slice.call(arguments);
callback = typeof args[args.length - 1] === 'function' && args.pop();
......@@ -69,9 +91,8 @@ exports.createServer = function () {
}
}
var proxy = new HttpProxy(options);
var server = http.createServer(function (req, res) {
proxy = new HttpProxy(options);
server = http.createServer(function (req, res) {
winston.verbose('Incoming HTTP request to: ' + req.headers.host + req.url);
// If we were passed a callback to process the request
......@@ -98,13 +119,14 @@ exports.createServer = function () {
proxy.on('routes', function (routes) {
server.emit('routes', routes);
})
});
if (!callback) {
// WebSocket support: if callback is empty tunnel
// websocket request automatically
server.on('upgrade', function(req, socket, head) {
// Tunnel websocket requests too
proxy.proxyWebSocketRequest(port, host);
});
}
......@@ -112,6 +134,25 @@ exports.createServer = function () {
return server;
};
//
// ### function HttpProxy (options)
// #### @options {Object} Options for this instance.
// Constructor function for new instances of HttpProxy responsible
// for managing the life-cycle of streaming reverse proxyied HTTP requests.
//
// Example options:
//
// {
// router: {
// 'foo.com': 'localhost:8080',
// 'bar.com': 'localhost:8081'
// },
// forward: {
// host: 'localhost',
// port: 9001
// }
// }
//
var HttpProxy = exports.HttpProxy = function (options) {
events.EventEmitter.call(this);
......@@ -127,38 +168,36 @@ var HttpProxy = exports.HttpProxy = function (options) {
}
};
// Inherit from events.EventEmitter
util.inherits(HttpProxy, events.EventEmitter);
HttpProxy.prototype.close = function () {
if (this.proxyTable) this.proxyTable.close();
};
/**
* Pause `data` and `end` events on the given `obj`.
* Middleware performing async tasks _should_ utilize
* this utility (or similar), to re-emit data once
* the async operation has completed, otherwise these
* events may be lost.
*
* var pause = utils.pause(req);
* fs.readFile(path, function(){
* next();
* pause.resume();
* });
*
* @param {Object} obj
* @return {Object}
* @api public
*/
//
// ### function pause (obj)
// #### @obj {Object} Object to pause events from
// Pause `data` and `end` events on the given `obj`.
// Consumers of HttpProxy performing async tasks
// __must__ utilize this utility, to re-emit data once
// the async operation has completed, otherwise these
// __events will be lost.__
//
// var pause = httpProxy.pause(req);
// fs.readFile(path, function(){
// httpProxy.proxyRequest(req, res, host, port, paused);
// });
//
// __Attribution:__ This approach is based heavily on
// [Connect](https://github.com/senchalabs/connect/blob/master/lib/utils.js#L157).
// However, this is not a big leap from the implementation in node-http-proxy < 0.4.0.
// This simply chooses to manage the scope of the events on a new Object literal as opposed to
// [on the HttpProxy instance](https://github.com/nodejitsu/node-http-proxy/blob/v0.3.1/lib/node-http-proxy.js#L154).
//
HttpProxy.prototype.pause = function (obj) {
var onData, onEnd, events = [];
// buffer data
obj.on('data', onData = function (data, encoding) {
events.push(['data', data, encoding]);
});
// buffer end
obj.on('end', onEnd = function (data, encoding) {
events.push(['end', data, encoding]);
});
......@@ -177,8 +216,25 @@ HttpProxy.prototype.pause = function (obj) {
};
};
//
// ### function close ()
// Frees the resources associated with this instance,
// if they exist.
//
HttpProxy.prototype.close = function () {
if (this.proxyTable) this.proxyTable.close();
};
//
// ### function proxyRequest (req, res, [port, host, paused])
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
// #### @port {number} **Optional** Port to use on the proxy target host.
// #### @host {string} **Optional** Host of the proxy target.
// #### @paused {Object} **Optional** Result from `httpProxy.pause(req)`
//
HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
var self = this, reverseProxy, location;
var self = this, reverseProxy, location, errState = false;
//
// Check the proxy table for this instance to see if we need
......@@ -189,6 +245,10 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
if (this.proxyTable && !host) {
location = this.proxyTable.getProxyLocation(req);
//
// If no location is returned from the ProxyTable instance
// then respond with `404` since we do not have a valid proxy target.
//
if (!location) {
res.writeHead(404);
return res.end();
......@@ -198,36 +258,34 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
// When using the ProxyTable in conjunction with an HttpProxy instance
// only the following arguments are valid:
//
// * proxy.proxyRequest(req, res, port, host, paused): This will be skipped
// * proxy.proxyRequest(req, res, paused): Paused will get updated appropriately
// * proxy.proxyRequest(req, res): No effect `undefined = undefined`
// * `proxy.proxyRequest(req, res, port, host, paused)`: This will be skipped
// * `proxy.proxyRequest(req, res, paused)`: Paused will get updated appropriately
// * `proxy.proxyRequest(req, res)`: No effect `undefined = undefined`
//
paused = port;
port = location.port;
host = location.host;
}
//
// If forwarding is enabled for this instance, foward proxy the
// specified request to the address provided in `this.options.forward`
//
if (this.options.forward) {
winston.verbose('Forwarding HTTP request to: ' + this.options.forward.host + ':' + this.options.forward.port);
this._forwardRequest(req);
}
// Create an error handler so we can use it temporarily
function error (obj) {
var fn = function (err) {
res.writeHead(500, {'Content-Type': 'text/plain'});
function proxyError(err) {
errState = true;
res.writeHead(500, { 'Content-Type': 'text/plain' });
if (req.method !== 'HEAD') {
res.write('An error has occurred: ' + JSON.stringify(err));
}
// Response end may never come so removeListener here
obj.removeListener('error', fn);
res.end();
};
return fn;
};
}
// Open new HTTP request to internal resource with will act as a reverse proxy pass
reverseProxy = http.request({
......@@ -263,26 +321,31 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
// Add event listener for end of proxied response
response.on('end', function () {
reverseProxy.removeListener('error', reverseProxyError);
if (!errState) {
reverseProxy.removeListener('error', proxyError);
res.end();
}
});
});
// Add a listener for the connection timeout event
var reverseProxyError = error(reverseProxy);
reverseProxy.on('error', reverseProxyError);
reverseProxy.once('error', proxyError);
// Chunk the client request body as chunks from the proxied request come in
req.on('data', function (chunk) {
if (!errState) {
reverseProxy.write(chunk);
}
});
// At the end of the client request, we are going to stop the proxied request
req.on('end', function () {
if (!errState) {
reverseProxy.end();
}
});
if (paused) {
if (paused && !errState) {
paused.resume();
}
};
......@@ -308,11 +371,12 @@ HttpProxy.prototype._forwardRequest = function (req) {
//
});
// Add a listener for the connection timeout event
forwardProxy.on('error', function (err) {
// Add a listener for the connection timeout event.
//
// Remark: Ignoring this error in the event
// forward target doesn't exist.
});
//
forwardProxy.on('error', function (err) { });
// Chunk the client request body as chunks from the proxied request come in
req.on('data', function (chunk) {
......
......@@ -26,7 +26,8 @@
var util = require('util'),
events = require('events'),
fs = require('fs');
fs = require('fs'),
winston = require('winston');
//
// ### function ProxyTable (router, silent)
......@@ -109,9 +110,7 @@ ProxyTable.prototype.getProxyLocation = function (req) {
host = location[0],
port = location.length === 1 ? 80 : location[1];
if (!this.silent) {
util.log('Proxy Table proxying request to: ' + host + ':' + port);
}
winston.verbose('Proxy Table proxying request to: ' + host + ':' + port);
return {
port: port,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment