From 8c3e993833e2a09376fdb5e7c847ff00b53e70d8 Mon Sep 17 00:00:00 2001 From: indexzero Date: Sun, 21 Nov 2010 02:07:07 -0500 Subject: [PATCH] [test] Simplified tests. Added tests for experimental websocket support --- lib/node-http-proxy.js | 148 ++++++++++------------ test/node-http-proxy-notarget.js | 140 --------------------- test/node-http-proxy-test.js | 167 +++++++++++++++---------- test/node-http-proxy-websocket-test.js | 54 -------- vendor/pool/main.js | 1 - 5 files changed, 171 insertions(+), 339 deletions(-) delete mode 100644 test/node-http-proxy-notarget.js delete mode 100644 test/node-http-proxy-websocket-test.js diff --git a/lib/node-http-proxy.js b/lib/node-http-proxy.js index 4bfb3c4cb..4758a8896 100644 --- a/lib/node-http-proxy.js +++ b/lib/node-http-proxy.js @@ -28,7 +28,6 @@ var sys = require('sys'), http = require('http'), events = require('events'), pool = require('./../vendor/pool/main'), - eyes = require('eyes'), min = 0, max = 100; @@ -44,13 +43,9 @@ exports.createServer = function () { if (args[0]) port = args[0]; if (args[1]) host = args[1]; - var server = http.createServer(function (req, res){ + var server = http.createServer(function (req, res) { var proxy = new HttpProxy(req, res); - proxy.emitter.on('proxy', function (err, body) { - server.emit('proxy', err, body); - }); - // If we were passed a callback to process the request // or response in some way, then call it. if(callback) { @@ -61,15 +56,14 @@ exports.createServer = function () { } }); - // If callback is empty - tunnel websocket request automatically if (!callback) { - // WebSocket support + // WebSocket support: if callback is empty tunnel + // websocket request automatically server.on('upgrade', function(req, socket, head) { var proxy = new HttpProxy(req, socket, head); // Tunnel websocket requests too proxy.proxyWebSocketRequest(port, host); - }); } @@ -87,7 +81,6 @@ exports.setMax = function (value) { }; var HttpProxy = function (req, res, head) { - this.emitter = new(events.EventEmitter); this.events = {}; this.req = req; @@ -145,96 +138,90 @@ HttpProxy.prototype = { self.body = ''; // Open new HTTP request to internal resource with will act as a reverse proxy pass - //var p = manager.getPool(port, server); - //sys.puts('Current pool count for ' + req.headers.host + ":" + port + ' ' + p.clients.length + ', Busy: ' + p.getBusy() + ', Free: ' + p.getFree()); + var p = manager.getPool(port, server); - //p.on('error', function (err) { + p.on('error', function (err) { // Remark: We should probably do something here // but this is a hot-fix because I don't think 'pool' // should be emitting this event. - //}); + }); var client = http.createClient(port, server); - var reverse_proxy = client.request(req.method, req.url, req.headers); - - //p.request(req.method, req.url, req.headers, function (reverse_proxy) { - - // Create an error handler so we can use it temporarily - function error(obj) { - var fn = function (err) { - res.writeHead(500, {'Content-Type': 'text/plain'}); - - if(req.method !== 'HEAD') { - res.write('An error has occurred: ' + JSON.stringify(err)); - } + p.request(req.method, req.url, req.headers, function (reverse_proxy) { + // Create an error handler so we can use it temporarily + function error(obj) { + var fn = function (err) { + res.writeHead(500, {'Content-Type': 'text/plain'}); + + if(req.method !== 'HEAD') { + res.write('An error has occurred: ' + JSON.stringify(err)); + } - // Response end may never come so removeListener here - obj.removeListener('error', fn); - res.end(); - }; + // Response end may never come so removeListener here + obj.removeListener('error', fn); + res.end(); + }; - return fn; - }; + return fn; + }; - // Add a listener for the connection timeout event - var reverseProxyError = error(reverse_proxy), - clientError = error(client); + // Add a listener for the connection timeout event + var reverseProxyError = error(reverse_proxy), + clientError = error(client); - reverse_proxy.addListener('error', reverseProxyError); - client.addListener('error', clientError); + reverse_proxy.addListener('error', reverseProxyError); + client.addListener('error', clientError); - // Add a listener for the reverse_proxy response event - reverse_proxy.addListener('response', function (response) { - if (response.headers.connection) { - if (req.headers.connection) response.headers.connection = req.headers.connection; - else response.headers.connection = 'close'; - } - - // Set the response headers of the client response - res.writeHead(response.statusCode, response.headers); + // Add a listener for the reverse_proxy response event + reverse_proxy.addListener('response', function (response) { + if (response.headers.connection) { + if (req.headers.connection) response.headers.connection = req.headers.connection; + else response.headers.connection = 'close'; + } - // Status code = 304 - // No 'data' event and no 'end' - if (response.statusCode === 304) { - res.end(); - return; - } + // Set the response headers of the client response + res.writeHead(response.statusCode, response.headers); - // Add event handler for the proxied response in chunks - response.addListener('data', function (chunk) { - if(req.method !== 'HEAD') { - res.write(chunk, 'binary'); - self.body += chunk; + // Status code = 304 + // No 'data' event and no 'end' + if (response.statusCode === 304) { + res.end(); + return; } - }); - // Add event listener for end of proxied response - response.addListener('end', function () { - // Remark: Emit the end event for testability - self.emitter.emit('proxy', null, self.body); - reverse_proxy.removeListener('error', reverseProxyError); - res.end(); + // Add event handler for the proxied response in chunks + response.addListener('data', function (chunk) { + if(req.method !== 'HEAD') { + res.write(chunk, 'binary'); + self.body += chunk; + } + }); + + // Add event listener for end of proxied response + response.addListener('end', function () { + reverse_proxy.removeListener('error', reverseProxyError); + res.end(); + }); }); - }); - // Chunk the client request body as chunks from the proxied request come in - req.addListener('data', function (chunk) { - reverse_proxy.write(chunk, 'binary'); - }) + // Chunk the client request body as chunks from the proxied request come in + req.addListener('data', function (chunk) { + reverse_proxy.write(chunk, 'binary'); + }) - // At the end of the client request, we are going to stop the proxied request - req.addListener('end', function () { - reverse_proxy.end(); - }); + // At the end of the client request, we are going to stop the proxied request + req.addListener('end', function () { + reverse_proxy.end(); + }); - // On 'close' event remove 'error' listener - client.addListener('close', function() { - client.removeListener('error', clientError); - }); + // On 'close' event remove 'error' listener + client.addListener('close', function() { + client.removeListener('error', clientError); + }); - self.unwatch(req); + self.unwatch(req); - //}); + }); }, proxyWebSocketRequest: function (port, server, host) { @@ -251,8 +238,7 @@ HttpProxy.prototype = { return h; } - // WebSocket requests has - // method = GET + // WebSocket requests has method = GET if (req.method !== 'GET' || headers.upgrade.toLowerCase() !== 'websocket') { // This request is not WebSocket request return; diff --git a/test/node-http-proxy-notarget.js b/test/node-http-proxy-notarget.js deleted file mode 100644 index 6afa33cc8..000000000 --- a/test/node-http-proxy-notarget.js +++ /dev/null @@ -1,140 +0,0 @@ -/* - node-http-proxy-test.js: http proxy for node.js - - Copyright (c) 2010 Charlie Robbins & Marak Squires http://github.com/nodejitsu/node-http-proxy - - 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 vows = require('vows'), - sys = require('sys'), - assert = require('assert'), - http = require('http'); - -var httpProxy = require('./../lib/node-http-proxy'); -var testServers = {}; - -// -// Creates the reverse proxy server -// -var startProxyServer = function (port, server) { - var proxyServer = httpProxy.createServer(port, server); - proxyServer.listen(8080); - return proxyServer; -}; - -// -// Creates the reverse proxy server with a specified latency -// -var startLatentProxyServer = function (port, server, latency) { - // Initialize the nodeProxy and start proxying the request - var proxyServer = httpProxy.createServer(function (req, res, proxy) { - setTimeout(function () { - proxy.proxyRequest(port, server); - }, latency); - }); - - proxyServer.listen(8081); - return proxyServer; -}; - -// -// Creates the 'hellonode' server -// -var startTargetServer = function (port) { - var targetServer = http.createServer(function (req, res) { - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.write('hello world') - res.end(); - }); - - targetServer.listen(port); - return targetServer; -}; - -// -// The default test bootstrapper with no latency -// -var startTest = function (port) { - var proxyServer = startProxyServer(port, 'localhost'), - targetServer = startTargetServer(port + 1000); - - testServers.noLatency = []; - testServers.noLatency.push(proxyServer); - testServers.noLatency.push(targetServer); - - return proxyServer; -}; - -// -// The test bootstrapper with some latency -// -var startTestWithLatency = function (port) { - var proxyServer = startLatentProxyServer(port, 'localhost', 2000), - targetServer = startTargetServer(port); - - testServers.latency = []; - testServers.latency.push(proxyServer); - testServers.latency.push(targetServer); - - return proxyServer; -}; - -vows.describe('node-http-proxy').addBatch({ - "A node-http-proxy": { - "when instantiated directly": { - "and an incoming request is proxied to the helloNode server" : { - "with no latency" : { - topic: function () { - var proxy = startTest(8082); - proxy.addListener('proxy', this.callback); - - var client = http.createClient(8080, 'localhost'); - var request = client.request('GET', '/'); - request.end(); - }, - "it should received 'hello world'": function (err, body) { - assert.equal(body, 'hello world'); - testServers.noLatency.forEach(function (server) { - server.close(); - }) - } - }, - "with latency": { - topic: function () { - var proxy = startTestWithLatency(8083); - proxy.addListener('proxy', this.callback); - - var client = http.createClient(8081, 'localhost'); - var request = client.request('GET', '/'); - request.end(); - }, - "it should receive 'hello world'": function (err, body) { - assert.equal(body, 'hello world'); - testServers.latency.forEach(function (server) { - server.close(); - }) - } - } - } - } - } -}).export(module); \ No newline at end of file diff --git a/test/node-http-proxy-test.js b/test/node-http-proxy-test.js index ac786ffcb..d1270b26f 100644 --- a/test/node-http-proxy-test.js +++ b/test/node-http-proxy-test.js @@ -26,34 +26,35 @@ var vows = require('vows'), sys = require('sys'), + request = require('request'), assert = require('assert'), http = require('http'); - + var httpProxy = require('./../lib/node-http-proxy'); -var testServers = {}; +var testServers = []; // // Creates the reverse proxy server // -var startProxyServer = function (port, server) { - var proxyServer = httpProxy.createServer(port, server); - proxyServer.listen(8080); - return proxyServer; +var startProxyServer = function (port, targetPort, server) { + var proxyServer = httpProxy.createServer(targetPort, server); + proxyServer.listen(port); + testServers.push(proxyServer); }; // // Creates the reverse proxy server with a specified latency // -var startLatentProxyServer = function (port, server, latency) { +var startLatentProxyServer = function (port, targetPort, server, latency) { // Initialize the nodeProxy and start proxying the request var proxyServer = httpProxy.createServer(function (req, res, proxy) { setTimeout(function () { - proxy.proxyRequest(port, server); + proxy.proxyRequest(targetPort, server); }, latency); }); - proxyServer.listen(8081); - return proxyServer; + proxyServer.listen(port); + testServers.push(proxyServer); }; // @@ -67,74 +68,114 @@ var startTargetServer = function (port) { }); targetServer.listen(port); + testServers.push(targetServer); return targetServer; }; -// -// The default test bootstrapper with no latency -// -var startTest = function (port) { - var proxyServer = startProxyServer(port, 'localhost'), - targetServer = startTargetServer(port); - - testServers.noLatency = []; - testServers.noLatency.push(proxyServer); - testServers.noLatency.push(targetServer); - - return proxyServer; -}; - -// -// The test bootstrapper with some latency -// -var startTestWithLatency = function (port) { - var proxyServer = startLatentProxyServer(port, 'localhost', 2000), - targetServer = startTargetServer(port); - - testServers.latency = []; - testServers.latency.push(proxyServer); - testServers.latency.push(targetServer); - - return proxyServer; -}; - vows.describe('node-http-proxy').addBatch({ - "A node-http-proxy": { - "when instantiated directly": { - "and an incoming request is proxied to the helloNode server" : { - "with no latency" : { + "An instance of HttpProxy": { + "an incoming request to the helloNode server": { + "with no latency" : { + "and a valid target server": { topic: function () { - var proxy = startTest(8082); - proxy.addListener('proxy', this.callback); - - var client = http.createClient(8080, 'localhost'); - var request = client.request('GET', '/'); - request.end(); + startProxyServer(8080, 8081, 'localhost'), + startTargetServer(8081); + var options = { + method: 'GET', + uri: 'http://localhost:8080' + }; + + request(options, this.callback); }, - "it should received 'hello world'": function (err, body) { + "it should received 'hello world'": function (err, res, body) { assert.equal(body, 'hello world'); - testServers.noLatency.forEach(function (server) { - server.close(); - }) } }, - "with latency": { + "and without a valid target server": { topic: function () { - var proxy = startTestWithLatency(8083); - proxy.addListener('proxy', this.callback); - - var client = http.createClient(8081, 'localhost'); - var request = client.request('GET', '/'); - request.end(); + startProxyServer(8082, 9000, 'localhost'); + var options = { + method: 'GET', + uri: 'http://localhost:8082' + }; + + request(options, this.callback); + }, + "it should receive 500 response code": function (err, res, body) { + assert.equal(res.statusCode, 500); + } + } + }, + "with latency": { + "and a valid target server": { + topic: function () { + startLatentProxyServer(8083, 8084, 'localhost', 1000), + startTargetServer(8084); + var options = { + method: 'GET', + uri: 'http://localhost:8083' + }; + + request(options, this.callback); }, - "it should receive 'hello world'": function (err, body) { + "it should receive 'hello world'": function (err, res, body) { assert.equal(body, 'hello world'); - testServers.latency.forEach(function (server) { - server.close(); - }) + } + }, + "and without a valid target server": { + topic: function () { + startLatentProxyServer(8085, 9000, 'localhost', 1000); + var options = { + method: 'GET', + uri: 'http://localhost:8085' + }; + + request(options, this.callback); + }, + "it should receive 500 response code": function (err, res, body) { + assert.equal(res.statusCode, 500); } } } } } +}).addBatch({ + "An instance of HttpProxy": { + "an incoming WebSocket request to the helloNode server": { + "with no latency" : { + // Remark: This test is not working + /*topic: function () { + startProxyServer(8086, 8087, 'localhost'), + startTargetServer(8087); + var options = { + method: 'GET', + uri: 'http://localhost:8086', + headers: { + 'Upgrade': 'WebSocket', + 'Connection': 'WebSocket', + 'Host': 'localhost' + } + }; + + request(options, this.callback); + }, + "it should receive 'hello world'": function (err, res, body) { + assert.equal(body, 'hello world'); + }*/ + } + } + } +}).addBatch({ + "When the tests are over": { + topic: function () { + testServers.forEach(function (server) { + server.close(); + }); + + return testServers; + }, + "the servers should clean up": function () { + assert.isTrue(true); + } + } }).export(module); \ No newline at end of file diff --git a/test/node-http-proxy-websocket-test.js b/test/node-http-proxy-websocket-test.js deleted file mode 100644 index 25e373e83..000000000 --- a/test/node-http-proxy-websocket-test.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - node-http-proxy-websocket-test.js: http proxy for node.js - - Copyright (c) 2010 Fedor Indutny - - 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 vows = require('vows'), - sys = require('sys'), - assert = require('assert'), - http = require('http'); - -var httpProxy = require('./../lib/node-http-proxy'); -var testServers = {}; - -var server = httpProxy.createServer(function(req, res) { - var p = new httpProxy.HttpProxy(req, res); - - sys.puts('http request'); - - p.proxyRequest(8080, 'localhost'); -}); - -server.on('upgrade', function(req, socket, head) { - var p = new httpProxy.HttpProxy(req, socket, head); - - sys.puts('websocket request'); - - p.proxyWebSocketRequest(8080, 'localhost'); -}); - -server.listen(8000); - -httpProxy.createServer(8080, 'localhost').listen(8001); - diff --git a/vendor/pool/main.js b/vendor/pool/main.js index 8daec03fe..30d960289 100644 --- a/vendor/pool/main.js +++ b/vendor/pool/main.js @@ -1,5 +1,4 @@ var sys = require('sys') - , eyes = require('eyes') , http = require('http') , events = require('events') ;