From 621f9b425a272421de98a674f1679f0c47912733 Mon Sep 17 00:00:00 2001 From: Samyak Bhuta Date: Mon, 28 Nov 2011 08:56:45 +0000 Subject: [PATCH 01/56] Allowing the common proxy headers' value to be appended in proxy chain scenarios. --- lib/node-http-proxy/http-proxy.js | 58 ++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/lib/node-http-proxy/http-proxy.js b/lib/node-http-proxy/http-proxy.js index 7724ddb..227948d 100644 --- a/lib/node-http-proxy/http-proxy.js +++ b/lib/node-http-proxy/http-proxy.js @@ -122,17 +122,36 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) { // // Add common proxy headers to the request so that they can - // be availible to the proxy target server: + // be availible to the proxy target server. If the proxy is + // part of proxy chain it will append the address: // // * `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. // - if (this.enable.xforward && req.connection && req.socket) { - req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.socket.remoteAddress; - req.headers['x-forwarded-port'] = req.connection.remotePort || req.socket.remotePort; - req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http'; + + if (req.headers['x-forwarded-for']){ + var addressToAppend = "," + req.connection.remoteAddress || req.socket.remoteAddress; + req.headers['x-forwarded-for'] += addressToAppend; + } else { + req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.socket.remoteAddress; + } + + if (req.headers['x-forwarded-port']){ + var portToAppend = "," + req.connection.remotePort || req.socket.remotePort; + req.headers['x-forwarded-port'] += portToAppend; + } else { + req.headers['x-forwarded-port'] = req.connection.remotePort || req.socket.remotePort; + } + + if (req.headers['x-forwarded-proto']){ + var protoToAppend = "," + req.connection.pair ? 'https' : 'http'; + req.headers['x-forwarded-proto'] += protoToAppend; + } else { + req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http'; + } + } // @@ -299,6 +318,7 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) { // `req` write it to the `reverseProxy` request. // req.on('data', function (chunk) { + if (!errState) { var flushed = reverseProxy.write(chunk); if (!flushed) { @@ -375,16 +395,36 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) // // Add common proxy headers to the request so that they can - // be availible to the proxy target server: + // be availible to the proxy target server. If the proxy is + // part of proxy chain it will append the address: // // * `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. // if (this.enable.xforward && req.connection && req.connection.socket) { - 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'] = req.connection.pair ? 'https' : 'http'; + + if (req.headers['x-forwarded-for']){ + var addressToAppend = "," + req.connection.remoteAddress || req.connection.socket.remoteAddress; + req.headers['x-forwarded-for'] += addressToAppend; + } else { + req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.connection.socket.remoteAddress; + } + + if (req.headers['x-forwarded-port']){ + var portToAppend = "," + req.connection.remotePort || req.connection.socket.remotePort; + req.headers['x-forwarded-port'] += portToAppend; + } else { + req.headers['x-forwarded-port'] = req.connection.remotePort || req.connection.socket.remotePort; + } + + if (req.headers['x-forwarded-proto']){ + var protoToAppend = "," + req.connection.pair ? 'https' : 'http'; + req.headers['x-forwarded-proto'] += protoToAppend; + } else { + req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http'; + } + } // From 553e7fbc335a9befd166d472f057aa50452a9d40 Mon Sep 17 00:00:00 2001 From: "Ken \"Elf\" Mathieu Sternberg" Date: Tue, 29 Nov 2011 09:52:32 -0800 Subject: [PATCH 02/56] Modified the ad-hoc proxy lookup to use _getKey(), rather than the error-prone in-line method. _getKey() will look for options.target as well as options.host:options.port, and so is useful for a segmented proxy server where the destination proxies are not constructed before first references. --- lib/node-http-proxy/routing-proxy.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/node-http-proxy/routing-proxy.js b/lib/node-http-proxy/routing-proxy.js index 77b4a8d..4e4fc32 100644 --- a/lib/node-http-proxy/routing-proxy.js +++ b/lib/node-http-proxy/routing-proxy.js @@ -180,8 +180,8 @@ RoutingProxy.prototype.proxyRequest = function (req, res, options) { options.host = location.host; } - var key = options.host + ':' + options.port, - proxy; + var key = this._getKey(options), + proxy; if (!this.proxies[key]) { this.add(options); @@ -218,8 +218,8 @@ RoutingProxy.prototype.proxyWebSocketRequest = function (req, socket, head, opti options.host = location.host; } - var key = options.host + ':' + options.port, - proxy; + var key = this._getKey(options), + proxy; if (!this.proxies[key]) { this.add(options); From 411bb51cc630d44c9d65b8a363fba52178cd081b Mon Sep 17 00:00:00 2001 From: "Ken \"Elf\" Mathieu Sternberg" Date: Tue, 29 Nov 2011 10:10:16 -0800 Subject: [PATCH 03/56] Fix issue where front-end is HTTPS, back-end is HTTP, and server issues a redirect. This handles the case where a back-end web application such as Django or Rails issues a redirect and automatically decorates the URL with the protocol with which it was addressed. If the back-ends are internal and HTTP-only, then they'll issue a URL with 'http://' as the protocol. This must be fixed before leaving the proxy. This also handles the (unusual) case where a back-end speaks only https://, but the user is deploying node-http-proxy to make that service available to non-SSL capable browsers. Works only with 301 and 302 codes. --- lib/node-http-proxy/http-proxy.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/node-http-proxy/http-proxy.js b/lib/node-http-proxy/http-proxy.js index 7724ddb..1c7842c 100644 --- a/lib/node-http-proxy/http-proxy.js +++ b/lib/node-http-proxy/http-proxy.js @@ -213,6 +213,15 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) { delete response.headers['transfer-encoding']; } + if ((response.statusCode === 301) || (response.statusCode === 302)) { + if (self.source.https && !self.target.https) { + response.headers.location = response.headers.location.replace(/^http\:/, 'https:'); + } + if (self.target.https && !self.source.https) { + response.headers.location = response.headers.location.replace(/^https\:/, 'http:'); + } + } + // Set the headers of the client response res.writeHead(response.statusCode, response.headers); From 8d701bb20b593c6cdf0ff1bc35cf83051b21a35e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Tue, 25 Oct 2011 09:38:38 +0200 Subject: [PATCH 04/56] [example] Replace `sys` usages with `util` --- examples/websocket/latent-websocket-proxy.js | 8 ++++---- examples/websocket/standalone-websocket-proxy.js | 8 ++++---- examples/websocket/websocket-proxy.js | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/websocket/latent-websocket-proxy.js b/examples/websocket/latent-websocket-proxy.js index 3112f6b..338d7ea 100644 --- a/examples/websocket/latent-websocket-proxy.js +++ b/examples/websocket/latent-websocket-proxy.js @@ -24,7 +24,7 @@ */ -var sys = require('sys'), +var util = require('util'), http = require('http'), colors = require('colors'), websocket = require('../../vendor/websocket'), @@ -55,10 +55,10 @@ server.listen(8080); // var socket = io.listen(server); socket.on('connection', function (client) { - sys.debug('Got websocket connection'); + util.debug('Got websocket connection'); client.on('message', function (msg) { - sys.debug('Got message from client: ' + msg); + util.debug('Got message from client: ' + msg); }); socket.broadcast('from server'); @@ -101,5 +101,5 @@ ws.on('open', function () { }); ws.on('message', function (msg) { - sys.debug('Got message: ' + utils.decode(msg)); + util.debug('Got message: ' + utils.decode(msg)); }); diff --git a/examples/websocket/standalone-websocket-proxy.js b/examples/websocket/standalone-websocket-proxy.js index bfbc252..cd4a855 100644 --- a/examples/websocket/standalone-websocket-proxy.js +++ b/examples/websocket/standalone-websocket-proxy.js @@ -24,7 +24,7 @@ */ -var sys = require('sys'), +var util = require('util'), http = require('http'), colors = require('colors'), websocket = require('../../vendor/websocket'), @@ -55,10 +55,10 @@ server.listen(8080); // var socket = io.listen(server); socket.on('connection', function (client) { - sys.debug('Got websocket connection'); + util.debug('Got websocket connection'); client.on('message', function (msg) { - sys.debug('Got message from client: ' + msg); + util.debug('Got message from client: ' + msg); }); socket.broadcast('from server'); @@ -97,5 +97,5 @@ ws.on('open', function () { }); ws.on('message', function (msg) { - sys.debug('Got message: ' + utils.decode(msg)); + util.debug('Got message: ' + utils.decode(msg)); }); diff --git a/examples/websocket/websocket-proxy.js b/examples/websocket/websocket-proxy.js index 975fab0..0b76b8a 100644 --- a/examples/websocket/websocket-proxy.js +++ b/examples/websocket/websocket-proxy.js @@ -24,7 +24,7 @@ */ -var sys = require('sys'), +var util = require('util'), http = require('http'), colors = require('colors'), websocket = require('../../vendor/websocket'), @@ -55,10 +55,10 @@ server.listen(8080); // var socket = io.listen(server); socket.on('connection', function (client) { - sys.debug('Got websocket connection'); + util.debug('Got websocket connection'); client.on('message', function (msg) { - sys.debug('Got message from client: ' + msg); + util.debug('Got message from client: ' + msg); }); socket.broadcast('from server'); @@ -80,5 +80,5 @@ ws.on('open', function () { }); ws.on('message', function (msg) { - sys.debug('Got message: ' + utils.decode(msg)); + util.debug('Got message: ' + utils.decode(msg)); }); From 6655e0164216449a97090651230266da8ced0150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Thu, 27 Oct 2011 19:36:05 +0200 Subject: [PATCH 05/56] [v0.6] Don't use `agent.appendMessage()` Instead, just perform a request. --- lib/node-http-proxy/http-proxy.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/node-http-proxy/http-proxy.js b/lib/node-http-proxy/http-proxy.js index 227948d..7ae8b6c 100644 --- a/lib/node-http-proxy/http-proxy.js +++ b/lib/node-http-proxy/http-proxy.js @@ -590,11 +590,12 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) // outgoing.host = this.target.host; outgoing.port = this.target.port; + outgoing.agent = agent; outgoing.method = 'GET'; outgoing.path = req.url; outgoing.headers = req.headers; - var reverseProxy = agent.appendMessage(outgoing); + var reverseProxy = this.target.protocol.request(outgoing); // // On any errors from the `reverseProxy` emit the From 86b4122323ca32d455714b1149b99acce49a9e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Wed, 2 Nov 2011 13:23:40 +0100 Subject: [PATCH 06/56] [v0.6] `http.Agent` uses different structure for sockets --- lib/node-http-proxy/http-proxy.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/node-http-proxy/http-proxy.js b/lib/node-http-proxy/http-proxy.js index 7ae8b6c..9e5e545 100644 --- a/lib/node-http-proxy/http-proxy.js +++ b/lib/node-http-proxy/http-proxy.js @@ -753,9 +753,11 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) HttpProxy.prototype.close = function () { [this.forward, this.target].forEach(function (proxy) { if (proxy && proxy.agent) { - proxy.agent.sockets.forEach(function (socket) { - socket.end(); - }); + for (var host in proxy.agent.sockets) { + proxy.agent.sockets[host].forEach(function (socket) { + socket.end(); + }); + } } }); }; From 5b52c896947db42ac01e6038c9170d8859d33aea Mon Sep 17 00:00:00 2001 From: indexzero Date: Sat, 10 Sep 2011 06:31:45 -0400 Subject: [PATCH 07/56] [refactor] Updates to support http2 from @mikeal Conflicts: lib/node-http-proxy/http-proxy.js --- lib/node-http-proxy/http-proxy.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/node-http-proxy/http-proxy.js b/lib/node-http-proxy/http-proxy.js index 9e5e545..476b16c 100644 --- a/lib/node-http-proxy/http-proxy.js +++ b/lib/node-http-proxy/http-proxy.js @@ -25,6 +25,7 @@ */ var events = require('events'), + http = require('http'), util = require('util'), httpProxy = require('../node-http-proxy'); @@ -594,6 +595,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) outgoing.method = 'GET'; outgoing.path = req.url; outgoing.headers = req.headers; + outgoing.agent = agent; var reverseProxy = this.target.protocol.request(outgoing); @@ -617,7 +619,6 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) // available to the `upgrade` event. This bookkeeping is not tracked anywhere // in nodejs core and is **very** specific to proxying WebSockets. // - reverseProxy.agent = agent; reverseProxy.incoming = { request: req, socket: socket, @@ -632,17 +633,15 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) // In addition, it's important to note the closure scope here. Since // there is no mapping of the socket to the request bound to it. // - 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 - // note that `remoteSocket._httpMessage === reverseProxy`. - // - _socket(remoteSocket, true); - onUpgrade(remoteSocket._httpMessage, remoteSocket); - }); - } + reverseProxy.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 + // note that `remoteSocket._httpMessage === reverseProxy`. + // + _socket(remoteSocket, true); + onUpgrade(remoteSocket._httpMessage, remoteSocket); + }); // // If the reverseProxy connection has an underlying socket, From 63dfc7f1757fc9a1a9bceeb3b035e97be6504692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Sun, 18 Dec 2011 21:43:33 +0100 Subject: [PATCH 08/56] [fix] In routing proxy, match line beginning Previous approach failed in case of routing table like: { 'domain.com': 'localhost:9000', 'a.domain.com': 'localhost:9001' } without `hostnameOnly`. When routing request to `a.domain.com`, `RegExp` matched first entry (`domain.com`) and returned it. --- lib/node-http-proxy/proxy-table.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/node-http-proxy/proxy-table.js b/lib/node-http-proxy/proxy-table.js index 0cdce50..9035ab8 100644 --- a/lib/node-http-proxy/proxy-table.js +++ b/lib/node-http-proxy/proxy-table.js @@ -97,7 +97,7 @@ ProxyTable.prototype.setRoutes = function (router) { this.routes = []; Object.keys(router).forEach(function (path) { - var route = new RegExp(path, 'i'); + var route = new RegExp('^' + path, 'i'); self.routes.push({ route: route, @@ -137,7 +137,6 @@ ProxyTable.prototype.getProxyLocation = function (req) { for (var i in this.routes) { var route = this.routes[i]; if (target.match(route.route)) { - var pathSegments = route.path.split('/'); if (pathSegments.length > 1) { From 8ca5d83497cc106a2456ff7f2ebe3db5c8634d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 09:32:35 +0200 Subject: [PATCH 09/56] [docs] Little explanation for test/core directory --- test/core/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 test/core/README.md diff --git a/test/core/README.md b/test/core/README.md new file mode 100644 index 0000000..152e5c6 --- /dev/null +++ b/test/core/README.md @@ -0,0 +1,10 @@ +# `test/core` + +`test/core` directory is a place where tests from node.js core go. They are +here to ensure that node-http-proxy works just fine with all kinds of +different situations, which are covered in core tests, but are not covered in +our tests. + +All these tests require little modifications to make them test node-http-proxy, +but we try to keep them as vanilla as possible. + From 543f214361605cffdbee7b233029edf343c358c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 11:43:36 +0200 Subject: [PATCH 10/56] [test] Add common.js file from core Modifications: * add `PROXY_PORT` constant --- test/core/common.js | 145 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 test/core/common.js diff --git a/test/core/common.js b/test/core/common.js new file mode 100644 index 0000000..9bd9dfc --- /dev/null +++ b/test/core/common.js @@ -0,0 +1,145 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 path = require('path'); +var assert = require('assert'); + +exports.testDir = path.dirname(__filename); +exports.fixturesDir = path.join(exports.testDir, 'fixtures'); +exports.libDir = path.join(exports.testDir, '../lib'); +exports.tmpDir = path.join(exports.testDir, 'tmp'); +exports.PORT = 12346; +exports.PROXY_PORT = 1234567; + +if (process.platform == 'win32') { + exports.PIPE = '\\\\.\\pipe\\libuv-test'; +} else { + exports.PIPE = exports.tmpDir + '/test.sock'; +} + +var util = require('util'); +for (var i in util) exports[i] = util[i]; +//for (var i in exports) global[i] = exports[i]; + +function protoCtrChain(o) { + var result = []; + for (; o; o = o.__proto__) { result.push(o.constructor); } + return result.join(); +} + +exports.indirectInstanceOf = function(obj, cls) { + if (obj instanceof cls) { return true; } + var clsChain = protoCtrChain(cls.prototype); + var objChain = protoCtrChain(obj); + return objChain.slice(-clsChain.length) === clsChain; +}; + + +exports.ddCommand = function(filename, kilobytes) { + if (process.platform == 'win32') { + return '"' + process.argv[0] + '" "' + path.resolve(exports.fixturesDir, + 'create-file.js') + '" "' + filename + '" ' + (kilobytes * 1024); + } else { + return 'dd if=/dev/zero of="' + filename + '" bs=1024 count=' + kilobytes; + } +}; + + +exports.spawnPwd = function(options) { + var spawn = require('child_process').spawn; + + if (process.platform == 'win32') { + return spawn('cmd.exe', ['/c', 'cd'], options); + } else { + return spawn('pwd', [], options); + } +}; + + +// Turn this off if the test should not check for global leaks. +exports.globalCheck = true; + +process.on('exit', function() { + if (!exports.globalCheck) return; + var knownGlobals = [setTimeout, + setInterval, + clearTimeout, + clearInterval, + console, + Buffer, + process, + global]; + + if (global.errno) { + knownGlobals.push(errno); + } + + if (global.gc) { + knownGlobals.push(gc); + } + + if (global.DTRACE_HTTP_SERVER_RESPONSE) { + knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE); + knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST); + knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE); + knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST); + knownGlobals.push(DTRACE_NET_STREAM_END); + knownGlobals.push(DTRACE_NET_SERVER_CONNECTION); + knownGlobals.push(DTRACE_NET_SOCKET_READ); + knownGlobals.push(DTRACE_NET_SOCKET_WRITE); + } + + if (global.ArrayBuffer) { + knownGlobals.push(ArrayBuffer); + knownGlobals.push(Int8Array); + knownGlobals.push(Uint8Array); + knownGlobals.push(Int16Array); + knownGlobals.push(Uint16Array); + knownGlobals.push(Int32Array); + knownGlobals.push(Uint32Array); + knownGlobals.push(Float32Array); + knownGlobals.push(Float64Array); + knownGlobals.push(DataView); + } + + for (var x in global) { + var found = false; + + for (var y in knownGlobals) { + if (global[x] === knownGlobals[y]) { + found = true; + break; + } + } + + if (!found) { + console.error('Unknown global: %s', x); + assert.ok(false, 'Unknown global found'); + } + } +}); + + +// This function allows one two run an HTTP test agaist both HTTPS and +// normal HTTP modules. This ensures they fit the same API. +exports.httpTest = function httpTest(cb) { +}; + From 87999d028880dfccca349c9c44f9e66a613c4d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 11:47:14 +0200 Subject: [PATCH 11/56] [test] Add basic test runner --- test/core/run | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100755 test/core/run diff --git a/test/core/run b/test/core/run new file mode 100755 index 0000000..d016589 --- /dev/null +++ b/test/core/run @@ -0,0 +1,57 @@ +#!/usr/bin/env node +/* + run.js: test runner for core tests + + 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 + "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. + +*/ + +// +// Basic idea behind core test runner is to modify core tests as little as +// possible. That's why we start up node-http-proxy here instead of embeeding +// this code in tests. +// +// In most cases only modification to core tests you'll need is changing port +// of http client to common.PROXY_PORT. +// + +var path = require('path'), + httpProxy = require('../../'), + common = require('./common'); + +var test = process.argv[2]; + +if (!test) { + return console.error('Need test to run'); +} + +console.log('Running test ' + test); + +var proxy = httpProxy.createServer(common.PORT, 'localhost'); +proxy.listen(common.PROXY_PORT); + +proxy.on('listening', function () { + require(path.join(process.cwd(), process.argv[2])); +}); + +// vim:filetype=javascript + From 31a8c6800ddf8d91b477d980605a4c19284a1648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:23:51 +0200 Subject: [PATCH 12/56] [fix test] Make test runner exit after test exits --- test/core/run | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/core/run b/test/core/run index d016589..ff20a9e 100755 --- a/test/core/run +++ b/test/core/run @@ -35,6 +35,7 @@ // var path = require('path'), + spawn = require('child_process').spawn, httpProxy = require('../../'), common = require('./common'); @@ -50,7 +51,11 @@ var proxy = httpProxy.createServer(common.PORT, 'localhost'); proxy.listen(common.PROXY_PORT); proxy.on('listening', function () { - require(path.join(process.cwd(), process.argv[2])); + console.log('Proxy server listening on ' + common.PROXY_PORT); + var testProcess = spawn(process.argv[0], [ process.argv[2] ]); + testProcess.stdout.pipe(process.stdout); + testProcess.stderr.pipe(process.stderr); + testProcess.on('exit', process.exit); }); // vim:filetype=javascript From 82060a53430de05f2dc95450d8487bc8139544d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:24:42 +0200 Subject: [PATCH 13/56] [test] Add core `test-http-status-code` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-status-code.js | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 test/core/simple/test-http-status-code.js diff --git a/test/core/simple/test-http-status-code.js b/test/core/simple/test-http-status-code.js new file mode 100644 index 0000000..b79e4d7 --- /dev/null +++ b/test/core/simple/test-http-status-code.js @@ -0,0 +1,69 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +// libuv-broken + + +var common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +// Simple test of Node's HTTP ServerResponse.statusCode +// ServerResponse.prototype.statusCode + +var testsComplete = 0; +var tests = [200, 202, 300, 404, 500]; +var testIdx = 0; + +var s = http.createServer(function(req, res) { + var t = tests[testIdx]; + res.writeHead(t, {'Content-Type': 'text/plain'}); + console.log('--\nserver: statusCode after writeHead: ' + res.statusCode); + assert.equal(res.statusCode, t); + res.end('hello world\n'); +}); + +s.listen(common.PORT, nextTest); + + +function nextTest() { + if (testIdx + 1 === tests.length) { + return s.close(); + } + var test = tests[testIdx]; + + http.get({ port: common.PROXY_PORT }, function(response) { + console.log('client: expected status: ' + test); + console.log('client: statusCode: ' + response.statusCode); + assert.equal(response.statusCode, test); + response.on('end', function() { + testsComplete++; + testIdx += 1; + nextTest(); + }); + }); +} + + +process.on('exit', function() { + assert.equal(4, testsComplete); +}); + From b3b5cce3aee98a7fd5b50fb8e1bd6bd5e1c7512f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 14/56] [test] Add core `test-http-set-cookies` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-set-cookies.js | 84 +++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 test/core/simple/test-http-set-cookies.js diff --git a/test/core/simple/test-http-set-cookies.js b/test/core/simple/test-http-set-cookies.js new file mode 100644 index 0000000..9510e49 --- /dev/null +++ b/test/core/simple/test-http-set-cookies.js @@ -0,0 +1,84 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var nresponses = 0; + +var server = http.createServer(function(req, res) { + if (req.url == '/one') { + res.writeHead(200, [['set-cookie', 'A'], + ['content-type', 'text/plain']]); + res.end('one\n'); + } else { + res.writeHead(200, [['set-cookie', 'A'], + ['set-cookie', 'B'], + ['content-type', 'text/plain']]); + res.end('two\n'); + } +}); +server.listen(common.PORT); + +server.on('listening', function() { + // + // one set-cookie header + // + http.get({ port: common.PROXY_PORT, path: '/one' }, function(res) { + // set-cookie headers are always return in an array. + // even if there is only one. + assert.deepEqual(['A'], res.headers['set-cookie']); + assert.equal('text/plain', res.headers['content-type']); + + res.on('data', function(chunk) { + console.log(chunk.toString()); + }); + + res.on('end', function() { + if (++nresponses == 2) { + server.close(); + } + }); + }); + + // two set-cookie headers + + http.get({ port: common.PROXY_PORT, path: '/two' }, function(res) { + assert.deepEqual(['A', 'B'], res.headers['set-cookie']); + assert.equal('text/plain', res.headers['content-type']); + + res.on('data', function(chunk) { + console.log(chunk.toString()); + }); + + res.on('end', function() { + if (++nresponses == 2) { + server.close(); + } + }); + }); + +}); + +process.on('exit', function() { + assert.equal(2, nresponses); +}); From c0857f2d59c33d91cb3e0c131c44ec1667f592fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:43:14 +0200 Subject: [PATCH 15/56] [test] Add core `test-http-head-request` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-head-request.js | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 test/core/simple/test-http-head-request.js diff --git a/test/core/simple/test-http-head-request.js b/test/core/simple/test-http-head-request.js new file mode 100644 index 0000000..409698a --- /dev/null +++ b/test/core/simple/test-http-head-request.js @@ -0,0 +1,56 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); +var util = require('util'); + + +var body = 'hello world\n'; + +var server = http.createServer(function(req, res) { + common.error('req: ' + req.method); + res.writeHead(200, {'Content-Length': body.length}); + res.end(); + server.close(); +}); + +var gotEnd = false; + +server.listen(common.PORT, function() { + var request = http.request({ + port: common.PROXY_PORT, + method: 'HEAD', + path: '/' + }, function(response) { + common.error('response start'); + response.on('end', function() { + common.error('response end'); + gotEnd = true; + }); + }); + request.end(); +}); + +process.on('exit', function() { + assert.ok(gotEnd); +}); From d7461f3206cca0691fbd438545ff325589770627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 16/56] [test] Add core `test-http-chunked` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-chunked.js | 63 +++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 test/core/simple/test-http-chunked.js diff --git a/test/core/simple/test-http-chunked.js b/test/core/simple/test-http-chunked.js new file mode 100644 index 0000000..8a19f57 --- /dev/null +++ b/test/core/simple/test-http-chunked.js @@ -0,0 +1,63 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var UTF8_STRING = '南越国是前203年至前111年存在于岭南地区的一个国家,' + + '国都位于番禺,疆域包括今天中国的广东、广西两省区的大部份地区,福建省、湖南、' + + '贵州、云南的一小部份地区和越南的北部。南越国是秦朝灭亡后,' + + '由南海郡尉赵佗于前203年起兵兼并桂林郡和象郡后建立。前196年和前179年,' + + '南越国曾先后两次名义上臣属于西汉,成为西汉的“外臣”。前112年,' + + '南越国末代君主赵建德与西汉发生战争,被汉武帝于前111年所灭。' + + '南越国共存在93年,历经五代君主。南越国是岭南地区的第一个有记载的政权国家,' + + '采用封建制和郡县制并存的制度,它的建立保证了秦末乱世岭南地区社会秩序的稳定,' + + '有效的改善了岭南地区落后的政治、经济现状。'; + +var server = http.createServer(function(req, res) { + res.writeHead(200, {'Content-Type': 'text/plain; charset=utf8'}); + res.end(UTF8_STRING, 'utf8'); +}); +server.listen(common.PORT, function() { + var data = ''; + var get = http.get({ + path: '/', + host: 'localhost', + port: common.PROXY_PORT + }, function(x) { + x.setEncoding('utf8'); + x.on('data', function(c) {data += c}); + x.on('error', function(e) { + throw e; + }); + x.on('end', function() { + assert.equal('string', typeof data); + console.log('here is the response:'); + assert.equal(UTF8_STRING, data); + console.log(data); + server.close(); + }); + }); + get.on('error', function(e) {throw e}); + get.end(); + +}); From 98bbe541e4fa581f1b9e2eadb821c0609da6ab81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 17/56] [test] Add core `test-http-client-abort2` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-client-abort2.js | 41 +++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 test/core/simple/test-http-client-abort2.js diff --git a/test/core/simple/test-http-client-abort2.js b/test/core/simple/test-http-client-abort2.js new file mode 100644 index 0000000..41c62d9 --- /dev/null +++ b/test/core/simple/test-http-client-abort2.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +// libuv-broken + + +var common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var server = http.createServer(function(req, res) { + res.end('Hello'); +}); + +server.listen(common.PORT, function() { + var req = http.get({port: common.PROXY_PORT}, function(res) { + res.on('data', function(data) { + req.abort(); + server.close(); + }); + }); +}); + From 60ff181af9c22405d3822ce5955f178ab13de79d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 18/56] [test] Add core `test-http-upload-timeout` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/pummel/test-http-upload-timeout.js | 69 ++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 test/core/pummel/test-http-upload-timeout.js diff --git a/test/core/pummel/test-http-upload-timeout.js b/test/core/pummel/test-http-upload-timeout.js new file mode 100644 index 0000000..f8efe41 --- /dev/null +++ b/test/core/pummel/test-http-upload-timeout.js @@ -0,0 +1,69 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +// This tests setTimeout() by having multiple clients connecting and sending +// data in random intervals. Clients are also randomly disconnecting until there +// are no more clients left. If no false timeout occurs, this test has passed. +var common = require('../common'), + assert = require('assert'), + http = require('http'), + server = http.createServer(), + connections = 0; + +server.on('request', function(req, res) { + req.socket.setTimeout(1000); + req.socket.on('timeout', function() { + throw new Error('Unexpected timeout'); + }); + req.on('end', function() { + connections--; + res.writeHead(200); + res.end('done\n'); + if (connections == 0) { + server.close(); + } + }); +}); + +server.listen(common.PORT, '127.0.0.1', function() { + for (var i = 0; i < 10; i++) { + connections++; + + setTimeout(function() { + var request = http.request({ + port: common.PROXY_PORT, + method: 'POST', + path: '/' + }); + + function ping() { + var nextPing = (Math.random() * 900).toFixed(); + if (nextPing > 600) { + request.end(); + return; + } + request.write('ping'); + setTimeout(ping, nextPing); + } + ping(); + }, i * 50); + } +}); From 25a9e2d217cabef07d6f161f5d6ded49342dbb2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 19/56] [test] Add core `test-http` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http.js | 108 ++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 test/core/simple/test-http.js diff --git a/test/core/simple/test-http.js b/test/core/simple/test-http.js new file mode 100644 index 0000000..fcbec28 --- /dev/null +++ b/test/core/simple/test-http.js @@ -0,0 +1,108 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); +var url = require('url'); + +function p(x) { + common.error(common.inspect(x)); +} + +var responses_sent = 0; +var responses_recvd = 0; +var body0 = ''; +var body1 = ''; + +var server = http.Server(function(req, res) { + if (responses_sent == 0) { + assert.equal('GET', req.method); + assert.equal('/hello', url.parse(req.url).pathname); + + console.dir(req.headers); + assert.equal(true, 'accept' in req.headers); + assert.equal('*/*', req.headers['accept']); + + assert.equal(true, 'foo' in req.headers); + assert.equal('bar', req.headers['foo']); + } + + if (responses_sent == 1) { + assert.equal('POST', req.method); + assert.equal('/world', url.parse(req.url).pathname); + this.close(); + } + + req.on('end', function() { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.write('The path was ' + url.parse(req.url).pathname); + res.end(); + responses_sent += 1; + }); + + //assert.equal('127.0.0.1', res.connection.remoteAddress); +}); +server.listen(common.PORT); + +server.on('listening', function() { + var agent = new http.Agent({ port: common.PROXY_PORT, maxSockets: 1 }); + http.get({ + port: common.PROXY_PORT, + path: '/hello', + headers: {'Accept': '*/*', 'Foo': 'bar'}, + agent: agent + }, function(res) { + assert.equal(200, res.statusCode); + responses_recvd += 1; + res.setEncoding('utf8'); + res.on('data', function(chunk) { body0 += chunk; }); + common.debug('Got /hello response'); + }); + + setTimeout(function() { + var req = http.request({ + port: common.PROXY_PORT, + method: 'POST', + path: '/world', + agent: agent + }, function(res) { + assert.equal(200, res.statusCode); + responses_recvd += 1; + res.setEncoding('utf8'); + res.on('data', function(chunk) { body1 += chunk; }); + common.debug('Got /world response'); + }); + req.end(); + }, 1); +}); + +process.on('exit', function() { + common.debug('responses_recvd: ' + responses_recvd); + assert.equal(2, responses_recvd); + + common.debug('responses_sent: ' + responses_sent); + assert.equal(2, responses_sent); + + assert.equal('The path was /hello', body0); + assert.equal('The path was /world', body1); +}); + From 7648fe50c1859597dc390e9e628db938372483e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 20/56] [test] Add core `test-http-client-upload` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-client-upload.js | 77 +++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 test/core/simple/test-http-client-upload.js diff --git a/test/core/simple/test-http-client-upload.js b/test/core/simple/test-http-client-upload.js new file mode 100644 index 0000000..fdd88ca --- /dev/null +++ b/test/core/simple/test-http-client-upload.js @@ -0,0 +1,77 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var sent_body = ''; +var server_req_complete = false; +var client_res_complete = false; + +var server = http.createServer(function(req, res) { + assert.equal('POST', req.method); + req.setEncoding('utf8'); + + req.on('data', function(chunk) { + console.log('server got: ' + JSON.stringify(chunk)); + sent_body += chunk; + }); + + req.on('end', function() { + server_req_complete = true; + console.log('request complete from server'); + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.write('hello\n'); + res.end(); + }); +}); +server.listen(common.PORT); + +server.on('listening', function() { + var req = http.request({ + port: common.PROXY_PORT, + method: 'POST', + path: '/' + }, function(res) { + res.setEncoding('utf8'); + res.on('data', function(chunk) { + console.log(chunk); + }); + res.on('end', function() { + client_res_complete = true; + server.close(); + }); + }); + + req.write('1\n'); + req.write('2\n'); + req.write('3\n'); + req.end(); + + common.error('client finished sending request'); +}); + +process.on('exit', function() { + assert.equal('1\n2\n3\n', sent_body); + assert.equal(true, server_req_complete); + assert.equal(true, client_res_complete); +}); From a6353897cdbe8c380d52a060f5e66784f67ad98e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 21/56] [test] Add core `test-http-malformed-request` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- .../simple/test-http-malformed-request.js | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/core/simple/test-http-malformed-request.js diff --git a/test/core/simple/test-http-malformed-request.js b/test/core/simple/test-http-malformed-request.js new file mode 100644 index 0000000..d453ddc --- /dev/null +++ b/test/core/simple/test-http-malformed-request.js @@ -0,0 +1,57 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var net = require('net'); +var http = require('http'); +var url = require('url'); + +// Make sure no exceptions are thrown when receiving malformed HTTP +// requests. + +var nrequests_completed = 0; +var nrequests_expected = 1; + +var server = http.createServer(function(req, res) { + console.log('req: ' + JSON.stringify(url.parse(req.url))); + + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.write('Hello World'); + res.end(); + + if (++nrequests_completed == nrequests_expected) server.close(); +}); +server.listen(common.PORT); + +server.on('listening', function() { + var c = net.createConnection(common.PROXY_PORT); + c.on('connect', function() { + c.write('GET /hello?foo=%99bar HTTP/1.1\r\n\r\n'); + c.end(); + }); + + // TODO add more! +}); + +process.on('exit', function() { + assert.equal(nrequests_expected, nrequests_completed); +}); From 80c216df0cc59b88c6934f795c03ea16a737af34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 22/56] [test] Add core `test-http-eof-on-connect` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-eof-on-connect.js | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/core/simple/test-http-eof-on-connect.js diff --git a/test/core/simple/test-http-eof-on-connect.js b/test/core/simple/test-http-eof-on-connect.js new file mode 100644 index 0000000..b043a2d --- /dev/null +++ b/test/core/simple/test-http-eof-on-connect.js @@ -0,0 +1,40 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var net = require('net'); +var http = require('http'); + +// This is a regression test for http://github.com/ry/node/issues/#issue/44 +// It is separate from test-http-malformed-request.js because it is only +// reproduceable on the first packet on the first connection to a server. + +var server = http.createServer(function(req, res) {}); +server.listen(common.PORT); + +server.on('listening', function() { + net.createConnection(common.PROXY_PORT).on('connect', function() { + this.destroy(); + }).on('close', function() { + server.close(); + }); +}); From 4d43d81e5c2d7c8088716d4fd574019f43ebb5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Sat, 29 Oct 2011 18:11:05 +0200 Subject: [PATCH 23/56] [fix] When client request is aborted, abort server request --- lib/node-http-proxy/http-proxy.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/node-http-proxy/http-proxy.js b/lib/node-http-proxy/http-proxy.js index 83a2bf1..bb4f453 100644 --- a/lib/node-http-proxy/http-proxy.js +++ b/lib/node-http-proxy/http-proxy.js @@ -322,6 +322,13 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) { // reverseProxy.once('error', proxyError); + // + // If `req` is aborted, we abort our `reverseProxy` request as well. + // + req.on('aborted', function () { + reverseProxy.abort(); + }); + // // For each data `chunk` received from the incoming // `req` write it to the `reverseProxy` request. From 7bf8d4a7be668591b350144b4546559abf9a0b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 24/56] [test] Add core `test-http-client-abort` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` As off a95cf9bd63, this test doesn't hang. --- test/core/simple/test-http-client-abort.js | 80 ++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 test/core/simple/test-http-client-abort.js diff --git a/test/core/simple/test-http-client-abort.js b/test/core/simple/test-http-client-abort.js new file mode 100644 index 0000000..c7a55e2 --- /dev/null +++ b/test/core/simple/test-http-client-abort.js @@ -0,0 +1,80 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var clientAborts = 0; + +var server = http.Server(function(req, res) { + console.log('Got connection'); + res.writeHead(200); + res.write('Working on it...'); + + // I would expect an error event from req or res that the client aborted + // before completing the HTTP request / response cycle, or maybe a new + // event like "aborted" or something. + req.on('aborted', function() { + clientAborts++; + console.log('Got abort ' + clientAborts); + if (clientAborts === N) { + console.log('All aborts detected, you win.'); + server.close(); + } + }); + + // since there is already clientError, maybe that would be appropriate, + // since "error" is magical + req.on('clientError', function() { + console.log('Got clientError'); + }); +}); + +var responses = 0; +var N = http.Agent.defaultMaxSockets - 1; +var requests = []; + +server.listen(common.PORT, function() { + console.log('Server listening.'); + + for (var i = 0; i < N; i++) { + console.log('Making client ' + i); + var options = { port: common.PROXY_PORT, path: '/?id=' + i }; + var req = http.get(options, function(res) { + console.log('Client response code ' + res.statusCode); + + if (++responses == N) { + console.log('All clients connected, destroying.'); + requests.forEach(function(outReq) { + console.log('abort'); + outReq.abort(); + }); + } + }); + + requests.push(req); + } +}); + +process.on('exit', function() { + assert.equal(N, clientAborts); +}); From f1c0be3f0bd2c5e87d44a37ba4f29aafd9903ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 25/56] [test] Add core `test-http-response-close` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-response-close.js | 55 ++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test/core/simple/test-http-response-close.js diff --git a/test/core/simple/test-http-response-close.js b/test/core/simple/test-http-response-close.js new file mode 100644 index 0000000..d4adf80 --- /dev/null +++ b/test/core/simple/test-http-response-close.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var gotEnd = false; + +var server = http.createServer(function(req, res) { + res.writeHead(200); + res.write('a'); + + req.on('close', function() { + console.error('aborted'); + gotEnd = true; + }); +}); +server.listen(common.PORT); + +server.on('listening', function() { + console.error('make req'); + http.get({ + port: common.PROXY_PORT + }, function(res) { + console.error('got res'); + res.on('data', function(data) { + console.error('destroy res'); + res.destroy(); + server.close(); + }); + }); +}); + +process.on('exit', function() { + assert.ok(gotEnd); +}); From 5ac987857c934d07073b853f5243d2d8fc6d8c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 26/56] [test] Add core `test-http-client-upload-buf` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- .../simple/test-http-client-upload-buf.js | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 test/core/simple/test-http-client-upload-buf.js diff --git a/test/core/simple/test-http-client-upload-buf.js b/test/core/simple/test-http-client-upload-buf.js new file mode 100644 index 0000000..125aa0c --- /dev/null +++ b/test/core/simple/test-http-client-upload-buf.js @@ -0,0 +1,74 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var N = 1024; +var bytesRecieved = 0; +var server_req_complete = false; +var client_res_complete = false; + +var server = http.createServer(function(req, res) { + assert.equal('POST', req.method); + + req.on('data', function(chunk) { + bytesRecieved += chunk.length; + }); + + req.on('end', function() { + server_req_complete = true; + console.log('request complete from server'); + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.write('hello\n'); + res.end(); + }); +}); +server.listen(common.PORT); + +server.on('listening', function() { + var req = http.request({ + port: common.PROXY_PORT, + method: 'POST', + path: '/' + }, function(res) { + res.setEncoding('utf8'); + res.on('data', function(chunk) { + console.log(chunk); + }); + res.on('end', function() { + client_res_complete = true; + server.close(); + }); + }); + + req.write(new Buffer(N)); + req.end(); + + common.error('client finished sending request'); +}); + +process.on('exit', function() { + assert.equal(N, bytesRecieved); + assert.equal(true, server_req_complete); + assert.equal(true, client_res_complete); +}); From f298411f76a106791f34dd4d31ea033a7bdca9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 27/56] [test] Add core `test-http-host-headers` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` * remove https part * change assertion to match `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-host-headers.js | 101 +++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 test/core/simple/test-http-host-headers.js diff --git a/test/core/simple/test-http-host-headers.js b/test/core/simple/test-http-host-headers.js new file mode 100644 index 0000000..bb0957e --- /dev/null +++ b/test/core/simple/test-http-host-headers.js @@ -0,0 +1,101 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +// libuv-broken + + +var http = require('http'), + common = require('../common'), + assert = require('assert'), + httpServer = http.createServer(reqHandler); + +function reqHandler(req, res) { + console.log('Got request: ' + req.headers.host + ' ' + req.url); + if (req.url === '/setHostFalse5') { + assert.equal(req.headers.host, undefined); + } else { + assert.equal(req.headers.host, 'localhost:' + common.PROXY_PORT, + 'Wrong host header for req[' + req.url + ']: ' + + req.headers.host); + } + res.writeHead(200, {}); + //process.nextTick(function() { res.end('ok'); }); + res.end('ok'); +} + +function thrower(er) { + throw er; +} + +testHttp(); + +function testHttp() { + + console.log('testing http on port ' + common.PROXY_PORT + ' (proxied to ' + + common.PORT + ')'); + + var counter = 0; + + function cb() { + counter--; + console.log('back from http request. counter = ' + counter); + if (counter === 0) { + httpServer.close(); + } + } + + httpServer.listen(common.PORT, function(er) { + console.error('listening on ' + common.PORT); + + if (er) throw er; + + http.get({ method: 'GET', + path: '/' + (counter++), + host: 'localhost', + //agent: false, + port: common.PROXY_PORT }, cb).on('error', thrower); + + http.request({ method: 'GET', + path: '/' + (counter++), + host: 'localhost', + //agent: false, + port: common.PROXY_PORT }, cb).on('error', thrower).end(); + + http.request({ method: 'POST', + path: '/' + (counter++), + host: 'localhost', + //agent: false, + port: common.PROXY_PORT }, cb).on('error', thrower).end(); + + http.request({ method: 'PUT', + path: '/' + (counter++), + host: 'localhost', + //agent: false, + port: common.PROXY_PORT }, cb).on('error', thrower).end(); + + http.request({ method: 'DELETE', + path: '/' + (counter++), + host: 'localhost', + //agent: false, + port: common.PROXY_PORT }, cb).on('error', thrower).end(); + }); +} + From f79f3adf0295ec5bb7fb9f6525b48ba5209d04c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 28/56] [test] Add core `test-http-head-response-has-no-body` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- .../test-http-head-response-has-no-body.js | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 test/core/simple/test-http-head-response-has-no-body.js diff --git a/test/core/simple/test-http-head-response-has-no-body.js b/test/core/simple/test-http-head-response-has-no-body.js new file mode 100644 index 0000000..614b648 --- /dev/null +++ b/test/core/simple/test-http-head-response-has-no-body.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); + +var http = require('http'); + +// This test is to make sure that when the HTTP server +// responds to a HEAD request, it does not send any body. +// In this case it was sending '0\r\n\r\n' + +var server = http.createServer(function(req, res) { + res.writeHead(200); // broken: defaults to TE chunked + res.end(); +}); +server.listen(common.PORT); + +var responseComplete = false; + +server.on('listening', function() { + var req = http.request({ + port: common.PROXY_PORT, + method: 'HEAD', + path: '/' + }, function(res) { + common.error('response'); + res.on('end', function() { + common.error('response end'); + server.close(); + responseComplete = true; + }); + }); + common.error('req'); + req.end(); +}); + +process.on('exit', function() { + assert.ok(responseComplete); +}); From 13389db1bef38a7fc7ddc3ada479a608f033020c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 29/56] [test] Add core `test-http-head-response-has-no-body-end` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- ...test-http-head-response-has-no-body-end.js | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 test/core/simple/test-http-head-response-has-no-body-end.js diff --git a/test/core/simple/test-http-head-response-has-no-body-end.js b/test/core/simple/test-http-head-response-has-no-body-end.js new file mode 100644 index 0000000..9a50e5a --- /dev/null +++ b/test/core/simple/test-http-head-response-has-no-body-end.js @@ -0,0 +1,61 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +// libuv-broken + + +var common = require('../common'); +var assert = require('assert'); + +var http = require('http'); + +// This test is to make sure that when the HTTP server +// responds to a HEAD request with data to res.end, +// it does not send any body. + +var server = http.createServer(function(req, res) { + res.writeHead(200); + res.end('FAIL'); // broken: sends FAIL from hot path. +}); +server.listen(common.PORT); + +var responseComplete = false; + +server.on('listening', function() { + var req = http.request({ + port: common.PROXY_PORT, + method: 'HEAD', + path: '/' + }, function(res) { + common.error('response'); + res.on('end', function() { + common.error('response end'); + server.close(); + responseComplete = true; + }); + }); + common.error('req'); + req.end(); +}); + +process.on('exit', function() { + assert.ok(responseComplete); +}); From 4e1ca6e61899b11cad1b437cc9d9490b9d856665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 30/56] [test] Add core `test-http-many-keep-alive-connections` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` * `process.exit` instead of `server.close` (process wouldn't exit otherwise, I'm not sure why) --- .../test-http-many-keep-alive-connections.js | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 test/core/simple/test-http-many-keep-alive-connections.js diff --git a/test/core/simple/test-http-many-keep-alive-connections.js b/test/core/simple/test-http-many-keep-alive-connections.js new file mode 100644 index 0000000..7b14ff0 --- /dev/null +++ b/test/core/simple/test-http-many-keep-alive-connections.js @@ -0,0 +1,68 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var expected = 10000; +var responses = 0; +var requests = 0; +var connection; + +var server = http.Server(function(req, res) { + requests++; + assert.equal(req.connection, connection); + res.writeHead(200); + res.end('hello world\n'); +}); + +server.once('connection', function(c) { + connection = c; +}); + +server.listen(common.PORT, function() { + var callee = arguments.callee; + var request = http.get({ + port: common.PROXY_PORT, + path: '/', + headers: { + 'Connection': 'Keep-alive' + } + }, function(res) { + res.on('end', function() { + if (++responses < expected) { + callee(); + } else { + process.exit(); + } + }); + }).on('error', function(e) { + console.log(e.message); + process.exit(1); + }); + request.agent.maxSockets = 1; +}); + +process.on('exit', function() { + assert.equal(expected, responses); + assert.equal(expected, requests); +}); From 004be38048792d6f1d3efb361a5e7e66d5dbee8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Mon, 31 Oct 2011 22:03:34 +0100 Subject: [PATCH 31/56] [test refactor] `test/core/{run => run-single}` --- test/core/{run => run-single} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/core/{run => run-single} (100%) diff --git a/test/core/run b/test/core/run-single similarity index 100% rename from test/core/run rename to test/core/run-single From 2ccc5c73eaef30ab5a2af7e456bfcc270583c460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Mon, 31 Oct 2011 22:05:08 +0100 Subject: [PATCH 32/56] [test minor] Update copyright notice on test runner --- test/core/run-single | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/run-single b/test/core/run-single index ff20a9e..ff1af48 100755 --- a/test/core/run-single +++ b/test/core/run-single @@ -1,8 +1,8 @@ #!/usr/bin/env node /* - run.js: test runner for core tests + run-single.js: test runner for core tests - Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Marak Squires, Fedor Indutny + Copyright (c) 2011 Nodejitsu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From a4079c6a1c8b87334d12d47d67f060cbb1214696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Mon, 31 Oct 2011 22:09:20 +0100 Subject: [PATCH 33/56] [test] Implement basic runner for multiple tests Features: * timeout * running tests in separate processes * printing basic summary --- test/core/run | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100755 test/core/run diff --git a/test/core/run b/test/core/run new file mode 100755 index 0000000..df20bc5 --- /dev/null +++ b/test/core/run @@ -0,0 +1,71 @@ +#!/usr/bin/env node +/* + run.js: test runner for core tests + + Copyright (c) 2011 Nodejitsu + + 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 path = require('path'), + spawn = require('child_process').spawn, + async = require('async'), + colors = require('colors'); + +var testTimeout = 15000; +var results = {}; + +function runTest(test, callback) { + var child = spawn(path.join(__dirname, 'run-single'), [ test ]); + + var killTimeout = setTimeout(function () { + child.kill(); + console.log(test.yellow + ' timed out'.red); + }, testTimeout); + + child.on('exit', function (exitCode) { + clearTimeout(killTimeout); + + console.log(test.yellow + ' exited with ' + + ((exitCode) ? exitCode.toString().red : exitCode.toString().green)); + results[test] = { exitCode: exitCode }; + callback(); + // + // We don't want tests to be stopped after first failure, and that's what + // async does when it receives truthy value in callback. + // + }); +}; + +var tests = process.argv.slice(2); +async.forEachSeries(tests, runTest, function () { + var failed = [], ok = []; + for (var test in results) { + (results[test].exitCode != 0 ? failed : ok).push(test); + } + + console.log('\nSummary:'); + console.log((' ' + failed.length + '\tfailed tests').red); + console.log((' ' + ok.length + '\tpassed tests').green); +}); + +// vim:filetype=javascript + From 219b0ff8f8780cde4714267273b0a1637c84679f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Mon, 31 Oct 2011 22:11:20 +0100 Subject: [PATCH 34/56] [dist] Test runner depends on `async` --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 775c029..d5d61c6 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "devDependencies": { "request": "1.9.x", "vows": "0.5.x", + "async": "0.1.x", "socket.io": "0.6.x" }, "main": "./lib/node-http-proxy", From e109eba9724494737021579938c1094c9dfbc8ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Tue, 1 Nov 2011 00:04:37 +0100 Subject: [PATCH 35/56] [minor] When running tests output only basename --- test/core/run | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/run b/test/core/run index df20bc5..e30ed4b 100755 --- a/test/core/run +++ b/test/core/run @@ -38,13 +38,13 @@ function runTest(test, callback) { var killTimeout = setTimeout(function () { child.kill(); - console.log(test.yellow + ' timed out'.red); + console.log(path.basename(test).yellow + ' timed out'.red); }, testTimeout); child.on('exit', function (exitCode) { clearTimeout(killTimeout); - console.log(test.yellow + ' exited with ' + + console.log(path.basename(test).yellow + ' exited with ' + ((exitCode) ? exitCode.toString().red : exitCode.toString().green)); results[test] = { exitCode: exitCode }; callback(); From 68cebbe0e79ea283eea8a1ca850ab462c66c611a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Tue, 1 Nov 2011 00:31:33 +0100 Subject: [PATCH 36/56] [test] Run tests in `test/core/simple` by default --- test/core/run | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/core/run b/test/core/run index e30ed4b..4210052 100755 --- a/test/core/run +++ b/test/core/run @@ -25,7 +25,8 @@ */ -var path = require('path'), +var fs = require('fs'), + path = require('path'), spawn = require('child_process').spawn, async = require('async'), colors = require('colors'); @@ -56,6 +57,17 @@ function runTest(test, callback) { }; var tests = process.argv.slice(2); + +if (!tests.length) { + var pathPrefix = path.join(__dirname, 'simple'); + tests = fs.readdirSync(pathPrefix).map(function (test) { + return path.join(pathPrefix, test); + }); + // + // We only run simple tests by default. + // +} + async.forEachSeries(tests, runTest, function () { var failed = [], ok = []; for (var test in results) { From b76680b045f69e03759bc119f4827f337a8f395d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Tue, 1 Nov 2011 00:56:25 +0100 Subject: [PATCH 37/56] [minor] Change test runner output order --- test/core/run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/run b/test/core/run index 4210052..fe156fd 100755 --- a/test/core/run +++ b/test/core/run @@ -75,8 +75,8 @@ async.forEachSeries(tests, runTest, function () { } console.log('\nSummary:'); - console.log((' ' + failed.length + '\tfailed tests').red); console.log((' ' + ok.length + '\tpassed tests').green); + console.log((' ' + failed.length + '\tfailed tests').red); }); // vim:filetype=javascript From 38bd906f2bc9322b156b92c47457bb7904f0d23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Tue, 1 Nov 2011 01:56:42 +0100 Subject: [PATCH 38/56] [minor] Everybody loves Unicode Tick and X marks in test runner output. --- test/core/run | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/run b/test/core/run index fe156fd..7784c08 100755 --- a/test/core/run +++ b/test/core/run @@ -45,8 +45,8 @@ function runTest(test, callback) { child.on('exit', function (exitCode) { clearTimeout(killTimeout); - console.log(path.basename(test).yellow + ' exited with ' + - ((exitCode) ? exitCode.toString().red : exitCode.toString().green)); + console.log(' ' + ((exitCode) ? '✘'.red : '✔'.green) + ' ' + + path.basename(test) + (exitCode && ' (exit code: ' + exitCode + ')')); results[test] = { exitCode: exitCode }; callback(); // From 5c3d41bf4e101d0250fb0b3db4a8dc078104dcad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Tue, 1 Nov 2011 02:00:21 +0100 Subject: [PATCH 39/56] [minor] Nicer output from test runner --- test/core/run | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/core/run b/test/core/run index 7784c08..f8b0444 100755 --- a/test/core/run +++ b/test/core/run @@ -45,8 +45,9 @@ function runTest(test, callback) { child.on('exit', function (exitCode) { clearTimeout(killTimeout); - console.log(' ' + ((exitCode) ? '✘'.red : '✔'.green) + ' ' + - path.basename(test) + (exitCode && ' (exit code: ' + exitCode + ')')); + console.log(' ' + ((exitCode) ? '✘'.red : '✔'.green) + ' ' + + path.basename(test) + + (exitCode ? (' (exit code: ' + exitCode + ')') : '')); results[test] = { exitCode: exitCode }; callback(); // @@ -68,13 +69,14 @@ if (!tests.length) { // } +console.log('Running tests:'.bold); async.forEachSeries(tests, runTest, function () { var failed = [], ok = []; for (var test in results) { (results[test].exitCode != 0 ? failed : ok).push(test); } - console.log('\nSummary:'); + console.log('\nSummary:'.bold); console.log((' ' + ok.length + '\tpassed tests').green); console.log((' ' + failed.length + '\tfailed tests').red); }); From 48d4a8b263faa9acda06651bceeff50881f21b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Tue, 1 Nov 2011 12:38:38 +0100 Subject: [PATCH 40/56] [minor] Allow user to set `colors.mode` --- test/core/run | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/core/run b/test/core/run index f8b0444..8f72a9a 100755 --- a/test/core/run +++ b/test/core/run @@ -29,7 +29,10 @@ var fs = require('fs'), path = require('path'), spawn = require('child_process').spawn, async = require('async'), - colors = require('colors'); + colors = require('colors'), + optimist = require('optimist'); + +optimist.argv.color && (colors.mode = optimist.argv.color); var testTimeout = 15000; var results = {}; @@ -57,7 +60,9 @@ function runTest(test, callback) { }); }; -var tests = process.argv.slice(2); +var tests = process.argv.slice(2).filter(function (test) { + return test.substr(0, 2) != '--'; +}); if (!tests.length) { var pathPrefix = path.join(__dirname, 'simple'); From 35d2088c96bacb44b17755176b6e9451ed0299dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 41/56] [test] Add core `test-http-multi-line-headers` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- .../simple/test-http-multi-line-headers.js | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 test/core/simple/test-http-multi-line-headers.js diff --git a/test/core/simple/test-http-multi-line-headers.js b/test/core/simple/test-http-multi-line-headers.js new file mode 100644 index 0000000..aa00a76 --- /dev/null +++ b/test/core/simple/test-http-multi-line-headers.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); + +var http = require('http'); +var net = require('net'); + +var gotResponse = false; + +var server = net.createServer(function(conn) { + var body = 'Yet another node.js server.'; + + var response = + 'HTTP/1.1 200 OK\r\n' + + 'Connection: close\r\n' + + 'Content-Length: ' + body.length + '\r\n' + + 'Content-Type: text/plain;\r\n' + + ' x-unix-mode=0600;\r\n' + + ' name=\"hello.txt\"\r\n' + + '\r\n' + + body; + + conn.write(response, function() { + conn.destroy(); + server.close(); + }); +}); + +server.listen(common.PORT, function() { + http.get({host: '127.0.0.1', port: common.PROXY_PORT}, function(res) { + assert.equal(res.headers['content-type'], + 'text/plain;x-unix-mode=0600;name="hello.txt"'); + gotResponse = true; + }); +}); + +process.on('exit', function() { + assert.ok(gotResponse); +}); From d7f15d02f7477c76529fc76daddee5029079eb2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 42/56] [test] Add core `test-http-server-multiheaders` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- .../simple/test-http-server-multiheaders.js | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 test/core/simple/test-http-server-multiheaders.js diff --git a/test/core/simple/test-http-server-multiheaders.js b/test/core/simple/test-http-server-multiheaders.js new file mode 100644 index 0000000..52b932f --- /dev/null +++ b/test/core/simple/test-http-server-multiheaders.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +// Verify that the HTTP server implementation handles multiple instances +// of the same header as per RFC2616: joining the handful of fields by ', ' +// that support it, and dropping duplicates for other fields. + +var common = require('../common'); +var assert = require('assert'); +var http = require('http'); + +var srv = http.createServer(function(req, res) { + assert.equal(req.headers.accept, 'abc, def, ghijklmnopqrst'); + assert.equal(req.headers.host, 'foo'); + assert.equal(req.headers['x-foo'], 'bingo'); + assert.equal(req.headers['x-bar'], 'banjo, bango'); + + res.writeHead(200, {'Content-Type' : 'text/plain'}); + res.end('EOF'); + + srv.close(); +}); + +srv.listen(common.PORT, function() { + http.get({ + host: 'localhost', + port: common.PROXY_PORT, + path: '/', + headers: [ + ['accept', 'abc'], + ['accept', 'def'], + ['Accept', 'ghijklmnopqrst'], + ['host', 'foo'], + ['Host', 'bar'], + ['hOst', 'baz'], + ['x-foo', 'bingo'], + ['x-bar', 'banjo'], + ['x-bar', 'bango'] + ] + }); +}); From c26ab5e46ff2649f0ea6585f20d8f58b7d0cadef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 43/56] [test] Add core `test-http-extra-response` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-extra-response.js | 86 ++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 test/core/simple/test-http-extra-response.js diff --git a/test/core/simple/test-http-extra-response.js b/test/core/simple/test-http-extra-response.js new file mode 100644 index 0000000..29143d0 --- /dev/null +++ b/test/core/simple/test-http-extra-response.js @@ -0,0 +1,86 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); +var net = require('net'); + +// If an HTTP server is broken and sends data after the end of the response, +// node should ignore it and drop the connection. +// Demos this bug: https://github.com/ry/node/issues/680 + +var body = 'hello world\r\n'; +var fullResponse = + 'HTTP/1.1 500 Internal Server Error\r\n' + + 'Content-Length: ' + body.length + '\r\n' + + 'Content-Type: text/plain\r\n' + + 'Date: Fri + 18 Feb 2011 06:22:45 GMT\r\n' + + 'Host: 10.20.149.2\r\n' + + 'Access-Control-Allow-Credentials: true\r\n' + + 'Server: badly broken/0.1 (OS NAME)\r\n' + + '\r\n' + + body; + +var gotResponse = false; + + +var server = net.createServer(function(socket) { + var postBody = ''; + + socket.setEncoding('utf8'); + + socket.on('data', function(chunk) { + postBody += chunk; + + if (postBody.indexOf('\r\n') > -1) { + socket.write(fullResponse); + // omg, I wrote the response twice, what a terrible HTTP server I am. + socket.end(fullResponse); + } + }); +}); + + +server.listen(common.PORT, function() { + http.get({ port: common.PROXY_PORT }, function(res) { + var buffer = ''; + console.log('Got res code: ' + res.statusCode); + + res.setEncoding('utf8'); + res.on('data', function(chunk) { + buffer += chunk; + }); + + res.on('end', function() { + console.log('Response ended, read ' + buffer.length + ' bytes'); + assert.equal(body, buffer); + server.close(); + gotResponse = true; + }); + }); +}); + + +process.on('exit', function() { + assert.ok(gotResponse); +}); + From 275109b2f8c8519c56ca9f456096d4002698fab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 44/56] [test] Add core `test-http-contentLength0` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-contentLength0.js | 42 ++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test/core/simple/test-http-contentLength0.js diff --git a/test/core/simple/test-http-contentLength0.js b/test/core/simple/test-http-contentLength0.js new file mode 100644 index 0000000..1602c02 --- /dev/null +++ b/test/core/simple/test-http-contentLength0.js @@ -0,0 +1,42 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var http = require('http'); + +// Simple test of Node's HTTP Client choking on a response +// with a 'Content-Length: 0 ' response header. +// I.E. a space character after the 'Content-Length' throws an `error` event. + + +var s = http.createServer(function(req, res) { + res.writeHead(200, {'Content-Length': '0 '}); + res.end(); +}); +s.listen(common.PORT, function() { + + var request = http.request({ port: common.PROXY_PORT }, function(response) { + console.log('STATUS: ' + response.statusCode); + s.close(); + }); + + request.end(); +}); From feb324b0d4c0a2307493b35be944ed08ffc9187a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 45/56] [test] Add core `test-http-proxy` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-proxy.js | 109 ++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 test/core/simple/test-http-proxy.js diff --git a/test/core/simple/test-http-proxy.js b/test/core/simple/test-http-proxy.js new file mode 100644 index 0000000..2bbf9be --- /dev/null +++ b/test/core/simple/test-http-proxy.js @@ -0,0 +1,109 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); +var url = require('url'); + +var PROXY_PORT = common.PORT; +var BACKEND_PORT = common.PORT + 1; + +var cookies = [ + 'session_token=; path=/; expires=Sun, 15-Sep-2030 13:48:52 GMT', + 'prefers_open_id=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT' +]; + +var headers = {'content-type': 'text/plain', + 'set-cookie': cookies, + 'hello': 'world' }; + +var backend = http.createServer(function(req, res) { + common.debug('backend request'); + res.writeHead(200, headers); + res.write('hello world\n'); + res.end(); +}); + +var proxy = http.createServer(function(req, res) { + common.debug('proxy req headers: ' + JSON.stringify(req.headers)); + var proxy_req = http.get({ + port: BACKEND_PORT, + path: url.parse(req.url).pathname + }, function(proxy_res) { + + common.debug('proxy res headers: ' + JSON.stringify(proxy_res.headers)); + + assert.equal('world', proxy_res.headers['hello']); + assert.equal('text/plain', proxy_res.headers['content-type']); + assert.deepEqual(cookies, proxy_res.headers['set-cookie']); + + res.writeHead(proxy_res.statusCode, proxy_res.headers); + + proxy_res.on('data', function(chunk) { + res.write(chunk); + }); + + proxy_res.on('end', function() { + res.end(); + common.debug('proxy res'); + }); + }); +}); + +var body = ''; + +var nlistening = 0; +function startReq() { + nlistening++; + if (nlistening < 2) return; + + var client = http.get({ + port: common.PROXY_PORT, + path: '/test' + }, function(res) { + common.debug('got res'); + assert.equal(200, res.statusCode); + + assert.equal('world', res.headers['hello']); + assert.equal('text/plain', res.headers['content-type']); + assert.deepEqual(cookies, res.headers['set-cookie']); + + res.setEncoding('utf8'); + res.on('data', function(chunk) { body += chunk; }); + res.on('end', function() { + proxy.close(); + backend.close(); + common.debug('closed both'); + }); + }); + common.debug('client req'); +} + +common.debug('listen proxy'); +proxy.listen(PROXY_PORT, startReq); + +common.debug('listen backend'); +backend.listen(BACKEND_PORT, startReq); + +process.on('exit', function() { + assert.equal(body, 'hello world\n'); +}); From bc98c0dbce154ef266eef83d3c2f737a2d60f0e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Fri, 28 Oct 2011 12:34:37 +0200 Subject: [PATCH 46/56] [test] Add core `test-http-upgrade-server2` test Modifications: * make client connect to `PROXY_PORT` instead of `PORT` --- test/core/simple/test-http-upgrade-server2.js | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 test/core/simple/test-http-upgrade-server2.js diff --git a/test/core/simple/test-http-upgrade-server2.js b/test/core/simple/test-http-upgrade-server2.js new file mode 100644 index 0000000..8825f97 --- /dev/null +++ b/test/core/simple/test-http-upgrade-server2.js @@ -0,0 +1,72 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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 common = require('../common'); +var assert = require('assert'); +var http = require('http'); +var net = require('net'); + +var server = http.createServer(function(req, res) { + common.error('got req'); + throw new Error('This shouldn\'t happen.'); +}); + +server.on('upgrade', function(req, socket, upgradeHead) { + common.error('got upgrade event'); + // test that throwing an error from upgrade gets + // is uncaught + throw new Error('upgrade error'); +}); + +var gotError = false; + +process.on('uncaughtException', function(e) { + common.error('got \'clientError\' event'); + assert.equal('upgrade error', e.message); + gotError = true; + process.exit(0); +}); + + +server.listen(common.PORT, function() { + var c = net.createConnection(common.PROXY_PORT); + + c.on('connect', function() { + common.error('client wrote message'); + c.write('GET /blah HTTP/1.1\r\n' + + 'Upgrade: WebSocket\r\n' + + 'Connection: Upgrade\r\n' + + '\r\n\r\nhello world'); + }); + + c.on('end', function() { + c.end(); + }); + + c.on('close', function() { + common.error('client close'); + server.close(); + }); +}); + +process.on('exit', function() { + assert.ok(gotError); +}); From c4124da4f25860497790fc06c97dde6e8985ab73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Tue, 1 Nov 2011 20:19:58 +0100 Subject: [PATCH 47/56] [minor] Fix indent on timeout notice --- test/core/run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/run b/test/core/run index 8f72a9a..adec53b 100755 --- a/test/core/run +++ b/test/core/run @@ -42,7 +42,7 @@ function runTest(test, callback) { var killTimeout = setTimeout(function () { child.kill(); - console.log(path.basename(test).yellow + ' timed out'.red); + console.log(' ' + path.basename(test).yellow + ' timed out'.red); }, testTimeout); child.on('exit', function (exitCode) { From 8358ef8a2bdf817c8ed515be7bc9cec0a9b5f486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ma=C5=82ecki?= Date: Sun, 18 Dec 2011 22:54:08 +0100 Subject: [PATCH 48/56] [test dist] Run core tests on `npm test` --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d5d61c6..0a6b9dc 100644 --- a/package.json +++ b/package.json @@ -28,9 +28,10 @@ "main": "./lib/node-http-proxy", "bin": { "node-http-proxy": "./bin/node-http-proxy" }, "scripts": { - "test": "npm run-script test-http && npm run-script test-https", + "test": "npm run-script test-http && npm run-script test-https && npm run-script test-core", "test-http": "vows --spec && vows --spec --target=secure", - "test-https": "vows --spec --source=secure && vows --spec --source=secure --target=secure" + "test-https": "vows --spec --source=secure && vows --spec --source=secure --target=secure", + "test-core": "test/core/run" }, "engines": { "node": "0.4.x || 0.5.x" } } From ea7fea627255ed34d39902438b55e740c7c9b08c Mon Sep 17 00:00:00 2001 From: indexzero Date: Fri, 23 Dec 2011 01:32:49 -0500 Subject: [PATCH 49/56] [refactor minor] Update vendor/websocket.js to be compatible with node@0.6.x --- vendor/websocket.js | 198 ++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 100 deletions(-) diff --git a/vendor/websocket.js b/vendor/websocket.js index d8c5eeb..406023c 100644 --- a/vendor/websocket.js +++ b/vendor/websocket.js @@ -496,7 +496,7 @@ var WebSocket = function(url, proto, opts) { if (u.protocol === 'ws:' || u.protocol === 'wss:') { protocol = u.protocol === 'ws:' ? http : https; port = u.protocol === 'ws:' ? 80 : 443; - agent = u.protocol === 'ws:' ? protocol.getAgent(u.hostname, u.port || port) : protocol.getAgent({ + agent = u.protocol === new protocol.Agent({ host: u.hostname, port: u.port || port }); @@ -514,105 +514,6 @@ var WebSocket = function(url, proto, opts) { throw new Error('Invalid URL scheme \'' + urlScheme + '\' specified.'); } - if (!agent._events || agent._events['upgrade'].length === 0) { - agent.on('upgrade', (function() { - var data = undefined; - - return function(res, s, head) { - stream = s; - - // - // Emit the `wsupgrade` event to inspect the raw - // arguments returned from the websocket request. - // - self.emit('wsupgrade', httpHeaders, res, s, head); - - stream.on('data', function(d) { - if (d.length <= 0) { - return; - } - - if (!data) { - data = d; - } else { - var data2 = new buffer.Buffer(data.length + d.length); - - data.copy(data2, 0, 0, data.length); - d.copy(data2, data.length, 0, d.length); - - data = data2; - } - - if (data.length >= 16) { - var expected = computeSecretKeySignature(key1, key2, challenge); - var actual = data.slice(0, 16).toString('binary'); - - // Handshaking fails; we're donezo - if (actual != expected) { - debug( - 'expected=\'' + str2hex(expected) + '\'; ' + - 'actual=\'' + str2hex(actual) + '\'' - ); - - process.nextTick(function() { - // N.B. Emit 'wserror' here, as 'error' is a reserved word in the - // EventEmitter world, and gets thrown. - self.emit( - 'wserror', - new Error('Invalid handshake from server:' + - 'expected \'' + str2hex(expected) + '\', ' + - 'actual \'' + str2hex(actual) + '\'' - ) - ); - - if (self.onerror) { - self.onerror(); - } - - finishClose(); - }); - } - - // - // Un-register our data handler and add the one to be used - // for the normal, non-handshaking case. If we have extra - // data left over, manually fire off the handler on - // whatever remains. - // - stream.removeAllListeners('data'); - stream.on('data', dataListener); - - readyState = OPEN; - - process.nextTick(function() { - self.emit('open'); - - if (self.onopen) { - self.onopen(); - } - }); - - // Consume any leftover data - if (data.length > 16) { - stream.emit('data', data.slice(16, data.length)); - } - } - }); - stream.on('fd', fdListener); - stream.on('error', errorListener); - stream.on('close', function() { - errorListener(new Error('Stream closed unexpectedly.')); - }); - - stream.emit('data', head); - }; - })()); - } - - agent.on('error', function (e) { - errorListener(e); - }); - var httpReq = protocol.request({ host: u.hostname, method: 'GET', @@ -622,6 +523,103 @@ var WebSocket = function(url, proto, opts) { headers: httpHeaders }); + httpReq.on('error', function (e) { + errorListener(e); + }); + + httpReq.on('upgrade', (function() { + var data = undefined; + + return function(res, s, head) { + stream = s; + + // + // Emit the `wsupgrade` event to inspect the raw + // arguments returned from the websocket request. + // + self.emit('wsupgrade', httpHeaders, res, s, head); + + stream.on('data', function(d) { + if (d.length <= 0) { + return; + } + + if (!data) { + data = d; + } else { + var data2 = new buffer.Buffer(data.length + d.length); + + data.copy(data2, 0, 0, data.length); + d.copy(data2, data.length, 0, d.length); + + data = data2; + } + + if (data.length >= 16) { + var expected = computeSecretKeySignature(key1, key2, challenge); + var actual = data.slice(0, 16).toString('binary'); + + // Handshaking fails; we're donezo + if (actual != expected) { + debug( + 'expected=\'' + str2hex(expected) + '\'; ' + + 'actual=\'' + str2hex(actual) + '\'' + ); + + process.nextTick(function() { + // N.B. Emit 'wserror' here, as 'error' is a reserved word in the + // EventEmitter world, and gets thrown. + self.emit( + 'wserror', + new Error('Invalid handshake from server:' + + 'expected \'' + str2hex(expected) + '\', ' + + 'actual \'' + str2hex(actual) + '\'' + ) + ); + + if (self.onerror) { + self.onerror(); + } + + finishClose(); + }); + } + + // + // Un-register our data handler and add the one to be used + // for the normal, non-handshaking case. If we have extra + // data left over, manually fire off the handler on + // whatever remains. + // + stream.removeAllListeners('data'); + stream.on('data', dataListener); + + readyState = OPEN; + + process.nextTick(function() { + self.emit('open'); + + if (self.onopen) { + self.onopen(); + } + }); + + // Consume any leftover data + if (data.length > 16) { + stream.emit('data', data.slice(16, data.length)); + } + } + }); + stream.on('fd', fdListener); + stream.on('error', errorListener); + stream.on('close', function() { + errorListener(new Error('Stream closed unexpectedly.')); + }); + + stream.emit('data', head); + }; + })()); + httpReq.write(challenge, 'binary'); httpReq.end(); })(); From 38286168161d4f4ad24d2ad95ccd8335e9ed08a4 Mon Sep 17 00:00:00 2001 From: indexzero Date: Fri, 23 Dec 2011 01:33:24 -0500 Subject: [PATCH 50/56] [refactor] Listen for `socket` events since reverseProxy.socket is no longer set synchronously --- lib/node-http-proxy/http-proxy.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/node-http-proxy/http-proxy.js b/lib/node-http-proxy/http-proxy.js index 476b16c..486c2e6 100644 --- a/lib/node-http-proxy/http-proxy.js +++ b/lib/node-http-proxy/http-proxy.js @@ -647,8 +647,8 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) // If the reverseProxy connection has an underlying socket, // then execute the WebSocket handshake. // - if (typeof reverseProxy.socket !== 'undefined') { - reverseProxy.socket.on('data', function handshake (data) { + reverseProxy.once('socket', function (revSocket) { + revSocket.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 @@ -681,12 +681,12 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) socket.write(sdata); var flushed = socket.write(data); if (!flushed) { - reverseProxy.socket.pause(); + revSocket.pause(); socket.once('drain', function () { - try { reverseProxy.socket.resume() } + try { revSocket.resume() } catch (er) { console.error("reverseProxy.socket.resume error: %s", er.message) } }); - + // // Force the `drain` event in 100ms if it hasn't // happened on its own. @@ -701,7 +701,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) // Remove data listener on socket error because the // 'handshake' has failed. // - reverseProxy.socket.removeListener('data', handshake); + revSocket.removeListener('data', handshake); return proxyError(ex); } @@ -711,9 +711,9 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) // // Remove data listener now that the 'handshake' is complete // - reverseProxy.socket.removeListener('data', handshake); + revSocket.removeListener('data', handshake); }); - } + }); reverseProxy.on('error', proxyError); From c81bae2fdde3bf0087fe71a39855c61c43ffb145 Mon Sep 17 00:00:00 2001 From: indexzero Date: Fri, 23 Dec 2011 01:40:35 -0500 Subject: [PATCH 51/56] [fix minor] Correctly set x-forwarded-proto in WebSocket requests --- lib/node-http-proxy/http-proxy.js | 58 ++++++++++++++++--------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/lib/node-http-proxy/http-proxy.js b/lib/node-http-proxy/http-proxy.js index 81f9596..46419ed 100644 --- a/lib/node-http-proxy/http-proxy.js +++ b/lib/node-http-proxy/http-proxy.js @@ -131,28 +131,29 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) { // * `x-forwarded-port`: Port of the original request. // if (this.enable.xforward && req.connection && req.socket) { - if (req.headers['x-forwarded-for']){ - var addressToAppend = "," + req.connection.remoteAddress || req.socket.remoteAddress; - req.headers['x-forwarded-for'] += addressToAppend; - } else { - req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.socket.remoteAddress; + var addressToAppend = "," + req.connection.remoteAddress || req.socket.remoteAddress; + req.headers['x-forwarded-for'] += addressToAppend; + } + else { + req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.socket.remoteAddress; } if (req.headers['x-forwarded-port']){ - var portToAppend = "," + req.connection.remotePort || req.socket.remotePort; - req.headers['x-forwarded-port'] += portToAppend; - } else { - req.headers['x-forwarded-port'] = req.connection.remotePort || req.socket.remotePort; + var portToAppend = "," + req.connection.remotePort || req.socket.remotePort; + req.headers['x-forwarded-port'] += portToAppend; + } + else { + req.headers['x-forwarded-port'] = req.connection.remotePort || req.socket.remotePort; } if (req.headers['x-forwarded-proto']){ - var protoToAppend = "," + req.connection.pair ? 'https' : 'http'; - req.headers['x-forwarded-proto'] += protoToAppend; - } else { - req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http'; + var protoToAppend = "," + req.connection.pair ? 'https' : 'http'; + req.headers['x-forwarded-proto'] += protoToAppend; + } + else { + req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http'; } - } // @@ -420,28 +421,29 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) // * `x-forwarded-port`: Port of the original request. // if (this.enable.xforward && req.connection && req.connection.socket) { - if (req.headers['x-forwarded-for']){ - var addressToAppend = "," + req.connection.remoteAddress || req.connection.socket.remoteAddress; - req.headers['x-forwarded-for'] += addressToAppend; - } else { - req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.connection.socket.remoteAddress; + var addressToAppend = "," + req.connection.remoteAddress || req.connection.socket.remoteAddress; + req.headers['x-forwarded-for'] += addressToAppend; + } + else { + req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.connection.socket.remoteAddress; } if (req.headers['x-forwarded-port']){ - var portToAppend = "," + req.connection.remotePort || req.connection.socket.remotePort; - req.headers['x-forwarded-port'] += portToAppend; - } else { - req.headers['x-forwarded-port'] = req.connection.remotePort || req.connection.socket.remotePort; + var portToAppend = "," + req.connection.remotePort || req.connection.socket.remotePort; + req.headers['x-forwarded-port'] += portToAppend; + } + else { + req.headers['x-forwarded-port'] = req.connection.remotePort || req.connection.socket.remotePort; } if (req.headers['x-forwarded-proto']){ - var protoToAppend = "," + req.connection.pair ? 'https' : 'http'; - req.headers['x-forwarded-proto'] += protoToAppend; - } else { - req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http'; + var protoToAppend = "," + req.connection.pair ? 'wss' : 'ws'; + req.headers['x-forwarded-proto'] += protoToAppend; + } + else { + req.headers['x-forwarded-proto'] = req.connection.pair ? 'wss' : 'ws'; } - } // From 5055689a11f3b990f848bf2699e0111d9e708d5f Mon Sep 17 00:00:00 2001 From: indexzero Date: Fri, 23 Dec 2011 01:42:26 -0500 Subject: [PATCH 52/56] [dist] Version bump. 0.8.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0a6b9dc..b52eb01 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "http-proxy", - "version": "0.7.6", + "version": "0.8.0", "description": "A full-featured http reverse proxy for node.js", "author": "Charlie Robbins ", "contributors": [ @@ -33,5 +33,5 @@ "test-https": "vows --spec --source=secure && vows --spec --source=secure --target=secure", "test-core": "test/core/run" }, - "engines": { "node": "0.4.x || 0.5.x" } + "engines": { "node": ">= 0.6.6" } } From 868f7e7a287c4709c541c077f3e2303f45b1f072 Mon Sep 17 00:00:00 2001 From: MetaHack Date: Mon, 9 Jan 2012 12:43:57 +0800 Subject: [PATCH 53/56] fixed comment typos in examples/http/proxy-https-to-http.js and proxy-https-to-https.js, lines 37 and 46 --- examples/http/proxy-https-to-http.js | 4 ++-- examples/http/proxy-https-to-https.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/http/proxy-https-to-http.js b/examples/http/proxy-https-to-http.js index 75b986b..d6fce41 100644 --- a/examples/http/proxy-https-to-http.js +++ b/examples/http/proxy-https-to-http.js @@ -34,7 +34,7 @@ var https = require('https'), var opts = helpers.loadHttps(); // -// Crete the target HTTPS server +// Create the target HTTPS server // http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); @@ -43,7 +43,7 @@ http.createServer(function (req, res) { }).listen(8000); // -// Create the proxy server listening on port 443. +// Create the proxy server listening on port 443 // httpProxy.createServer(8000, 'localhost', { https: opts diff --git a/examples/http/proxy-https-to-https.js b/examples/http/proxy-https-to-https.js index bd54e88..9acef1f 100644 --- a/examples/http/proxy-https-to-https.js +++ b/examples/http/proxy-https-to-https.js @@ -34,7 +34,7 @@ var https = require('https'), var opts = helpers.loadHttps(); // -// Crete the target HTTPS server +// Create the target HTTPS server // https.createServer(opts, function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }); @@ -43,7 +43,7 @@ https.createServer(opts, function (req, res) { }).listen(8000); // -// Create the proxy server listening on port 443. +// Create the proxy server listening on port 443 // httpProxy.createServer(8000, 'localhost', { https: opts, From 73e415a22634bfc9e5993377902f67ac3212714a Mon Sep 17 00:00:00 2001 From: Drew Perttula Date: Mon, 6 Feb 2012 17:15:44 -0800 Subject: [PATCH 54/56] Address ticket #180 here since that problem is so hard to discover when you run into it. If there was an error, people would search for the error text, but there isn't. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 77fc3f3..0b20ffc 100644 --- a/README.md +++ b/README.md @@ -363,6 +363,12 @@ By default, `node-http-proxy` will set a 100 socket limit for all `host:port` pr 1. By passing the `maxSockets` option to `httpProxy.createServer()` 2. By calling `httpProxy.setMaxSockets(n)`, where `n` is the number of sockets you with to use. +## POST requests and buffering + +express.bodyParser will interfere with proxying of POST requests (and other methods that have a request +body). They'll never sending anything to the upstream server, and the original client will just hang. +See https://github.com/nodejitsu/node-http-proxy/issues/180 for options. + ## Using node-http-proxy from the command line When you install this package with npm, a node-http-proxy binary will become available to you. Using this binary is easy with some simple options: From e15db4fb50db3e2191f3ebd30e12eeed9c376bc2 Mon Sep 17 00:00:00 2001 From: Drew Perttula Date: Thu, 1 Mar 2012 20:35:56 -0800 Subject: [PATCH 55/56] fix the broken english and clarified the sentence (I hope) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0b20ffc..e708c5f 100644 --- a/README.md +++ b/README.md @@ -366,8 +366,8 @@ By default, `node-http-proxy` will set a 100 socket limit for all `host:port` pr ## POST requests and buffering express.bodyParser will interfere with proxying of POST requests (and other methods that have a request -body). They'll never sending anything to the upstream server, and the original client will just hang. -See https://github.com/nodejitsu/node-http-proxy/issues/180 for options. +body). With bodyParser active, proxied requests will never send anything to the upstream server, and +the original client will just hang. See https://github.com/nodejitsu/node-http-proxy/issues/180 for options. ## Using node-http-proxy from the command line When you install this package with npm, a node-http-proxy binary will become available to you. Using this binary is easy with some simple options: From 7fa6599f4f2c92bb29bc5fc8a9ba06d704652c5e Mon Sep 17 00:00:00 2001 From: Lars Burgess Date: Tue, 13 Mar 2012 17:10:14 -0700 Subject: [PATCH 56/56] Making README links consistent with latest project structure. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e708c5f..47e857b 100644 --- a/README.md +++ b/README.md @@ -432,9 +432,9 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. [0]: http://nodejitsu.com -[1]: https://github.com/nodejitsu/node-http-proxy/blob/master/examples/web-socket-proxy.js -[2]: https://github.com/nodejitsu/node-http-proxy/blob/master/examples/basic-proxy-https.js -[3]: https://github.com/nodejitsu/node-http-proxy/tree/v0.5.0/examples +[1]: https://github.com/nodejitsu/node-http-proxy/blob/master/examples/websocket/websocket-proxy.js +[2]: https://github.com/nodejitsu/node-http-proxy/blob/master/examples/http/proxy-https-to-http.js +[3]: https://github.com/nodejitsu/node-http-proxy/tree/master/examples [4]: http://www.ietf.org/rfc/rfc2616.txt [5]: http://socket.io [6]: http://github.com/nodejitsu/node-http-proxy/issues