From c773eedeb6d0b22e2b41ab9215cfdc064a8095e3 Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Sun, 24 Jul 2011 23:17:24 +1000 Subject: [PATCH 01/19] [minor] add middleware to node-http-proxy --- lib/node-http-proxy.js | 89 ++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/lib/node-http-proxy.js b/lib/node-http-proxy.js index 227d06f..2e41b42 100644 --- a/lib/node-http-proxy.js +++ b/lib/node-http-proxy.js @@ -112,6 +112,30 @@ exports.getMaxSockets = function () { exports.setMaxSockets = function (value) { maxSockets = value; }; +// +// stack +// adapted from https://github.com/creationix/stack +// + +function stack (middlewares, proxy) { + var handle; + middlewares.reverse().forEach(function (layer) { + + var child = handle; + var next = function (err) { + if (err) { + throw err; + //return error(req, res, err); + } + child(req, res); + } + next.__proto__ = proxy; + handle = function (req, res) { + layer(req, res, next); + }; + }); + return handle; +} // // ### function createServer ([port, host, options, handler]) @@ -127,60 +151,59 @@ exports.setMaxSockets = function (value) { // exports.createServer = function () { var args = Array.prototype.slice.call(arguments), - callback = typeof args[0] === 'function' && args.shift(), - options = {}, port, host, forward, silent, proxy, server, handler; + callback, + options = {}, port, host, forward, silent, proxy, server, middleware = []; - if (args.length >= 2) { - port = args[0]; - host = args[1]; - options = args[2] || {}; - } - else if (args.length === 1) { - options = args[0] || {}; - if (!options.router && !callback) { - throw new Error('Cannot create server with no router and no callback'); - } - } + args.forEach(function (arg) { + + switch (typeof arg) { + case 'string': host = arg; break; + case 'number': port = arg; break; + case 'function': middleware.push(arg); break; + case 'object': options = arg; break; + }; + + }); - proxy = new HttpProxy(options); - - handler = function (req, res) { - if (callback) { - // - // If we were passed a callback to process the request - // or response in some way, then call it. - // - callback(req, res, proxy); - } - else if (port && host) { + 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. // - proxy.proxyRequest(req, res, { - port: port, - host: host - }); + handler = function (req, res) { + proxy.proxyRequest(req, res, { + port: port, + host: host + }); + } } else if (proxy.proxyTable) { // // If the proxy is configured with a ProxyTable // instance then use that before failing. // - proxy.proxyRequest(req, res); + handler = function (req, res) { + proxy.proxyRequest(req, res); + } } - else { + else 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(); }); From 7976de1121a40f963e18ea0a4673d185f847df4c Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Mon, 25 Jul 2011 22:51:09 +1000 Subject: [PATCH 02/19] support old (port,host) and (options) style when using middlewares --- lib/node-http-proxy.js | 45 ++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/lib/node-http-proxy.js b/lib/node-http-proxy.js index 2e41b42..14f7f99 100644 --- a/lib/node-http-proxy.js +++ b/lib/node-http-proxy.js @@ -112,6 +112,7 @@ exports.getMaxSockets = function () { exports.setMaxSockets = function (value) { maxSockets = value; }; + // // stack // adapted from https://github.com/creationix/stack @@ -122,15 +123,16 @@ function stack (middlewares, proxy) { middlewares.reverse().forEach(function (layer) { var child = handle; - var next = function (err) { - if (err) { - throw err; - //return error(req, res, err); - } - child(req, res); - } - next.__proto__ = proxy; 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); }; }); @@ -167,11 +169,6 @@ exports.createServer = function () { 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 @@ -183,6 +180,8 @@ exports.createServer = function () { host: host }); } + if(middleware.length) + middleware.push(handler) } else if (proxy.proxyTable) { // @@ -192,13 +191,21 @@ exports.createServer = function () { handler = function (req, res) { proxy.proxyRequest(req, res); } + if(middleware.length) + middleware.push(handler) } - else if (!handler) { - // - // Otherwise this server is improperly configured. - // - throw new Error('Cannot proxy without port, host, or router.') - } + + if(middleware.length) + //handler = callback = middleware.shift() + //else if (middleware.length) + handler = callback = stack(middleware, proxy); + + 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) From d3c06973a1bf1f1c54ca55a5d7f93b77133ef9a2 Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Mon, 25 Jul 2011 22:54:05 +1000 Subject: [PATCH 03/19] [minor] add example of using middleware to gzip response --- examples/gzip-middleware.js | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/gzip-middleware.js diff --git a/examples/gzip-middleware.js b/examples/gzip-middleware.js new file mode 100644 index 0000000..ce18c5d --- /dev/null +++ b/examples/gzip-middleware.js @@ -0,0 +1,51 @@ +/* + basic-proxy.js: Basic example of proxying over HTTP + + 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( + 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); From b5d5eaababa276f7d197e4b6a8a771b364b73139 Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Mon, 25 Jul 2011 23:16:54 +1000 Subject: [PATCH 04/19] [doc] note in readme about middleware --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 8ff8c62..45671cd 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,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: From 45f3df80937ffd5854727c91ea6b0e09cf77e160 Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Tue, 26 Jul 2011 10:47:39 +1000 Subject: [PATCH 05/19] [minor] add url-proxying middleware example --- examples/url-middleware.js | 87 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 examples/url-middleware.js diff --git a/examples/url-middleware.js b/examples/url-middleware.js new file mode 100644 index 0000000..0b10297 --- /dev/null +++ b/examples/url-middleware.js @@ -0,0 +1,87 @@ +/* + gzip-middleware.js: Basic example of 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 +// +function matcher (url, dest) { + var r = new RegExp (url) + + return function (url) { + var m = r(url) + if (!m) return + var path = url.slice(m[0].length) + console.log('proxy:', url, '->', path) + return {url: path, dest: dest} + } +} + +exports.urls = function (urls) { + + var matchers = [] + for (var url in urls) { + matchers.push(matcher(url, urls[url])) + } + + return function (req, res, next) { + // + // in nhp middlewares, `proxy` is the prototype of `next` + // + var proxy = next; + + for (var k in matchers) { + var m + if (m = matchers[k](req.url)) { + req.url = m.url + return proxy.proxyRequest(req,res, m.dest) + } + } + + } + +} + +httpProxy.createServer( + exports.urls({'/hello': {port: 9000, host: '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); From f6484de4112463c74105db82d27f131d64478f1d Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Tue, 26 Jul 2011 10:56:55 +1000 Subject: [PATCH 06/19] [doc] add comments to examples/url-middleware.js --- examples/url-middleware.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/url-middleware.js b/examples/url-middleware.js index 0b10297..a625183 100644 --- a/examples/url-middleware.js +++ b/examples/url-middleware.js @@ -1,5 +1,5 @@ /* - gzip-middleware.js: Basic example of middleware in node-http-proxy + urls-middleware.js: Basic example of middleware in node-http-proxy Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, Marak Squires, & Dominic Tarr. @@ -30,7 +30,9 @@ var util = require('util'), httpProxy = require('./../lib/node-http-proxy'); // -// Basic Http Proxy Server +// url proxying middleware example. +// +// this is not optimised or tested but shows the basic approch to writing a middleware. // function matcher (url, dest) { var r = new RegExp (url) @@ -54,19 +56,19 @@ exports.urls = function (urls) { return function (req, res, next) { // // in nhp middlewares, `proxy` is the prototype of `next` + // (this means nhp middlewares support both connect API (req, res, next) + // and nhp API (req, res, proxy) // var proxy = next; for (var k in matchers) { - var m + var m; if (m = matchers[k](req.url)) { - req.url = m.url - return proxy.proxyRequest(req,res, m.dest) + req.url = m.url; + return proxy.proxyRequest(req,res, m.dest); } } - } - } httpProxy.createServer( From caa1f494ab4effabad6d08272c3606c1d82005ea Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Tue, 26 Jul 2011 11:16:34 +1000 Subject: [PATCH 07/19] [minor] minor fixes to gzip middleware example --- examples/gzip-middleware.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/gzip-middleware.js b/examples/gzip-middleware.js index ce18c5d..6b3ff18 100644 --- a/examples/gzip-middleware.js +++ b/examples/gzip-middleware.js @@ -1,7 +1,7 @@ /* - basic-proxy.js: Basic example of proxying over HTTP + gzip-middleware.js: Basic example of middleware in node-http-proxy - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires. + 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 @@ -32,8 +32,9 @@ var util = require('util'), // // Basic Http Proxy Server // + httpProxy.createServer( - require('connect-gzip').gzip({ matchType: /./ }), + require('connect-gzip').gzip({ matchType: /?:./ }), 9000, 'localhost' ).listen(8000); From 8b48b7e0af656fdbd6da2b16ec6365beec47c302 Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Tue, 26 Jul 2011 11:18:05 +1000 Subject: [PATCH 08/19] [minor] code style changes --- lib/node-http-proxy.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/node-http-proxy.js b/lib/node-http-proxy.js index 14f7f99..d7bdf2a 100644 --- a/lib/node-http-proxy.js +++ b/lib/node-http-proxy.js @@ -180,8 +180,7 @@ exports.createServer = function () { host: host }); } - if(middleware.length) - middleware.push(handler) + if(middleware.length) middleware.push(handler) } else if (proxy.proxyTable) { // @@ -191,14 +190,10 @@ exports.createServer = function () { handler = function (req, res) { proxy.proxyRequest(req, res); } - if(middleware.length) - middleware.push(handler) + if(middleware.length) middleware.push(handler) } - if(middleware.length) - //handler = callback = middleware.shift() - //else if (middleware.length) - handler = callback = stack(middleware, proxy); + if(middleware.length) handler = stack(middleware, proxy); if (!handler) { // From 4cc18f4217739b0bd1b3ac88287cc8a23d486b6b Mon Sep 17 00:00:00 2001 From: Charlie McConnell Date: Tue, 26 Jul 2011 18:00:41 -0700 Subject: [PATCH 09/19] Tested & fixed url middleware example, added comments. --- examples/url-middleware.js | 100 ++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/examples/url-middleware.js b/examples/url-middleware.js index a625183..96f4b81 100644 --- a/examples/url-middleware.js +++ b/examples/url-middleware.js @@ -1,82 +1,79 @@ -/* - urls-middleware.js: Basic example of 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'); + httpProxy = require('http-proxy'); + +// +// This is an example of a url-routing middleware. +// This is not intended for production use, but rather as +// an example of how to write a middleware. +// -// -// url proxying middleware example. -// -// this is not optimised or tested but shows the basic approch to writing a middleware. -// function matcher (url, dest) { - var r = new RegExp (url) - + // First, turn the URL into a regex. + // NOTE: Turning user input directly into a Regular Expression is NOT SAFE. + var r = new RegExp(url.replace(/\//, '\\/')); + // This next block of code may look a little confusing. + // It returns a closure (anonymous function) for each URL to be matched, + // storing them in an array - on each request, if the URL matches one that has + // a function stored for it, the function will be called. return function (url) { var m = r(url) - if (!m) return - var path = url.slice(m[0].length) - console.log('proxy:', url, '->', path) - return {url: path, dest: dest} + if (!m) { + return; + } + var path = url.slice(m[0].length); + console.log('proxy:', url, '->', dest); + return {url: path, dest: dest}; } } exports.urls = function (urls) { - - var matchers = [] + // This is the entry point for our middleware. + // 'matchers' is the array of URL matchers, as mentioned above. + var matchers = []; for (var url in urls) { - matchers.push(matcher(url, urls[url])) + // Call the 'matcher' function above, and store the resulting closure. + matchers.push(matcher(url, urls[url])); } + // This closure is returned as the request handler. return function (req, res, next) { // - // in nhp middlewares, `proxy` is the prototype of `next` - // (this means nhp middlewares support both connect API (req, res, next) - // and nhp API (req, res, proxy) + // in node-http-proxy middlewares, `proxy` is the prototype of `next` + // (this means node-http-proxy middlewares support both the connect API (req, res, next) + // and the node-http-proxy API (req, res, proxy) // var proxy = next; - for (var k in matchers) { - var m; - if (m = matchers[k](req.url)) { + // for each URL matcher, try the request's URL. + var m = matchers[k](req.url); + // If it's a match: + if (m) { + // Replace the local URL with the destination URL. req.url = m.url; - return proxy.proxyRequest(req,res, m.dest); + // If routing to a server on another domain, the hostname in the request must be changed. + req.headers.host = m.host; + // Once any changes are taken care of, this line makes the magic happen. + proxy.proxyRequest(req, res, m.dest); } } } } +// Now we set up our proxy. httpProxy.createServer( - exports.urls({'/hello': {port: 9000, host: 'localhost'}}) + // This is where our middlewares go, with any options desired - in this case, + // the list of routes/URLs and their destinations. + exports.urls({ + '/hello': { port: 9000, host: 'localhost' }, + '/charlie': { port: 80, host: 'charlieistheman.com' }, + '/google': { port: 80, host: 'google.com' } + }) ).listen(8000); // -// Target Http Server +// Target Http Server (to listen for requests on 'localhost') // http.createServer( function (req, res) { @@ -85,5 +82,6 @@ http.createServer( 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); +util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow); \ No newline at end of file From 549bfeac233888ec84edeec350ed5a7377f3773e Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Thu, 28 Jul 2011 15:01:19 +1000 Subject: [PATCH 10/19] [fix] broken RegExp --- examples/gzip-middleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gzip-middleware.js b/examples/gzip-middleware.js index 6b3ff18..2aae822 100644 --- a/examples/gzip-middleware.js +++ b/examples/gzip-middleware.js @@ -34,7 +34,7 @@ var util = require('util'), // httpProxy.createServer( - require('connect-gzip').gzip({ matchType: /?:./ }), + require('connect-gzip').gzip({ matchType: /.?/ }), 9000, 'localhost' ).listen(8000); From 8eaec3507456731c1138c0b8ebb4e51dedc7c300 Mon Sep 17 00:00:00 2001 From: Charlie McConnell Date: Thu, 28 Jul 2011 13:43:40 -0700 Subject: [PATCH 11/19] [minor] Added body decoder middleware example. Needs fixing. --- examples/body-decoder.js | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 examples/body-decoder.js diff --git a/examples/body-decoder.js b/examples/body-decoder.js new file mode 100644 index 0000000..e4a3267 --- /dev/null +++ b/examples/body-decoder.js @@ -0,0 +1,43 @@ +#!/usr/local/bin/node + +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); \ No newline at end of file From f0917a3f97e8df2d58252f14c15ec54369c969ae Mon Sep 17 00:00:00 2001 From: indexzero Date: Fri, 29 Jul 2011 22:26:00 -0400 Subject: [PATCH 12/19] [minor] Style updates and whitespace cleaning for consistency --- examples/body-decoder.js | 35 +++- examples/gzip-middleware.js | 14 +- examples/url-middleware.js | 53 ++++++- examples/web-socket-proxy.js | 1 + lib/node-http-proxy.js | 299 ++++++++++++++++++----------------- 5 files changed, 242 insertions(+), 160 deletions(-) diff --git a/examples/body-decoder.js b/examples/body-decoder.js index e4a3267..23ae96d 100644 --- a/examples/body-decoder.js +++ b/examples/body-decoder.js @@ -1,11 +1,34 @@ -#!/usr/local/bin/node +/* + 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) { @@ -16,16 +39,22 @@ exports.bodyMod = function () { 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); } } diff --git a/examples/gzip-middleware.js b/examples/gzip-middleware.js index 2aae822..856e2f6 100644 --- a/examples/gzip-middleware.js +++ b/examples/gzip-middleware.js @@ -1,5 +1,5 @@ /* - gzip-middleware.js: Basic example of middleware in node-http-proxy + 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. @@ -32,7 +32,6 @@ var util = require('util'), // // Basic Http Proxy Server // - httpProxy.createServer( require('connect-gzip').gzip({ matchType: /.?/ }), 9000, 'localhost' @@ -41,12 +40,11 @@ httpProxy.createServer( // // 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); +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); diff --git a/examples/url-middleware.js b/examples/url-middleware.js index 96f4b81..920a477 100644 --- a/examples/url-middleware.js +++ b/examples/url-middleware.js @@ -1,3 +1,29 @@ +/* + 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'), @@ -10,13 +36,18 @@ var util = require('util'), // function matcher (url, dest) { + // // First, turn the URL into a regex. // NOTE: Turning user input directly into a Regular Expression is NOT SAFE. + // var r = new RegExp(url.replace(/\//, '\\/')); + + // // This next block of code may look a little confusing. // It returns a closure (anonymous function) for each URL to be matched, // storing them in an array - on each request, if the URL matches one that has // a function stored for it, the function will be called. + // return function (url) { var m = r(url) if (!m) { @@ -24,7 +55,10 @@ function matcher (url, dest) { } var path = url.slice(m[0].length); console.log('proxy:', url, '->', dest); - return {url: path, dest: dest}; + return { + url: path, + dest: dest + }; } } @@ -61,26 +95,29 @@ exports.urls = function (urls) { } } +// // 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. + // exports.urls({ '/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); +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); diff --git a/examples/web-socket-proxy.js b/examples/web-socket-proxy.js index d948988..d7a8efa 100644 --- a/examples/web-socket-proxy.js +++ b/examples/web-socket-proxy.js @@ -47,6 +47,7 @@ var server = http.createServer(function (req, res) { res.writeHead(200); res.end(); }); + server.listen(8080); // diff --git a/lib/node-http-proxy.js b/lib/node-http-proxy.js index d7bdf2a..150d484 100644 --- a/lib/node-http-proxy.js +++ b/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; } @@ -117,25 +117,27 @@ exports.setMaxSockets = function (value) { // 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); + 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; } @@ -152,48 +154,60 @@ 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 'object': options = arg; break; }; - }); var proxy = new HttpProxy(options); - 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); - } - if(middleware.length) middleware.push(handler) + 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) handler = stack(middleware, proxy); + 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); + } + + if (middleware.length) { + middleware.push(handler); + } + } + + if (middleware.length) { + //handler = callback = middleware.shift() + //else if (middleware.length) + handler = callback = stack(middleware, proxy); + } if (!handler) { // @@ -202,37 +216,36 @@ exports.createServer = function () { throw new Error('Cannot proxy without port, host, or router.') } - server = options.https + 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; }; @@ -253,23 +266,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` @@ -277,7 +290,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) { @@ -290,10 +303,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.__ @@ -303,9 +316,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). // @@ -337,10 +350,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(); + } }; // @@ -357,18 +372,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 @@ -377,7 +392,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. @@ -386,38 +401,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` @@ -426,16 +441,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. @@ -458,10 +473,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, @@ -470,12 +485,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; @@ -507,17 +522,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) { @@ -526,8 +541,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) { @@ -540,7 +555,7 @@ HttpProxy.prototype.proxyRequest = function (req, res, options) { options.buffer.resume(); } }; - + // // ### @private function _forwardRequest (req) // #### @req {ServerRequest} Incoming HTTP Request to proxy. @@ -552,7 +567,7 @@ HttpProxy.prototype._forwardRequest = function (req) { port = this.forward.port; host = this.forward.host; - + outgoing = { host: host, port: port, @@ -561,13 +576,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) { // @@ -575,10 +590,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) { }); @@ -596,7 +611,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. @@ -607,28 +622,30 @@ 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(); } + 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') { @@ -637,7 +654,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options // return; } - + // // Helper function for setting appropriate socket values: // 1. Turn of all bufferings @@ -654,14 +671,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) { @@ -670,7 +687,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. @@ -680,7 +697,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(); @@ -696,15 +713,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() { @@ -715,19 +732,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() { @@ -738,12 +755,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`. @@ -758,7 +775,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options req.headers.host = remoteHost; req.headers.origin = protocolName + '://' + remoteHost; } - + // // Make the outgoing WebSocket request // @@ -772,7 +789,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options 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. // @@ -781,14 +798,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; @@ -797,27 +814,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. @@ -826,9 +843,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(); @@ -838,7 +855,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 @@ -850,12 +867,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) } @@ -867,7 +884,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options reverseProxy.socket.removeListener('data', handshake); }); } - + reverseProxy.on('error', proxyError); try { @@ -875,11 +892,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. // From f7452bc42d963406f7ee19dfa353d72ce3252dd6 Mon Sep 17 00:00:00 2001 From: indexzero Date: Fri, 29 Jul 2011 22:27:59 -0400 Subject: [PATCH 13/19] [fix] Dont use res.* in proxyWebSocketRequest --- lib/node-http-proxy.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/node-http-proxy.js b/lib/node-http-proxy.js index 150d484..e1c95f1 100644 --- a/lib/node-http-proxy.js +++ b/lib/node-http-proxy.js @@ -636,8 +636,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options location = this.proxyTable.getProxyLocation(req); if (!location) { - res.writeHead(404); - return res.end(); + return socket.destroy(); } options.port = location.port; @@ -786,6 +785,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options path: req.url, headers: req.headers, }; + var reverseProxy = agent.appendMessage(outgoing); // From 020290a162146c4996831f4f13d71c1dc949f508 Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Sat, 30 Jul 2011 15:48:27 +1000 Subject: [PATCH 14/19] [docs] add middleware examples (first draft) --- examples/bodyDecoder-middleware.js | 97 ++++++++++++++++++++++++++++++ examples/jsonp-middleware.js | 30 +++++++++ examples/url-middleware2.js | 88 +++++++++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 examples/bodyDecoder-middleware.js create mode 100644 examples/jsonp-middleware.js create mode 100644 examples/url-middleware2.js diff --git a/examples/bodyDecoder-middleware.js b/examples/bodyDecoder-middleware.js new file mode 100644 index 0000000..ec10db3 --- /dev/null +++ b/examples/bodyDecoder-middleware.js @@ -0,0 +1,97 @@ + +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. + function (req, res, next) { + //remove bodyParser's listeners + req.removeAllListeners('data') + req.removeAllListeners('end') + next() + process.nextTick(function () { + if(req.body) + req.emit('data', JSON.stringify(req.body)) + req.emit('end') + }) + }, + 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"] + + }) + }) +}) diff --git a/examples/jsonp-middleware.js b/examples/jsonp-middleware.js new file mode 100644 index 0000000..11cc658 --- /dev/null +++ b/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) diff --git a/examples/url-middleware2.js b/examples/url-middleware2.js new file mode 100644 index 0000000..c59acfc --- /dev/null +++ b/examples/url-middleware2.js @@ -0,0 +1,88 @@ +var util = require('util'), + colors = require('colors'), + http = require('http'), + httpProxy = require('http-proxy'), + Store = require('./lib/store') +// +// This is an example of a url-routing middleware. +// This is not intended for production use, but rather as +// an example of how to write a middleware. +// + +function matcher (url, dest) { + // First, turn the URL into a regex. + // NOTE: Turning user input directly into a Regular Expression is NOT SAFE. + var r = new RegExp(url.replace(/\//, '\\/')); + // This next block of code may look a little confusing. + // It returns a closure (anonymous function) for each URL to be matched, + // storing them in an array - on each request, if the URL matches one that has + // a function stored for it, the function will be called. + return function (url) { + var m = r(url) + if (!m) { + return; + } + var path = url.slice(m[0].length); + console.log('proxy:', url, '->', dest); + return {url: path, dest: dest}; + } +} + +exports.urls = function (urls) { + // This is the entry point for our middleware. + // 'matchers' is the array of URL matchers, as mentioned above. + var matchers = []; + for (var url in urls) { + // Call the 'matcher' function above, and store the resulting closure. + matchers.push(matcher(url, urls[url])); + } + + // This closure is returned as the request handler. + return function (req, res, next) { + // + // in node-http-proxy middlewares, `proxy` is the prototype of `next` + // (this means node-http-proxy middlewares support both the connect API (req, res, next) + // and the node-http-proxy API (req, res, proxy) + // + var proxy = next; + for (var k in matchers) { + // for each URL matcher, try the request's URL. + var m = matchers[k](req.url); + // If it's a match: + if (m) { + // Replace the local URL with the destination URL. + req.url = m.url; + // If routing to a server on another domain, the hostname in the request must be changed. + req.headers.host = m.host; + // Once any changes are taken care of, this line makes the magic happen. + proxy.proxyRequest(req, res, m.dest); + } + } + } +} + +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. + exports.urls({ + '/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); \ No newline at end of file From 2cf4e0a9e6c78dfd093c098fc87100ae71bc9450 Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Sat, 30 Jul 2011 20:00:24 +1000 Subject: [PATCH 15/19] [api] refactor out middlewares from examples. --- examples/bodyDecoder-middleware.js | 12 +----- examples/lib/store.js | 67 +++++++++++++++++++++++++++++ examples/package.json | 12 ++++++ examples/url-middleware.js | 68 +----------------------------- examples/url-middleware2.js | 59 +------------------------- 5 files changed, 82 insertions(+), 136 deletions(-) create mode 100644 examples/lib/store.js create mode 100644 examples/package.json diff --git a/examples/bodyDecoder-middleware.js b/examples/bodyDecoder-middleware.js index ec10db3..180372f 100644 --- a/examples/bodyDecoder-middleware.js +++ b/examples/bodyDecoder-middleware.js @@ -48,17 +48,7 @@ http.createServer(new Store().handler()).listen(7531, function () { //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. - function (req, res, next) { - //remove bodyParser's listeners - req.removeAllListeners('data') - req.removeAllListeners('end') - next() - process.nextTick(function () { - if(req.body) - req.emit('data', JSON.stringify(req.body)) - req.emit('end') - }) - }, + require('connect-restreamer')(), function (req, res, proxy) { //if your posting an obect which contains type: "insult" //it will get redirected to port 2600. diff --git a/examples/lib/store.js b/examples/lib/store.js new file mode 100644 index 0000000..8144b13 --- /dev/null +++ b/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}) + }) + } + } + } +} diff --git a/examples/package.json b/examples/package.json new file mode 100644 index 0000000..16af680 --- /dev/null +++ b/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" + } +} \ No newline at end of file diff --git a/examples/url-middleware.js b/examples/url-middleware.js index 920a477..669a73e 100644 --- a/examples/url-middleware.js +++ b/examples/url-middleware.js @@ -28,72 +28,6 @@ var util = require('util'), colors = require('colors'), http = require('http'), httpProxy = require('http-proxy'); - -// -// This is an example of a url-routing middleware. -// This is not intended for production use, but rather as -// an example of how to write a middleware. -// - -function matcher (url, dest) { - // - // First, turn the URL into a regex. - // NOTE: Turning user input directly into a Regular Expression is NOT SAFE. - // - var r = new RegExp(url.replace(/\//, '\\/')); - - // - // This next block of code may look a little confusing. - // It returns a closure (anonymous function) for each URL to be matched, - // storing them in an array - on each request, if the URL matches one that has - // a function stored for it, the function will be called. - // - return function (url) { - var m = r(url) - if (!m) { - return; - } - var path = url.slice(m[0].length); - console.log('proxy:', url, '->', dest); - return { - url: path, - dest: dest - }; - } -} - -exports.urls = function (urls) { - // This is the entry point for our middleware. - // 'matchers' is the array of URL matchers, as mentioned above. - var matchers = []; - for (var url in urls) { - // Call the 'matcher' function above, and store the resulting closure. - matchers.push(matcher(url, urls[url])); - } - - // This closure is returned as the request handler. - return function (req, res, next) { - // - // in node-http-proxy middlewares, `proxy` is the prototype of `next` - // (this means node-http-proxy middlewares support both the connect API (req, res, next) - // and the node-http-proxy API (req, res, proxy) - // - var proxy = next; - for (var k in matchers) { - // for each URL matcher, try the request's URL. - var m = matchers[k](req.url); - // If it's a match: - if (m) { - // Replace the local URL with the destination URL. - req.url = m.url; - // If routing to a server on another domain, the hostname in the request must be changed. - req.headers.host = m.host; - // Once any changes are taken care of, this line makes the magic happen. - proxy.proxyRequest(req, res, m.dest); - } - } - } -} // // Now we set up our proxy. @@ -103,7 +37,7 @@ httpProxy.createServer( // This is where our middlewares go, with any options desired - in this case, // the list of routes/URLs and their destinations. // - exports.urls({ + require('proxy-by-url')({ '/hello': { port: 9000, host: 'localhost' }, '/charlie': { port: 80, host: 'charlieistheman.com' }, '/google': { port: 80, host: 'google.com' } diff --git a/examples/url-middleware2.js b/examples/url-middleware2.js index c59acfc..95dfef4 100644 --- a/examples/url-middleware2.js +++ b/examples/url-middleware2.js @@ -3,63 +3,6 @@ var util = require('util'), http = require('http'), httpProxy = require('http-proxy'), Store = require('./lib/store') -// -// This is an example of a url-routing middleware. -// This is not intended for production use, but rather as -// an example of how to write a middleware. -// - -function matcher (url, dest) { - // First, turn the URL into a regex. - // NOTE: Turning user input directly into a Regular Expression is NOT SAFE. - var r = new RegExp(url.replace(/\//, '\\/')); - // This next block of code may look a little confusing. - // It returns a closure (anonymous function) for each URL to be matched, - // storing them in an array - on each request, if the URL matches one that has - // a function stored for it, the function will be called. - return function (url) { - var m = r(url) - if (!m) { - return; - } - var path = url.slice(m[0].length); - console.log('proxy:', url, '->', dest); - return {url: path, dest: dest}; - } -} - -exports.urls = function (urls) { - // This is the entry point for our middleware. - // 'matchers' is the array of URL matchers, as mentioned above. - var matchers = []; - for (var url in urls) { - // Call the 'matcher' function above, and store the resulting closure. - matchers.push(matcher(url, urls[url])); - } - - // This closure is returned as the request handler. - return function (req, res, next) { - // - // in node-http-proxy middlewares, `proxy` is the prototype of `next` - // (this means node-http-proxy middlewares support both the connect API (req, res, next) - // and the node-http-proxy API (req, res, proxy) - // - var proxy = next; - for (var k in matchers) { - // for each URL matcher, try the request's URL. - var m = matchers[k](req.url); - // If it's a match: - if (m) { - // Replace the local URL with the destination URL. - req.url = m.url; - // If routing to a server on another domain, the hostname in the request must be changed. - req.headers.host = m.host; - // Once any changes are taken care of, this line makes the magic happen. - proxy.proxyRequest(req, res, m.dest); - } - } - } -} http.createServer(new Store().handler()).listen(7531) @@ -67,7 +10,7 @@ http.createServer(new Store().handler()).listen(7531) httpProxy.createServer( // This is where our middlewares go, with any options desired - in this case, // the list of routes/URLs and their destinations. - exports.urls({ + require('proxy-by-url')({ '/store': { port: 7531, host: 'localhost' }, '/': { port: 9000, host: 'localhost' } }) From 5bf2d59241a7695f43bb89e5cb41ade2ab7a0ad2 Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Sat, 30 Jul 2011 20:33:59 +1000 Subject: [PATCH 16/19] [doc] add note on middleware to Using node-http-proxy section of the README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 45671cd..e270ff7 100644 --- a/README.md +++ b/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: From 6ec8d6caace3797841c0447feb081aa7920aa0dd Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Tue, 2 Aug 2011 14:43:56 +1000 Subject: [PATCH 17/19] [minor] add example to test concurrency --- examples/concurrent-proxy.js | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 examples/concurrent-proxy.js diff --git a/examples/concurrent-proxy.js b/examples/concurrent-proxy.js new file mode 100644 index 0000000..4bf5673 --- /dev/null +++ b/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); From 20125889b362c61c85924810de446e1e7b18d079 Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Tue, 2 Aug 2011 15:25:23 +1000 Subject: [PATCH 18/19] [fix] do not use middleware code if it's not needed --- lib/node-http-proxy.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/node-http-proxy.js b/lib/node-http-proxy.js index e1c95f1..dd2279f 100644 --- a/lib/node-http-proxy.js +++ b/lib/node-http-proxy.js @@ -166,7 +166,7 @@ exports.createServer = function () { 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; }; }); @@ -203,11 +203,15 @@ exports.createServer = function () { } } - if (middleware.length) { + if (middleware.length /*> 1*/) { //handler = callback = middleware.shift() //else if (middleware.length) 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) }; + } if (!handler) { // From 0f8fe8e2460fd27edfba44989b78aa6b8c9a38e2 Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Tue, 2 Aug 2011 15:33:03 +1000 Subject: [PATCH 19/19] [style] tidy --- lib/node-http-proxy.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/node-http-proxy.js b/lib/node-http-proxy.js index dd2279f..01f9ed7 100644 --- a/lib/node-http-proxy.js +++ b/lib/node-http-proxy.js @@ -203,13 +203,12 @@ exports.createServer = function () { } } - if (middleware.length /*> 1*/) { - //handler = callback = middleware.shift() - //else if (middleware.length) + 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] + 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) }; }