Skip to content

Commit cfddd12

Browse files
committed
[api] Update .proxyRequest() and .proxyWebSocketRequest() APIs to take an options hash instead of a set of arguments. Add HTTPS support.
1 parent 6e4bf6a commit cfddd12

File tree

4 files changed

+141
-69
lines changed

4 files changed

+141
-69
lines changed

examples/latent-proxy.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ var util = require('util'),
3535
httpProxy.createServer(function (req, res, proxy) {
3636
var buffer = proxy.buffer(req);
3737
setTimeout(function() {
38-
proxy.proxyRequest(req, res, 9000, 'localhost', buffer);
38+
proxy.proxyRequest(req, res, {
39+
port: 9000,
40+
host: 'localhost',
41+
buffer: buffer
42+
});
3943
}, 200)
4044
}).listen(8002);
4145

examples/standalone-proxy.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ var proxy = new httpProxy.HttpProxy();
3636
http.createServer(function (req, res) {
3737
var buffer = proxy.buffer(req);
3838
setTimeout(function() {
39-
proxy.proxyRequest(req, res, 9000, 'localhost', buffer);
39+
proxy.proxyRequest(req, res, {
40+
port: 9000,
41+
host: 'localhost',
42+
buffer: buffer
43+
});
4044
}, 200);
4145
}).listen(8004);
4246

lib/node-http-proxy.js

+122-64
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
var util = require('util'),
2828
http = require('http'),
29+
https = require('https'),
2930
events = require('events'),
3031
ProxyTable = require('./proxy-table').ProxyTable,
3132
maxSockets = 100;
@@ -36,21 +37,46 @@ var util = require('util'),
3637
exports.version = [0, 4, 2];
3738

