Commit 973f19fd authored by indexzero's avatar indexzero

[doc] Added more documentation

parent d8c54063
...@@ -39,7 +39,6 @@ var welcome = '\ ...@@ -39,7 +39,6 @@ var welcome = '\
# # # # # # # # #### # # # \n'; # # # # # # # # #### # # # \n';
util.puts(welcome.rainbow.bold); util.puts(welcome.rainbow.bold);
// //
// Basic Http Proxy Server // Basic Http Proxy Server
// //
......
...@@ -32,7 +32,13 @@ var util = require('util'), ...@@ -32,7 +32,13 @@ var util = require('util'),
ProxyTable = require('./proxy-table').ProxyTable, ProxyTable = require('./proxy-table').ProxyTable,
maxSockets = 100; 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) { function _getAgent (host, port) {
// //
// TODO (indexzero): Make this configurable for http / https // TODO (indexzero): Make this configurable for http / https
...@@ -42,17 +48,33 @@ function _getAgent (host, port) { ...@@ -42,17 +48,33 @@ function _getAgent (host, port) {
return agent; return agent;
} }
//
// ### function getMaxSockets ()
// Returns the maximum number of sockets
// allowed on __every__ outgoing request
// made by __all__ instances of `HttpProxy`
//
exports.getMaxSockets = function () { exports.getMaxSockets = function () {
return maxSockets; 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) { exports.setMaxSockets = function (value) {
maxSockets = value; maxSockets = value;
}; };
//
// ### function createServer ([port, host, options], handler)
//
//
exports.createServer = function () { exports.createServer = function () {
var args, callback, port, host, forward, var args, callback, port, host, forward,
silent, proxyTable, options = {}; silent, options, proxy, server;
args = Array.prototype.slice.call(arguments); args = Array.prototype.slice.call(arguments);
callback = typeof args[args.length - 1] === 'function' && args.pop(); callback = typeof args[args.length - 1] === 'function' && args.pop();
...@@ -69,9 +91,8 @@ exports.createServer = function () { ...@@ -69,9 +91,8 @@ exports.createServer = function () {
} }
} }
var proxy = new HttpProxy(options); proxy = new HttpProxy(options);
server = http.createServer(function (req, res) {
var server = http.createServer(function (req, res) {
winston.verbose('Incoming HTTP request to: ' + req.headers.host + req.url); winston.verbose('Incoming HTTP request to: ' + req.headers.host + req.url);
// If we were passed a callback to process the request // If we were passed a callback to process the request
...@@ -98,13 +119,14 @@ exports.createServer = function () { ...@@ -98,13 +119,14 @@ exports.createServer = function () {
proxy.on('routes', function (routes) { proxy.on('routes', function (routes) {
server.emit('routes', routes); server.emit('routes', routes);
}) });
if (!callback) { if (!callback) {
// WebSocket support: if callback is empty tunnel // WebSocket support: if callback is empty tunnel
// websocket request automatically // websocket request automatically
server.on('upgrade', function(req, socket, head) { server.on('upgrade', function(req, socket, head) {
// Tunnel websocket requests too // Tunnel websocket requests too
proxy.proxyWebSocketRequest(port, host); proxy.proxyWebSocketRequest(port, host);
}); });
} }
...@@ -112,6 +134,25 @@ exports.createServer = function () { ...@@ -112,6 +134,25 @@ exports.createServer = function () {
return server; 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) { var HttpProxy = exports.HttpProxy = function (options) {
events.EventEmitter.call(this); events.EventEmitter.call(this);
...@@ -127,38 +168,36 @@ var HttpProxy = exports.HttpProxy = function (options) { ...@@ -127,38 +168,36 @@ var HttpProxy = exports.HttpProxy = function (options) {
} }
}; };
// Inherit from events.EventEmitter
util.inherits(HttpProxy, events.EventEmitter); util.inherits(HttpProxy, events.EventEmitter);
HttpProxy.prototype.close = function () { //
if (this.proxyTable) this.proxyTable.close(); // ### 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
* Pause `data` and `end` events on the given `obj`. // __must__ utilize this utility, to re-emit data once
* Middleware performing async tasks _should_ utilize // the async operation has completed, otherwise these
* this utility (or similar), to re-emit data once // __events will be lost.__
* the async operation has completed, otherwise these //
* events may be lost. // var pause = httpProxy.pause(req);
* // fs.readFile(path, function(){
* var pause = utils.pause(req); // httpProxy.proxyRequest(req, res, host, port, paused);
* fs.readFile(path, function(){ // });
* next(); //
* pause.resume(); // __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.
* @param {Object} obj // This simply chooses to manage the scope of the events on a new Object literal as opposed to
* @return {Object} // [on the HttpProxy instance](https://github.com/nodejitsu/node-http-proxy/blob/v0.3.1/lib/node-http-proxy.js#L154).
* @api public //
*/
HttpProxy.prototype.pause = function (obj) { HttpProxy.prototype.pause = function (obj) {
var onData, onEnd, events = []; var onData, onEnd, events = [];
// buffer data
obj.on('data', onData = function (data, encoding) { obj.on('data', onData = function (data, encoding) {
events.push(['data', data, encoding]); events.push(['data', data, encoding]);
}); });
// buffer end
obj.on('end', onEnd = function (data, encoding) { obj.on('end', onEnd = function (data, encoding) {
events.push(['end', data, encoding]); events.push(['end', data, encoding]);
}); });
...@@ -177,8 +216,25 @@ HttpProxy.prototype.pause = function (obj) { ...@@ -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) { 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 // 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) { ...@@ -189,6 +245,10 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
if (this.proxyTable && !host) { if (this.proxyTable && !host) {
location = this.proxyTable.getProxyLocation(req); 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) { if (!location) {
res.writeHead(404); res.writeHead(404);
return res.end(); return res.end();
...@@ -198,36 +258,34 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) { ...@@ -198,36 +258,34 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
// When using the ProxyTable in conjunction with an HttpProxy instance // When using the ProxyTable in conjunction with an HttpProxy instance
// only the following arguments are valid: // only the following arguments are valid:
// //
// * proxy.proxyRequest(req, res, port, host, paused): This will be skipped // * `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, paused)`: Paused will get updated appropriately
// * proxy.proxyRequest(req, res): No effect `undefined = undefined` // * `proxy.proxyRequest(req, res)`: No effect `undefined = undefined`
// //
paused = port; paused = port;
port = location.port; port = location.port;
host = location.host; 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) { if (this.options.forward) {
winston.verbose('Forwarding HTTP request to: ' + this.options.forward.host + ':' + this.options.forward.port); winston.verbose('Forwarding HTTP request to: ' + this.options.forward.host + ':' + this.options.forward.port);
this._forwardRequest(req); this._forwardRequest(req);
} }
// Create an error handler so we can use it temporarily function proxyError(err) {
function error (obj) { errState = true;
var fn = function (err) { res.writeHead(500, { 'Content-Type': 'text/plain' });
res.writeHead(500, {'Content-Type': 'text/plain'});
if (req.method !== 'HEAD') { if (req.method !== 'HEAD') {
res.write('An error has occurred: ' + JSON.stringify(err)); 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; res.end();
}; }
// Open new HTTP request to internal resource with will act as a reverse proxy pass // Open new HTTP request to internal resource with will act as a reverse proxy pass
reverseProxy = http.request({ reverseProxy = http.request({
...@@ -263,26 +321,31 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) { ...@@ -263,26 +321,31 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
// Add event listener for end of proxied response // Add event listener for end of proxied response
response.on('end', function () { response.on('end', function () {
reverseProxy.removeListener('error', reverseProxyError); if (!errState) {
res.end(); reverseProxy.removeListener('error', proxyError);
res.end();
}
}); });
}); });
// Add a listener for the connection timeout event // Add a listener for the connection timeout event
var reverseProxyError = error(reverseProxy); reverseProxy.once('error', proxyError);
reverseProxy.on('error', reverseProxyError);
// Chunk the client request body as chunks from the proxied request come in // Chunk the client request body as chunks from the proxied request come in
req.on('data', function (chunk) { req.on('data', function (chunk) {
reverseProxy.write(chunk); if (!errState) {
reverseProxy.write(chunk);
}
}); });
// At the end of the client request, we are going to stop the proxied request // At the end of the client request, we are going to stop the proxied request
req.on('end', function () { req.on('end', function () {
reverseProxy.end(); if (!errState) {
reverseProxy.end();
}
}); });
if (paused) { if (paused && !errState) {
paused.resume(); paused.resume();
} }
}; };
...@@ -308,11 +371,12 @@ HttpProxy.prototype._forwardRequest = function (req) { ...@@ -308,11 +371,12 @@ HttpProxy.prototype._forwardRequest = function (req) {
// //
}); });
// Add a listener for the connection timeout event // Add a listener for the connection timeout event.
forwardProxy.on('error', function (err) { //
// Remark: Ignoring this error in the event // Remark: Ignoring this error in the event
// forward target doesn't exist. // forward target doesn't exist.
}); //
forwardProxy.on('error', function (err) { });
// Chunk the client request body as chunks from the proxied request come in // Chunk the client request body as chunks from the proxied request come in
req.on('data', function (chunk) { req.on('data', function (chunk) {
......
...@@ -26,7 +26,8 @@ ...@@ -26,7 +26,8 @@
var util = require('util'), var util = require('util'),
events = require('events'), events = require('events'),
fs = require('fs'); fs = require('fs'),
winston = require('winston');
// //
// ### function ProxyTable (router, silent) // ### function ProxyTable (router, silent)
...@@ -109,9 +110,7 @@ ProxyTable.prototype.getProxyLocation = function (req) { ...@@ -109,9 +110,7 @@ ProxyTable.prototype.getProxyLocation = function (req) {
host = location[0], host = location[0],
port = location.length === 1 ? 80 : location[1]; port = location.length === 1 ? 80 : location[1];
if (!this.silent) { winston.verbose('Proxy Table proxying request to: ' + host + ':' + port);
util.log('Proxy Table proxying request to: ' + host + ':' + port);
}
return { return {
port: port, 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