Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[api] merge middleware branch

  • Loading branch information...
commit e6ff8d6597a977baf0caf4f69c75bfa93d7281f3 2 parents 5ba0f89 + 0f8fe8e
@dominictarr dominictarr authored
View
11 README.md
@@ -43,6 +43,7 @@ There are several ways to use node-http-proxy; the library is designed to be fle
3. In conjunction with a Proxy Routing Table
4. As a forward-proxy with a reverse proxy
5. From the command-line as a long running process
+6. customized with 3rd party middleware.
In each of these scenarios node-http-proxy can handle any of these types of requests:
@@ -312,6 +313,16 @@ https.createServer(options.https, function (req, res) {
res.end();
}).listen(8000);
```
+## Middleware
+
+`node-http-proxy` now supports connect middleware. Add middleware functions to your createServer call:
+
+``` js
+httpProxy.createServer(
+ require('connect-gzip').gzip(),
+ 9000, 'localhost'
+).listen(8000);
+```
## Proxying WebSockets
Websockets are handled automatically when using the `httpProxy.createServer()`, but if you want to use it in conjunction with a stand-alone HTTP + WebSocket (such as [socket.io][5]) server here's how:
View
72 examples/body-decoder.js
@@ -0,0 +1,72 @@
+/*
+ body-decoder.js: Example of body-decoder middleware with node-http-proxy
+
+ Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+var httpProxy = require('http-proxy'),
+ http = require('http'),
+ util = require('util'),
+ colors = require('colors');
+
+exports.bodyMod = function () {
+ console.log('middleware has been started.'.green);
+ return function (req, res, next) {
+ var proxy = next,
+ total = '';
+
+ req.on('data', function (data) {
+ console.log('ON DATA')
+ total += data;
+ });
+
+ req.on('end', function () {
+ console.log('ON END')
+ console.log(total);
+ //
+ // This line, uncommented, hangs forever.
+ // proxy.proxyRequest(req, res, { port: 9000, host: 'localhost' });
+ // The following also hangs forever.
+ // next.proxyRequest(req, res, { port: 9000, host: 'localhost' });
+ //
+ });
+
+ //
+ // The following fires just fine.
+ //proxy.proxyRequest(req, res, { port: 9000, host: 'localhost' });
+ //
+ console.log('request proxied...'.blue);
+ }
+}
+
+var proxyServer = httpProxy.createServer(
+ // Your middleware stack goes here.
+ exports.bodyMod()
+).listen(8000);
+
+
+var httpServer = http.createServer(function (req, res) {
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
+ res.end();
+}).listen(9000);
View
87 examples/bodyDecoder-middleware.js
@@ -0,0 +1,87 @@
+
+var Store = require('./lib/store')
+ , http = require('http')
+
+http.createServer(new Store().handler()).listen(7531, function () {
+//try these commands:
+// get index:
+// curl localhost:7531
+// []
+//
+// get a doc:
+// curl localhost:7531/foo
+// {"error":"not_found"}
+//
+// post an doc:
+// curl -X POST localhost:7531/foo -d '{"content": "hello", "type": "greeting"}'
+// {"ok":true}
+//
+// get index (now, not empty)
+// curl localhost:7531
+// ["/foo"]
+//
+// get doc
+// curl localhost:7531/foo
+// {"content": "hello", "type": "greeting"}
+
+//
+// now, suppose we wanted to direct all objects where type == "greeting" to a different store
+// than where type == "insult"
+//
+// we can use connect connect-bodyDecoder and some custom logic to send insults to another Store.
+
+//insult server:
+
+ http.createServer(new Store().handler()).listen(2600, function () {
+
+ //greetings -> 7531, insults-> 2600
+
+ // now, start a proxy server.
+
+ var bodyParser = require('connect/lib/middleware/bodyParser')
+ //don't worry about incoming contont type
+ //bodyParser.parse[''] = JSON.parse
+
+ require('http-proxy').createServer(
+ //refactor the body parser and re-streamer into a separate package
+ bodyParser(),
+ //body parser absorbs the data and end events before passing control to the next
+ // middleware. if we want to proxy it, we'll need to re-emit these events after
+ //passing control to the middleware.
+ require('connect-restreamer')(),
+ function (req, res, proxy) {
+ //if your posting an obect which contains type: "insult"
+ //it will get redirected to port 2600.
+ //normal get requests will go to 7531 nad will not return insults.
+ var port = (req.body && req.body.type === 'insult' ? 2600 : 7531)
+ proxy.proxyRequest(req, res, {host: 'localhost', port: port})
+ }
+ ).listen(1337, function () {
+ var request = require('request')
+ //bodyParser needs content-type set to application/json
+ //if we use request, it will set automatically if we use the 'json:' field.
+ function post (greeting, type) {
+ request.post({
+ url: 'http://localhost:1337/' + greeting,
+ json: {content: greeting, type: type || "greeting"}
+ })
+ }
+ post("hello")
+ post("g'day")
+ post("kiora")
+ post("houdy")
+ post("java", "insult")
+
+ //now, the insult should have been proxied to 2600
+
+ //curl localhost:2600
+ //["/java"]
+
+ //but the greetings will be sent to 7531
+
+ //curl localhost:7531
+ //["/hello","/g%27day","/kiora","/houdy"]
+
+ })
+ })
+})
View
65 examples/concurrent-proxy.js
@@ -0,0 +1,65 @@
+/*
+ concurrent-proxy.js: check levelof concurrency through proxy.
+
+ Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+var util = require('util'),
+ colors = require('colors'),
+ http = require('http'),
+ httpProxy = require('./../lib/node-http-proxy');
+
+//
+// Basic Http Proxy Server
+//
+httpProxy.createServer(9000, 'localhost').listen(8000);
+
+//
+// Target Http Server
+//
+// to check apparent problems with concurrent connections
+// make a server which only responds when there is a given nubmer on connections
+//
+
+
+var connections = []
+ , go
+
+http.createServer(function (req, res) {
+
+ connections.push (function (){
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
+ res.end();
+ })
+ process.stdout.write(connections.length + ', ')
+ if (connections.length > 110 || go) {
+ go = true
+ while(connections.length)
+ connections.shift()()
+ }
+
+}).listen(9000);
+
+util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
+util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
View
50 examples/gzip-middleware.js
@@ -0,0 +1,50 @@
+/*
+ gzip-middleware.js: Basic example of `connect-gzip` middleware in node-http-proxy
+
+ Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, Marak Squires, & Dominic Tarr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+var util = require('util'),
+ colors = require('colors'),
+ http = require('http'),
+ httpProxy = require('./../lib/node-http-proxy');
+
+//
+// Basic Http Proxy Server
+//
+httpProxy.createServer(
+ require('connect-gzip').gzip({ matchType: /.?/ }),
+ 9000, 'localhost'
+).listen(8000);
+
+//
+// Target Http Server
+//
+http.createServer(function (req, res) {
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
+ res.end();
+}).listen(9000);
+
+util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
+util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
View
30 examples/jsonp-middleware.js
@@ -0,0 +1,30 @@
+var Store = require('./lib/store')
+ , http = require('http')
+
+//
+// jsonp is a handy technique for getting around the limitations of the same-origin policy.
+// (http://en.wikipedia.org/wiki/Same_origin_policy)
+//
+// normally, to dynamically update a page you use an XmlHttpRequest. this has flakey support
+// is some browsers and is restricted by the same origin policy. you cannot perform XHR requests to
+// someone else's server. one way around this would be to proxy requests to all the servers you want
+// to xhr to, and your core server - so that everything has the same port and host.
+//
+// another way, is to turn json into javascript. (which is exempt from the same origin policy)
+// this is done by wrapping the json object in a function call, and then including a script tag.
+//
+// here we're proxing our own JSON returning server, but we could proxy any server on the internet,
+// and our client side app would be slurping down JSONP from anywhere.
+//
+// curl localhost:1337/whatever?callback=alert
+// alert([]) //which is valid javascript!
+//
+// also see http://en.wikipedia.org/wiki/JSONP#JSONP
+//
+
+http.createServer(new Store().handler()).listen(7531)
+
+require('http-proxy').createServer(
+ require('connect-jsonp')(true),
+ 'localhost', 7531
+).listen(1337)
View
67 examples/lib/store.js
@@ -0,0 +1,67 @@
+
+module.exports = Store
+//
+// just to make these example a little bit interesting,
+// make a little key value store with an http interface
+// (see couchbd for a grown-up version of this)
+//
+// API:
+// GET /
+// retrive list of keys
+//
+// GET /[url]
+// retrive object stored at [url]
+// will respond with 404 if there is nothing stored at [url]
+//
+// POST /[url]
+//
+// JSON.parse the body and store it under [url]
+// will respond 400 (bad request) if body is not valid json.
+//
+// TODO: cached map-reduce views and auto-magic sharding.
+//
+
+
+
+function Store () {
+ this.store = {}
+}
+Store.prototype = {
+ get: function (key) {
+ return this.store[key]
+ },
+ set: function (key, value) {
+ return this.store[key] = value
+ },
+ handler:function () {
+ var store = this
+ return function (req, res) {
+ function send (obj, status) {
+ res.writeHead(200 || status,{'Content-Type': 'application/json'})
+ res.write(JSON.stringify(obj) + '\n')
+ res.end()
+ }
+ var url = req.url.split('?').shift()
+ if(url === '/') {
+ console.log('get index')
+ return send(Object.keys(store.store))
+ } else if(req.method == 'GET') {
+ var obj = store.get (url)
+ send(obj || {error: 'not_found', url: url}, obj ? 200 : 404)
+ } else {
+ //post: buffer body, and parse.
+ var body = '', obj
+ req.on('data', function (c) { body += c})
+ req.on('end', function (c) {
+ try {
+ obj = JSON.parse(body)
+ } catch (err) {
+ return send (err, 400)
+ }
+ store.set(url, obj)
+ send({ok: true})
+ })
+ }
+ }
+ }
+}
View
12 examples/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "http-proxy-examples"
+, "description": "packages required to run the examples"
+, "version": "0.0.0"
+, "dependencies": {
+ "connect": "1.6"
+ , "connect-gzip": "0.1"
+ , "connect-jsonp": "0.0.5"
+ , "connect-restreamer": "1"
+ , "proxy-by-url": "0.0.0"
+ }
+}
View
58 examples/url-middleware.js
@@ -0,0 +1,58 @@
+/*
+ url-middleware.js: Example of a simple url routing middleware for node-http-proxy
+
+ Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+var util = require('util'),
+ colors = require('colors'),
+ http = require('http'),
+ httpProxy = require('http-proxy');
+
+//
+// Now we set up our proxy.
+//
+httpProxy.createServer(
+ //
+ // This is where our middlewares go, with any options desired - in this case,
+ // the list of routes/URLs and their destinations.
+ //
+ require('proxy-by-url')({
+ '/hello': { port: 9000, host: 'localhost' },
+ '/charlie': { port: 80, host: 'charlieistheman.com' },
+ '/google': { port: 80, host: 'google.com' }
+ });
+).listen(8000);
+
+//
+// Target Http Server (to listen for requests on 'localhost')
+//
+http.createServer(function (req, res) {
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
+ res.end();
+}).listen(9000);
+
+// And finally, some colored startup output.
+util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
+util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
View
31 examples/url-middleware2.js
@@ -0,0 +1,31 @@
+var util = require('util'),
+ colors = require('colors'),
+ http = require('http'),
+ httpProxy = require('http-proxy'),
+ Store = require('./lib/store')
+
+http.createServer(new Store().handler()).listen(7531)
+
+// Now we set up our proxy.
+httpProxy.createServer(
+ // This is where our middlewares go, with any options desired - in this case,
+ // the list of routes/URLs and their destinations.
+ require('proxy-by-url')({
+ '/store': { port: 7531, host: 'localhost' },
+ '/': { port: 9000, host: 'localhost' }
+ })
+).listen(8000);
+
+//
+// Target Http Server (to listen for requests on 'localhost')
+//
+http.createServer(
+ function (req, res) {
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
+ res.end();
+ }).listen(9000);
+
+// And finally, some colored startup output.
+util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
+util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
View
1  examples/web-socket-proxy.js
@@ -47,6 +47,7 @@ var server = http.createServer(function (req, res) {
res.writeHead(200);
res.end();
});
+
server.listen(8080);
//
View
328 lib/node-http-proxy.js
@@ -1,7 +1,7 @@
/*
node-http-proxy.js: http proxy for node.js
- Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Marak Squires, Fedor Indutny
+ Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Marak Squires, Fedor Indutny
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@@ -51,16 +51,16 @@ var _agents = {};
//
function _getAgent (host, port, secure) {
var Agent, id = [host, port].join(':');
-
+
if (!port) {
port = secure ? 443 : 80;
}
-
+
if (!_agents[id]) {
Agent = secure ? https.Agent : http.Agent;
- _agents[id] = new Agent({
- host: host,
+ _agents[id] = new Agent({
+ host: host,
port: port,
maxSockets: maxSockets
});
@@ -70,17 +70,17 @@ function _getAgent (host, port, secure) {
}
//
-// ### function _getProtocol (secure, outgoing)
+// ### function _getProtocol (secure, outgoing)
// #### @secure {Object|boolean} Settings for `https`
// #### @outgoing {Object} Outgoing request options
-// Returns the appropriate protocol based on the settings in
+// Returns the appropriate protocol based on the settings in
// `secure`. If the protocol is `https` this function will update
// the options in `outgoing` as appropriate by adding `ca`, `key`,
// and `cert` if they exist in `secure`.
//
function _getProtocol (secure, outgoing) {
var protocol = secure ? https : http;
-
+
if (typeof secure === 'object') {
outgoing = outgoing || {};
['ca', 'cert', 'key'].forEach(function (prop) {
@@ -89,7 +89,7 @@ function _getProtocol (secure, outgoing) {
}
})
}
-
+
return protocol;
}
@@ -138,6 +138,34 @@ function stack (middlewares, proxy) {
}
//
+// stack
+// adapted from https://github.com/creationix/stack
+//
+function stack (middlewares, proxy) {
+ var handle;
+ middlewares.reverse().forEach(function (layer) {
+ var child = handle;
+ handle = function (req, res) {
+ var next = function (err) {
+ if (err) {
+ throw err;
+ //
+ // TODO: figure out where to send errors.
+ // return error(req, res, err);
+ //
+ }
+ child(req, res);
+ }
+
+ next.__proto__ = proxy;
+ layer(req, res, next);
+ };
+ });
+
+ return handle;
+}
+
+//
// ### function createServer ([port, host, options, handler])
// #### @port {number} **Optional** Port to use on the proxy target host.
// #### @host {string} **Optional** Host of the proxy target.
@@ -150,87 +178,101 @@ function stack (middlewares, proxy) {
// * `httpPRoxy.createServer(function (req, res, proxy) { ... })`
//
exports.createServer = function () {
- var args = Array.prototype.slice.call(arguments),
- callback,
- options = {}, port, host, forward, silent, proxy, server, middleware = [];
-
+ var args = Array.prototype.slice.call(arguments),
+ callback, forward,
+ port, host,
+ proxy, server,
+ options = {},
+ middleware = [],
+ silent;
+
args.forEach(function (arg) {
-
switch (typeof arg) {
case 'string': host = arg; break;
case 'number': port = arg; break;
- case 'function': middleware.push(arg); break;
+ case 'function': middleware.push(handler = callback = arg); break;
case 'object': options = arg; break;
};
-
});
var proxy = new HttpProxy(options);
- if(middleware.length)
- //handler = callback = middleware.shift()
- //else if (middleware.length)
- handler = callback = stack(middleware, proxy);
+ if (port && host) {
+ //
+ // If we have a target host and port for the request
+ // then proxy to the specified location.
+ //
+ handler = function (req, res) {
+ proxy.proxyRequest(req, res, {
+ port: port,
+ host: host
+ });
+ }
- if (port && host) {
- //
- // If we have a target host and port for the request
- // then proxy to the specified location.
- //
- handler = function (req, res) {
- proxy.proxyRequest(req, res, {
- port: port,
- host: host
- });
- }
+ if (middleware.length) {
+ middleware.push(handler);
}
- else if (proxy.proxyTable) {
- //
- // If the proxy is configured with a ProxyTable
- // instance then use that before failing.
- //
- handler = function (req, res) {
- proxy.proxyRequest(req, res);
- }
+ }
+ else if (proxy.proxyTable) {
+ //
+ // If the proxy is configured with a ProxyTable
+ // instance then use that before failing.
+ //
+ handler = function (req, res) {
+ proxy.proxyRequest(req, res);
}
- else if (!handler) {
- //
- // Otherwise this server is improperly configured.
- //
- throw new Error('Cannot proxy without port, host, or router.')
+
+ if (middleware.length) {
+ middleware.push(handler);
}
+ }
+
+ if (middleware.length > 1) {
+ handler = callback = stack(middleware, proxy);
+ }
+ else if (middleware.length) {
+ //do not use middleware code if it's not needed.
+ var h = middleware[0];
+ handler = callback = function (req,res) { h(req,res,proxy) };
+ }
- server = options.https
+ if (!handler) {
+ //
+ // Otherwise this server is improperly configured.
+ //
+ throw new Error('Cannot proxy without port, host, or router.')
+ }
+
+ 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);
});
if (!callback) {
- // WebSocket support: if callback is empty tunnel
+ // WebSocket support: if callback is empty tunnel
// websocket request automatically
server.on('upgrade', function (req, socket, head) {
// Tunnel websocket requests too
-
proxy.proxyWebSocketRequest(req, socket, head, {
port: port,
host: host
});
});
}
-
+
//
// Set the proxy on the server so it is available
// to the consumer of the server
//
server.proxy = proxy;
-
+
return server;
};
@@ -251,23 +293,23 @@ exports.createServer = function () {
// host: 'localhost',
// port: 9001
// }
-// }
+// }
//
var HttpProxy = exports.HttpProxy = function (options) {
events.EventEmitter.call(this);
-
+
var self = this;
options = options || {};
-
+
//
// Setup basic proxying options
//
this.https = options.https;
this.forward = options.forward;
this.target = options.target || {};
-
+
//
- // Setup additional options for WebSocket proxying. When forcing
+ // Setup additional options for WebSocket proxying. When forcing
// the WebSocket handshake to change the `sec-websocket-location`
// and `sec-websocket-origin` headers `options.source` **MUST**
// be provided or the operation will fail with an `origin mismatch`
@@ -275,7 +317,7 @@ var HttpProxy = exports.HttpProxy = function (options) {
//
this.source = options.source || { host: 'localhost', port: 8000 };
this.changeOrigin = options.changeOrigin || false;
-
+
if (options.router) {
this.proxyTable = new ProxyTable(options.router, options.silent, options.hostnameOnly);
this.proxyTable.on('routes', function (routes) {
@@ -288,10 +330,10 @@ var HttpProxy = exports.HttpProxy = function (options) {
util.inherits(HttpProxy, events.EventEmitter);
//
-// ### function buffer (obj)
+// ### function buffer (obj)
// #### @obj {Object} Object to pause events from
// Buffer `data` and `end` events from the given `obj`.
-// Consumers of HttpProxy performing async tasks
+// 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.__
@@ -301,9 +343,9 @@ util.inherits(HttpProxy, events.EventEmitter);
// httpProxy.proxyRequest(req, res, host, port, buffer);
// });
//
-// __Attribution:__ This approach is based heavily on
+// __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.
+// 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).
//
@@ -335,10 +377,12 @@ HttpProxy.prototype.buffer = function (obj) {
//
// ### function close ()
// Frees the resources associated with this instance,
-// if they exist.
+// if they exist.
//
HttpProxy.prototype.close = function () {
- if (this.proxyTable) this.proxyTable.close();
+ if (this.proxyTable) {
+ this.proxyTable.close();
+ }
};
//
@@ -355,18 +399,18 @@ HttpProxy.prototype.close = function () {
//
HttpProxy.prototype.proxyRequest = function (req, res, options) {
var self = this, errState = false, location, outgoing, protocol, reverseProxy;
-
+
//
// Create an empty options hash if none is passed.
- // If default options have been passed to the constructor
+ // If default options have been passed to the constructor
// of this instance, use them by default.
//
options = options || {};
options.host = options.host || this.target.host;
options.port = options.port || this.target.port;
- options.enableXForwarded =
+ options.enableXForwarded =
(undefined === options.enableXForwarded ? true : options.enableXForwarded);
-
+
//
// Check the proxy table for this instance to see if we need
// to get the proxy location for the request supplied. We will
@@ -375,7 +419,7 @@ HttpProxy.prototype.proxyRequest = function (req, res, options) {
//
if (this.proxyTable && !options.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.
@@ -384,38 +428,38 @@ HttpProxy.prototype.proxyRequest = function (req, res, options) {
res.writeHead(404);
return res.end();
}
-
+
//
// When using the ProxyTable in conjunction with an HttpProxy instance
// only the following arguments are valid:
- //
+ //
// * `proxy.proxyRequest(req, res, { host: 'localhost' })`: This will be skipped
// * `proxy.proxyRequest(req, res, { buffer: buffer })`: Buffer will get updated appropriately
- // * `proxy.proxyRequest(req, res)`: Options will be assigned appropriately.
+ // * `proxy.proxyRequest(req, res)`: Options will be assigned appropriately.
//
options.port = location.port;
options.host = location.host;
}
-
+
//
- // Add common proxy headers to the request so that they can
+ // Add common proxy headers to the request so that they can
// be availible to the proxy target server:
- //
+ //
// * `x-forwarded-for`: IP Address of the original request
// * `x-forwarded-proto`: Protocol of the original request
- // * `x-forwarded-port`: Port of the original request.
+ // * `x-forwarded-port`: Port of the original request.
//
- if (options.enableXForwarded == true) {
+ if (options.enableXForwarded === true) {
req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.connection.socket.remoteAddress;
req.headers['x-forwarded-port'] = req.connection.remotePort || req.connection.socket.remotePort;
req.headers['x-forwarded-proto'] = res.connection.pair ? 'https' : 'http';
}
-
+
//
// Emit the `start` event indicating that we have begun the proxy operation.
//
this.emit('start', req, res, options);
-
+
//
// If forwarding is enabled for this instance, foward proxy the
// specified request to the address provided in `this.forward`
@@ -424,16 +468,16 @@ HttpProxy.prototype.proxyRequest = function (req, res, options) {
this.emit('forward', req, res, this.forward);
this._forwardRequest(req);
}
-
+
//
// #### function proxyError (err)
// #### @err {Error} Error contacting the proxy target
- // Short-circuits `res` in the event of any error when
+ // Short-circuits `res` in the event of any error when
// contacting the proxy target at `host` / `port`.
//
function proxyError(err) {
errState = true;
-
+
//
// Emit an `error` event, allowing the application to use custom
// error handling. The error handler should end the response.
@@ -456,10 +500,10 @@ HttpProxy.prototype.proxyRequest = function (req, res, options) {
res.write('An error has occurred: ' + JSON.stringify(err));
}
}
-
+
res.end();
}
-
+
outgoing = {
host: options.host,
port: options.port,
@@ -468,12 +512,12 @@ HttpProxy.prototype.proxyRequest = function (req, res, options) {
path: req.url,
headers: req.headers
};
-
+
protocol = _getProtocol(options.https || this.target.https, outgoing);
-
+
// Open new HTTP request to internal resource with will act as a reverse proxy pass
reverseProxy = protocol.request(outgoing, function (response) {
-
+
// Process the `reverseProxy` `response` when it's received.
if (response.headers.connection) {
if (req.headers.connection) response.headers.connection = req.headers.connection;
@@ -505,17 +549,17 @@ HttpProxy.prototype.proxyRequest = function (req, res, options) {
if (!errState) {
reverseProxy.removeListener('error', proxyError);
res.end();
-
+
// Emit the `end` event now that we have completed proxying
self.emit('end', req, res);
}
});
});
-
+
// Handle 'error' events from the `reverseProxy`.
reverseProxy.once('error', proxyError);
-
- // For each data `chunk` received from the incoming
+
+ // For each data `chunk` received from the incoming
// `req` write it to the `reverseProxy` request.
req.on('data', function (chunk) {
if (!errState) {
@@ -524,8 +568,8 @@ HttpProxy.prototype.proxyRequest = function (req, res, options) {
});
//
- // When the incoming `req` ends, end the corresponding `reverseProxy`
- // request unless we have entered an error state.
+ // When the incoming `req` ends, end the corresponding `reverseProxy`
+ // request unless we have entered an error state.
//
req.on('end', function () {
if (!errState) {
@@ -538,7 +582,7 @@ HttpProxy.prototype.proxyRequest = function (req, res, options) {
options.buffer.resume();
}
};
-
+
//
// ### @private function _forwardRequest (req)
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
@@ -550,7 +594,7 @@ HttpProxy.prototype._forwardRequest = function (req) {
port = this.forward.port;
host = this.forward.host;
-
+
outgoing = {
host: host,
port: port,
@@ -559,13 +603,13 @@ HttpProxy.prototype._forwardRequest = function (req) {
path: req.url,
headers: req.headers
};
-
+
// Force the `connection` header to be 'close' until
// node.js core re-implements 'keep-alive'.
outgoing.headers['connection'] = 'close';
-
+
protocol = _getProtocol(this.forward.https, outgoing);
-
+
// Open new HTTP request to internal resource with will act as a reverse proxy pass
forwardProxy = protocol.request(outgoing, function (response) {
//
@@ -573,10 +617,10 @@ HttpProxy.prototype._forwardRequest = function (req) {
// Remark (indexzero): We will eventually emit a 'forward' event here for performance tuning.
//
});
-
+
// Add a listener for the connection timeout event.
//
- // Remark: Ignoring this error in the event
+ // Remark: Ignoring this error in the event
// forward target doesn't exist.
//
forwardProxy.once('error', function (err) { });
@@ -594,7 +638,7 @@ HttpProxy.prototype._forwardRequest = function (req) {
//
// ### function proxyWebSocketRequest (req, socket, head, options)
-// #### @req {ServerRequest} Websocket request to proxy.
+// #### @req {ServerRequest} Websocket request to proxy.
// #### @socket {net.Socket} Socket for the underlying HTTP request
// #### @head {string} Headers for the Websocket request.
// #### @options {Object} Options to use when proxying this request.
@@ -605,28 +649,29 @@ HttpProxy.prototype._forwardRequest = function (req) {
// options.https {Object|boolean} Settings for https.
//
HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options) {
- var self = this,
+ var self = this,
listeners = {},
- errState = false,
+ errState = false,
CRLF = '\r\n',
outgoing;
options = options || {};
options.host = options.host || this.target.host;
options.port = options.port || this.target.port;
-
+
if (this.proxyTable && !options.host) {
location = this.proxyTable.getProxyLocation(req);
-
+
if (!location) {
- res.writeHead(404);
- return res.end();
+ return socket.destroy();
}
+
options.port = location.port;
options.host = location.host;
}
+
//
- // WebSocket requests must have the `GET` method and
+ // WebSocket requests must have the `GET` method and
// the `upgrade:websocket` header
//
if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') {
@@ -635,7 +680,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
//
return;
}
-
+
//
// Helper function for setting appropriate socket values:
// 1. Turn of all bufferings
@@ -652,14 +697,14 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
else if (socket.pair.cleartext.socket.setKeepAlive) {
socket.pair.cleartext.socket.setKeepAlive(true, 0);
}
- }
+ }
else {
socket.setEncoding('utf8');
}
}
-
+
//
- // On `upgrade` from the Agent socket, listen to
+ // On `upgrade` from the Agent socket, listen to
// the appropriate events.
//
function onUpgrade (reverseProxy, proxySocket) {
@@ -668,7 +713,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
socket.end();
return;
}
-
+
//
// Any incoming data on this WebSocket to the proxy target
// will be written to the `reverseProxy` socket.
@@ -678,7 +723,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
try {
self.emit('websocket:outgoing', req, socket, head, data);
reverseProxy.incoming.socket.write(data);
- }
+ }
catch (e) {
reverseProxy.incoming.socket.end();
proxySocket.end();
@@ -694,15 +739,15 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
try {
self.emit('websocket:incoming', reverseProxy, reverseProxy.incoming, head, data);
proxySocket.write(data);
- }
+ }
catch (e) {
proxySocket.end();
socket.end();
}
});
-
+
//
- // Helper function to detach all event listeners
+ // Helper function to detach all event listeners
// from `reverseProxy` and `proxySocket`.
//
function detach() {
@@ -713,19 +758,19 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
}
//
- // If the incoming `proxySocket` socket closes, then
- // detach all event listeners.
+ // If the incoming `proxySocket` socket closes, then
+ // detach all event listeners.
//
proxySocket.on('end', listeners.onIncomingClose = function() {
reverseProxy.incoming.socket.end();
detach();
-
+
// Emit the `end` event now that we have completed proxying
self.emit('websocket:end', req, socket, head);
});
//
- // If the `reverseProxy` socket closes, then detach all
+ // If the `reverseProxy` socket closes, then detach all
// event listeners.
//
reverseProxy.incoming.socket.on('end', listeners.onOutgoingClose = function() {
@@ -736,12 +781,12 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
// Setup the incoming client socket.
_socket(socket);
-
+
function getPort (port) {
port = port || 80;
return port - 80 === 0 ? '' : ':' + port
}
-
+
//
// Get the protocol, and host for this request and create an instance
// of `http.Agent` or `https.Agent` from the pool managed by `node-http-proxy`.
@@ -756,7 +801,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
req.headers.host = remoteHost;
req.headers.origin = protocolName + '://' + remoteHost;
}
-
+
//
// Make the outgoing WebSocket request
//
@@ -767,10 +812,11 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
path: req.url,
headers: req.headers,
};
+
var reverseProxy = agent.appendMessage(outgoing);
//
- // On any errors from the `reverseProxy` emit the
+ // On any errors from the `reverseProxy` emit the
// `webSocketProxyError` and close the appropriate
// connections.
//
@@ -779,14 +825,14 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
if (self.emit('webSocketProxyError', req, socket, head)) {
return;
}
-
+
socket.end();
}
//
// Here we set the incoming `req`, `socket` and `head` data to the outgoing
// request so that we can reuse this data later on in the closure scope
- // available to the `upgrade` event. This bookkeeping is not tracked anywhere
+ // available to the `upgrade` event. This bookkeeping is not tracked anywhere
// in nodejs core and is **very** specific to proxying WebSockets.
//
reverseProxy.agent = agent;
@@ -795,27 +841,27 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
socket: socket,
head: head
};
-
+
//
// If the agent for this particular `host` and `port` combination
// is not already listening for the `upgrade` event, then do so once.
- // This will force us not to disconnect.
+ // This will force us not to disconnect.
//
// In addition, it's important to note the closure scope here. Since
- // there is no mapping of the
+ // there is no mapping of the
//
if (!agent._events || agent._events['upgrade'].length === 0) {
agent.on('upgrade', function (_, remoteSocket, head) {
//
- // Prepare the socket for the reverseProxy request and begin to
- // stream data between the two sockets. Here it is important to
+ // Prepare the socket for the reverseProxy request and begin to
+ // stream data between the two sockets. Here it is important to
// note that `remoteSocket._httpMessage === reverseProxy`.
//
_socket(remoteSocket, true);
onUpgrade(remoteSocket._httpMessage, remoteSocket);
});
}
-
+
//
// If the reverseProxy connection has an underlying socket,
// then execute the WebSocket handshake.
@@ -824,9 +870,9 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
reverseProxy.socket.on('data', function handshake (data) {
//
// Ok, kind of harmfull part of code. Socket.IO sends a hash
- // at the end of handshake if protocol === 76, but we need
- // to replace 'host' and 'origin' in response so we split
- // data to printable data and to non-printable. (Non-printable
+ // at the end of handshake if protocol === 76, but we need
+ // to replace 'host' and 'origin' in response so we split
+ // data to printable data and to non-printable. (Non-printable
// will come after double-CRLF).
//
var sdata = data.toString();
@@ -836,7 +882,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
// Get the Non-Printable data
data = data.slice(Buffer.byteLength(sdata), data.length);
-
+
if (self.https && !self.target.https) {
//
// If the proxy server is running HTTPS but the client is running
@@ -848,12 +894,12 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
try {
//
// Write the printable and non-printable data to the socket
- // from the original incoming request.
- //
+ // from the original incoming request.
+ //
self.emit('websocket:handshake', req, socket, head, sdata, data);
socket.write(sdata);
socket.write(data);
- }
+ }
catch (ex) {
proxyError(ex)
}
@@ -865,7 +911,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
reverseProxy.socket.removeListener('data', handshake);
});
}
-
+
reverseProxy.on('error', proxyError);
try {
@@ -873,11 +919,11 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options
// Attempt to write the upgrade-head to the reverseProxy request.
//
reverseProxy.write(head);
- }
+ }
catch (ex) {
proxyError(ex);
}
-
+
//
// If we have been passed buffered data, resume it.
//
Please sign in to comment.
Something went wrong with that request. Please try again.