3839
//
39-
// ### function _getAgent (host, port)
40+
// ### function _getAgent (host, port, secure)
4041
// #### @host {string} Host of the agent to get
4142
// #### @port {number} Port of the agent to get
43+
// #### @secure {boolean} Value indicating whether or not to use HTTPS
4244
// Retreives an agent from the `http` module
4345
// and sets the `maxSockets` property appropriately.
4446
//
45-
function _getAgent (host, port) {
46-
//
47-
// TODO (indexzero): Make this configurable for http / https
48-
//
49-
var agent = http.getAgent(host, port);
47+
function _getAgent (host, port, secure) {
48+
var agent = !secure ? http.getAgent(host, port) : https.getAgent({
49+
host: host,
50+
port: port
51+
});
52+
5053
agent.maxSockets = maxSockets;
5154
return agent;
5255
}
5356

57+
//
58+
// ### function _getProtocol (outgoing, https)
59+
// #### @outgoing {Object} Outgoing request options
60+
// #### @secure {Object|boolean} Settings for `https`
61+
// Returns the appropriate protocol based on the settings in
62+
// `secure`. If the protocol is `https` this function will update
63+
// the options in `outgoing` as appropriate by adding `ca`, `key`,
64+
// and `cert` if they exist in `secure`.
65+
//
66+
function _getProtocol (outgoing, secure) {
67+
var protocol = secure ? https : http;
68+
69+
if (typeof secure === 'object') {
70+
['ca', 'cert', 'key'].forEach(function (prop) {
71+
if (secure[prop]) {
72+
outgoing[prop] = secure[prop];
73+
}
74+
})
75+
}
76+
77+
return protocol;
78+
}
79+
5480
//
5581
// ### function getMaxSockets ()
5682
// Returns the maximum number of sockets
@@ -104,20 +130,34 @@ exports.createServer = function () {
104130

105131
proxy = new HttpProxy(options);
106132
server = http.createServer(function (req, res) {
107-
proxy.emit('request', req, res, req.headers.host, req.url);
108-
109-
// If we were passed a callback to process the request
110-
// or response in some way, then call it.
111133
if (callback) {
134+
//
135+
// If we were passed a callback to process the request
136+
// or response in some way, then call it.
137+
//
112138
callback(req, res, proxy);
113139
}
114140
else if (port && host) {
115-
proxy.proxyRequest(req, res, port, host);
141+
//
142+
// If we have a target host and port for the request
143+
// then proxy to the specified location.
144+
//
145+
proxy.proxyRequest(req, res, {
146+
port: port,
147+
host: host
148+
});
116149
}
117150
else if (proxy.proxyTable) {
151+
//
152+
// If the proxy is configured with a ProxyTable
153+
// instance then use that before failing.
154+
//
118155
proxy.proxyRequest(req, res);
119156
}
120157
else {
158+
//
159+
// Otherwise this server is improperly configured.
160+
//
121161
throw new Error('Cannot proxy without port, host, or router.')
122162
}
123163
});
@@ -136,10 +176,19 @@ exports.createServer = function () {
136176
server.on('upgrade', function(req, socket, head) {
137177
// Tunnel websocket requests too
138178

139-
proxy.proxyWebSocketRequest(req, socket, head, port, host);
179+
proxy.proxyWebSocketRequest(req, socket, head, {
180+
port: port,
181+
host: host
182+
});
140183
});
141184
}
142-
185+
186+
//
187+
// Set the proxy on the server so it is available
188+
// to the consumer of the server
189+
//
190+
server.proxy = proxy;
191+
143192
return server;
144193
};
145194

@@ -165,11 +214,12 @@ exports.createServer = function () {
165214
var HttpProxy = exports.HttpProxy = function (options) {
166215
events.EventEmitter.call(this);
167216

168-
options = options || {};
169-
this.options = options;
217+
var self = this;
218+
options = options || {};
219+
this.forward = options.forward;
220+
this.https = options.https;
170221

171222
if (options.router) {
172-
var self = this;
173223
this.proxyTable = new ProxyTable(options.router, options.silent, options.hostnameOnly);
174224
this.proxyTable.on('routes', function (routes) {
175225
self.emit('routes', routes);
@@ -196,7 +246,7 @@ util.inherits(HttpProxy, events.EventEmitter);
196246
//
197247
// __Attribution:__ This approach is based heavily on
198248
// [Connect](https://github.com/senchalabs/connect/blob/master/lib/utils.js#L157).
199-
// However, this is not a big leap from the implementation in node-http-proxy < 0.4.0.
249+
// However, this is not a big leap from the implementation in node-http-proxy < 0.4.0.
200250
// This simply chooses to manage the scope of the events on a new Object literal as opposed to
201251
// [on the HttpProxy instance](https://github.com/nodejitsu/node-http-proxy/blob/v0.3.1/lib/node-http-proxy.js#L154).
202252
//
@@ -238,20 +288,25 @@ HttpProxy.prototype.close = function () {
238288
// ### function proxyRequest (req, res, [port, host, paused])
239289
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
240290
// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
241-
// #### @port {number} **Optional** Port to use on the proxy target host.
242-
// #### @host {string} **Optional** Host of the proxy target.
243-
// #### @buffer {Object} **Optional** Result from `httpProxy.buffer(req)`
291+
// #### @options {Object} Options for the outgoing proxy request.
292+
// options.port {number} Port to use on the proxy target host.
293+
// options.host {string} Host of the proxy target.
294+
// options.buffer {Object} Result from `httpProxy.buffer(req)`
295+
// options.https {Object|boolean} Settings for https.
244296
//
245-
HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
246-
var self = this, reverseProxy, location, errState = false, opts;
297+
HttpProxy.prototype.proxyRequest = function (req, res, options) {
298+
var self = this, errState = false, location, outgoing, protocol, reverseProxy;
299+
300+
// Create an empty options hash if none is passed.
301+
options = options || {};
247302

248303
//
249304
// Check the proxy table for this instance to see if we need
250305
// to get the proxy location for the request supplied. We will
251306
// always ignore the proxyTable if an explicit `port` and `host`
252307
// arguments are supplied to `proxyRequest`.
253308
//
254-
if (this.proxyTable && !host) {
309+
if (this.proxyTable && !options.host) {
255310
location = this.proxyTable.getProxyLocation(req);
256311

257312
//
@@ -267,13 +322,12 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
267322
// When using the ProxyTable in conjunction with an HttpProxy instance
268323
// only the following arguments are valid:
269324
//
270-
// * `proxy.proxyRequest(req, res, port, host, buffer)`: This will be skipped
271-
// * `proxy.proxyRequest(req, res, buffer)`: Buffer will get updated appropriately
272-
// * `proxy.proxyRequest(req, res)`: No effect `undefined = undefined`
325+
// * `proxy.proxyRequest(req, res, { host: 'localhost' })`: This will be skipped
326+
// * `proxy.proxyRequest(req, res, { buffer: buffer })`: Buffer will get updated appropriately
327+
// * `proxy.proxyRequest(req, res)`: Options will be assigned appropriately.
273328
//
274-
buffer = port;
275-
port = location.port;
276-
host = location.host;
329+
options.port = location.port;
330+
options.host = location.host;
277331
}
278332

279333
//
@@ -284,14 +338,14 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
284338
//
285339
// Emit the `start` event indicating that we have begun the proxy operation.
286340
//
287-
this.emit('start', req, res, host, port);
341+
this.emit('start', req, res, options);
288342

289343
//
290344
// If forwarding is enabled for this instance, foward proxy the
291-
// specified request to the address provided in `this.options.forward`
345+
// specified request to the address provided in `this.forward`
292346
//
293-
if (this.options.forward) {
294-
this.emit('forward', req, res, this.options.forward.host, this.options.forward.port);
347+
if (this.forward) {
348+
this.emit('forward', req, res, this.forward);
295349
this._forwardRequest(req);
296350
}
297351

@@ -312,21 +366,23 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
312366
res.end();
313367
}
314368

315-
var opts = {
316-
host: host,
317-
port: port,
318-
agent: _getAgent(host, port),
369+
outgoing = {
370+
host: options.host,
371+
port: options.port,
372+
agent: _getAgent(options.host, options.port, options.https || this.https),
319373
method: req.method,
320374
path: req.url,
321375
headers: req.headers
322376
};
323377

324378
// Force the `connection` header to be 'close' until
325379
// node.js core re-implements 'keep-alive'.
326-
opts.headers['connection'] = 'close';
380+
outgoing.headers['connection'] = 'close';
381+
382+
protocol = _getProtocol(outgoing, options.https || this.https);
327383

328384
// Open new HTTP request to internal resource with will act as a reverse proxy pass
329-
reverseProxy = http.request(opts, function (response) {
385+
reverseProxy = protocol.request(outgoing, function (response) {
330386

331387
// Process the `reverseProxy` `response` when it's received.
332388
if (response.headers.connection) {
@@ -388,38 +444,40 @@ HttpProxy.prototype.proxyRequest = function (req, res, port, host, buffer) {
388444
});
389445

390446
// If we have been passed buffered data, resume it.
391-
if (buffer && !errState) {
392-
buffer.resume();
447+
if (options.buffer && !errState) {
448+
options.buffer.resume();
393449
}
394450
};
395451

396452
//
397453
// ### @private function _forwardRequest (req)
398454
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
399455
// Forwards the specified `req` to the location specified
400-
// by `this.options.forward` ignoring errors and the subsequent response.
456+
// by `this.forward` ignoring errors and the subsequent response.
401457
//
402458
HttpProxy.prototype._forwardRequest = function (req) {
403-
var self = this, port, host, forwardProxy, opts;
459+
var self = this, port, host, outgoing, protocol, forwardProxy;
404460

405-
port = this.options.forward.port;
406-
host = this.options.forward.host;
461+
port = this.forward.port;
462+
host = this.forward.host;
407463

408-
opts = {
464+
outgoing = {
409465
host: host,
410466
port: port,
411-
agent: _getAgent(host, port),
467+
agent: _getAgent(host, port, this.forward.https),
412468
method: req.method,
413469
path: req.url,
414470
headers: req.headers
415471
};
416472

417473
// Force the `connection` header to be 'close' until
418474
// node.js core re-implements 'keep-alive'.
419-
opts.headers['connection'] = 'close';
475+
outgoing.headers['connection'] = 'close';
476+
477+
protocol = _getProtocol(outgoing, this.forward.https);
420478

421479
// Open new HTTP request to internal resource with will act as a reverse proxy pass
422-
forwardProxy = http.request(opts, function (response) {
480+
forwardProxy = protocol.request(outgoing, function (response) {
423481
//
424482
// Ignore the response from the forward proxy since this is a 'fire-and-forget' proxy.
425483
// Remark (indexzero): We will eventually emit a 'forward' event here for performance tuning.
@@ -444,8 +502,8 @@ HttpProxy.prototype._forwardRequest = function (req) {
444502
});
445503
};
446504

447-
HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, port, host, buffer) {
448-
var self = this, CRLF = '\r\n';
505+
HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options) {
506+
var self = this, outgoing, errState = false, CRLF = '\r\n';
449507

450508
// WebSocket requests has method = GET
451509
if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') {
@@ -519,24 +577,24 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, port, h
519577
_socket(socket);
520578

521579
// Remote host address
522-
var remoteHost = host + (port - 80 === 0 ? '' : ':' + port),
523-
agent = _getAgent(host, port);
580+
var agent = _getAgent(options.host, options.port),
581+
remoteHost = options.host + (options.port - 80 === 0 ? '' : ':' + options.port);
524582

525583
// Change headers
526-
req.headers.host = remoteHost;
527-
req.headers.origin = 'http://' + host;
584+
req.headers.host = remoteHost;
585+
req.headers.origin = 'http://' + options.host;
528586

529-
var opts = {
530-
host: host,
531-
port: port,
587+
outgoing = {
588+
host: options.host,
589+
port: options.port,
532590
agent: agent,
533591
method: 'GET',
534592
path: req.url,
535593
headers: req.headers
536-
}
594+
};
537595

538596
// Make the outgoing WebSocket request
539-
var request = http.request(opts, function () { });
597+
var request = http.request(outgoing, function () { });
540598

541599
// Not disconnect on update
542600
agent.on('upgrade', function(request, remoteSocket, head) {
@@ -567,8 +625,8 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, port, h
567625
data = data.slice(Buffer.byteLength(sdata), data.length);
568626

569627
// Replace host and origin
570-
sdata = sdata.replace(remoteHost, host)
571-
.replace(remoteHost, host);
628+
sdata = sdata.replace(remoteHost, options.host)
629+
.replace(remoteHost, options.host);
572630

573631
try {
574632
// Write printable
@@ -602,7 +660,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, port, h
602660
}
603661

604662
// If we have been passed buffered data, resume it.
605-
if (buffer && !errState) {
606-
buffer.resume();
663+
if (options.buffer && !errState) {
664+
options.buffer.resume();
607665
}
608666
};

0 commit comments

Comments
 (0)