Skip to content

Commit 973f19f

Browse files
committed
[doc] Added more documentation
1 parent d8c5406 commit 973f19f

File tree

3 files changed

+126
-64
lines changed

3 files changed

+126
-64
lines changed

demo.js

-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ var welcome = '\
3939
# # # # # # # # #### # # # \n';
4040
util.puts(welcome.rainbow.bold);
4141

42-
4342
//
4443
// Basic Http Proxy Server
4544
//

lib/node-http-proxy.js

+123-59
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,13 @@ var util = require('util'),
3232
ProxyTable = require('./proxy-table').ProxyTable,
3333
maxSockets = 100;
3434

35-
35+
//
36+
// ### function _getAgent (host, port)
37+
// #### @host {string} Host of the agent to get
38+
// #### @port {number} Port of the agent to get
39+
// Retreives an agent from the `http` module
40+
// and sets the `maxSockets` property appropriately.
41+
//
3642
function _getAgent (host, port) {
3743
//
3844
// TODO (indexzero): Make this configurable for http / https
@@ -42,17 +48,33 @@ function _getAgent (host, port) {
4248
return agent;
4349
}
4450

51+
//
52+
// ### function getMaxSockets ()
53+
// Returns the maximum number of sockets
54+
// allowed on __every__ outgoing request
55+
// made by __all__ instances of `HttpProxy`
56+
//
4557
exports.getMaxSockets = function () {
4658
return maxSockets;
4759
};
4860

61+
//
62+
// ### function setMaxSockets ()
63+
// Sets the maximum number of sockets
64+
// allowed on __every__ outgoing request
65+
// made by __all__ instances of `HttpProxy`
66+
//
4967
exports.setMaxSockets = function (value) {
5068
maxSockets = value;
5169
};
5270

71+
//
72+
// ### function createServer ([port, host, options], handler)
73+
//
74+
//
5375
exports.createServer = function () {
5476
var args, callback, port, host, forward,
55-
silent, proxyTable, options = {};
77+
silent, options, proxy, server;
5678

5779
args = Array.prototype.slice.call(arguments);
5880
callback = typeof args[args.length - 1] === 'function' && args.pop();
@@ -69,9 +91,8 @@ exports.createServer = function () {
6991
}
7092
}
7193

72-
var proxy = new HttpProxy(options);
73-
74-
var server = http.createServer(function (req, res) {
94+
proxy = new HttpProxy(options);
95+
server = http.createServer(function (req, res) {
7596
winston.verbose('Incoming HTTP request to: ' + req.headers.host + req.url);
7697

7798
// If we were passed a callback to process the request
@@ -98,20 +119,40 @@ exports.createServer = function () {
98119

99120
proxy.on('routes', function (routes) {
100121
server.emit('routes', routes);
101-
})
122+
});
102123

103124
if (!callback) {
104125
// WebSocket support: if callback is empty tunnel
105126
// websocket request automatically
106127
server.on('upgrade', function(req, socket, head) {
107128
// Tunnel websocket requests too
129+
108130
proxy.proxyWebSocketRequest(port, host);
109131
});
110132
}
111133

112134
return server;
113135
};
114136

137+
//
138+
// ### function HttpProxy (options)
139+
// #### @options {Object} Options for this instance.
140+
// Constructor function for new instances of HttpProxy responsible
141+
// for managing the life-cycle of streaming reverse proxyied HTTP requests.
142+
//
143+
// Example options:
144+
//
145+
// {
146+
// router: {
147+
// 'foo.com': 'localhost:8080',
148+
// 'bar.com': 'localhost:8081'
149+
// },
150+
// forward: {
151+
// host: 'localhost',
152+
// port: 9001
153+
// }
154+
// }
155+
//
115156
var HttpProxy = exports.HttpProxy = function (options) {
116157
events.EventEmitter.call(this);
117158

@@ -127,38 +168,36 @@ var HttpProxy = exports.HttpProxy = function (options) {
127168
}
128169
};
129170

171+
// Inherit from events.EventEmitter
130172
util.inherits(HttpProxy, events.EventEmitter);
131173

132-
HttpProxy.prototype.close = function () {
133-
if (this.proxyTable) this.proxyTable.close();
134-
};
135-
136-
/**
137-
* Pause `data` and `end` events on the given `obj`.
138-
* Middleware performing async tasks _should_ utilize
139-
* this utility (or similar), to re-emit data once
140-
* the async operation has completed, otherwise these
141-
* events may be lost.
142-
*
143-
* var pause = utils.pause(req);
144-
* fs.readFile(path, function(){
145-
* next();
146-
* pause.resume();
147-
* });
148-
*
149-
* @param {Object} obj
150-
* @return {Object}
151-
* @api public
152-
*/
174+
//
175+
// ### function pause (obj)
176+
// #### @obj {Object} Object to pause events from
177+
// Pause `data` and `end` events on the given `obj`.
178+
// Consumers of HttpProxy performing async tasks
179+
// __must__ utilize this utility, to re-emit data once
180+
// the async operation has completed, otherwise these
181+
// __events will be lost.__
182+
//
183+
// var pause = httpProxy.pause(req);
184+
// fs.readFile(path, function(){
185+
// httpProxy.proxyRequest(req, res, host, port, paused);
186+
// });
187+
//
188+
// __Attribution:__ This approach is based heavily on
189+
// [Connect](https://github.com/senchalabs/connect/blob/master/lib/utils.js#L157).
190+
// However, this is not a big leap from the implementation in node-http-proxy < 0.4.0.
191+
// This simply chooses to manage the scope of the events on a new Object literal as opposed to
192+
// [on the HttpProxy instance](https://github.com/nodejitsu/node-http-proxy/blob/v0.3.1/lib/node-http-proxy.js#L154).
193+
//
153194
HttpProxy.prototype.pause = function (obj) {
154195
var onData, onEnd, events = [];
155196

156-
// buffer data
157197
obj.on('data', onData = function (data, encoding) {
158198
events.push(['data', data, encoding]);
159199
});
160200

161-
// buffer end
162201
obj.on('end', onEnd = function (data, encoding) {
163202
events.push(['end', data, encoding]);
164203
});
@@ -177,8 +216,25 @@ HttpProxy.prototype.pause = function (obj) {
177216
};
178217
};
179218

219+
//
220+
// ### function close ()
221+
// Frees the resources associated with this instance,
222+
// if they exist.
223+
//
224+
HttpProxy.prototype.close = function () {
225+
if (this.proxyTable) this.proxyTable.close();
226+
};
227+
228+
//
229+
// ### function proxyRequest (req, res, [port, host, paused])
230+
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
231+
// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
232+
// #### @port {number} **Optional** Port to use on the proxy target host.
233+
// #### @host {string} **Optional** Host of the proxy target.
234+
// #### @paused {Object} **Optional** Result from `httpProxy.pause(req)`
235+
//
180236
HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
181-
var self = this, reverseProxy, location;
237+
var self = this, reverseProxy, location, errState = false;
182238

183239
//
184240
// 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) {
189245
if (this.proxyTable && !host) {
190246
location = this.proxyTable.getProxyLocation(req);
191247

248+
//
249+
// If no location is returned from the ProxyTable instance
250+
// then respond with `404` since we do not have a valid proxy target.
251+
//
192252
if (!location) {
193253
res.writeHead(404);
194254
return res.end();
@@ -198,36 +258,34 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
198258
// When using the ProxyTable in conjunction with an HttpProxy instance
199259
// only the following arguments are valid:
200260
//
201-
// * proxy.proxyRequest(req, res, port, host, paused): This will be skipped
202-
// * proxy.proxyRequest(req, res, paused): Paused will get updated appropriately
203-
// * proxy.proxyRequest(req, res): No effect `undefined = undefined`
261+
// * `proxy.proxyRequest(req, res, port, host, paused)`: This will be skipped
262+
// * `proxy.proxyRequest(req, res, paused)`: Paused will get updated appropriately
263+
// * `proxy.proxyRequest(req, res)`: No effect `undefined = undefined`
204264
//
205265
paused = port;
206266
port = location.port;
207267
host = location.host;
208268
}
209269

270+
//
271+
// If forwarding is enabled for this instance, foward proxy the
272+
// specified request to the address provided in `this.options.forward`
273+
//
210274
if (this.options.forward) {
211275
winston.verbose('Forwarding HTTP request to: ' + this.options.forward.host + ':' + this.options.forward.port);
212276
this._forwardRequest(req);
213277
}
214278

215-
// Create an error handler so we can use it temporarily
216-
function error (obj) {
217-
var fn = function (err) {
218-
res.writeHead(500, {'Content-Type': 'text/plain'});
279+
function proxyError(err) {
280+
errState = true;
281+
res.writeHead(500, { 'Content-Type': 'text/plain' });
219282

220-
if (req.method !== 'HEAD') {
221-
res.write('An error has occurred: ' + JSON.stringify(err));
222-
}
223-
224-
// Response end may never come so removeListener here
225-
obj.removeListener('error', fn);
226-
res.end();
227-
};
283+
if (req.method !== 'HEAD') {
284+
res.write('An error has occurred: ' + JSON.stringify(err));
285+
}
228286

229-
return fn;
230-
};
287+
res.end();
288+
}
231289

232290
// Open new HTTP request to internal resource with will act as a reverse proxy pass
233291
reverseProxy = http.request({
@@ -263,26 +321,31 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
263321

264322
// Add event listener for end of proxied response
265323
response.on('end', function () {
266-
reverseProxy.removeListener('error', reverseProxyError);
267-
res.end();
324+
if (!errState) {
325+
reverseProxy.removeListener('error', proxyError);
326+
res.end();
327+
}
268328
});
269329
});
270330

271331
// Add a listener for the connection timeout event
272-
var reverseProxyError = error(reverseProxy);
273-
reverseProxy.on('error', reverseProxyError);
332+
reverseProxy.once('error', proxyError);
274333

275334
// Chunk the client request body as chunks from the proxied request come in
276335
req.on('data', function (chunk) {
277-
reverseProxy.write(chunk);
336+
if (!errState) {
337+
reverseProxy.write(chunk);
338+
}
278339
});
279340

280341
// At the end of the client request, we are going to stop the proxied request
281342
req.on('end', function () {
282-
reverseProxy.end();
343+
if (!errState) {
344+
reverseProxy.end();
345+
}
283346
});
284347

285-
if (paused) {
348+
if (paused && !errState) {
286349
paused.resume();
287350
}
288351
};
@@ -308,11 +371,12 @@ HttpProxy.prototype._forwardRequest = function (req) {
308371
//
309372
});
310373

311-
// Add a listener for the connection timeout event
312-
forwardProxy.on('error', function (err) {
313-
// Remark: Ignoring this error in the event
314-
// forward target doesn't exist.
315-
});
374+
// Add a listener for the connection timeout event.
375+
//
376+
// Remark: Ignoring this error in the event
377+
// forward target doesn't exist.
378+
//
379+
forwardProxy.on('error', function (err) { });
316380

317381
// Chunk the client request body as chunks from the proxied request come in
318382
req.on('data', function (chunk) {

lib/proxy-table.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626

2727
var util = require('util'),
2828
events = require('events'),
29-
fs = require('fs');
29+
fs = require('fs'),
30+
winston = require('winston');
3031

3132
//
3233
// ### function ProxyTable (router, silent)
@@ -109,9 +110,7 @@ ProxyTable.prototype.getProxyLocation = function (req) {
109110
host = location[0],
110111
port = location.length === 1 ? 80 : location[1];
111112

112-
if (!this.silent) {
113-
util.log('Proxy Table proxying request to: ' + host + ':' + port);
114-
}
113+
winston.verbose('Proxy Table proxying request to: ' + host + ':' + port);
115114

116115
return {
117116
port: port,

0 commit comments

Comments
 (0)