Merge branch 'master' of github.com:nodejitsu/node-http-proxy

This commit is contained in:
Marak Squires 2012-03-28 22:36:23 -07:00
commit f20b3740b1
38 changed files with 2122 additions and 162 deletions

View File

@ -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()` 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. 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). 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 ## 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: 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:
@ -426,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. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[0]: http://nodejitsu.com [0]: http://nodejitsu.com
[1]: https://github.com/nodejitsu/node-http-proxy/blob/master/examples/web-socket-proxy.js [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/basic-proxy-https.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/v0.5.0/examples [3]: https://github.com/nodejitsu/node-http-proxy/tree/master/examples
[4]: http://www.ietf.org/rfc/rfc2616.txt [4]: http://www.ietf.org/rfc/rfc2616.txt
[5]: http://socket.io [5]: http://socket.io
[6]: http://github.com/nodejitsu/node-http-proxy/issues [6]: http://github.com/nodejitsu/node-http-proxy/issues

View File

@ -34,7 +34,7 @@ var https = require('https'),
var opts = helpers.loadHttps(); var opts = helpers.loadHttps();
// //
// Crete the target HTTPS server // Create the target HTTPS server
// //
http.createServer(function (req, res) { http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' }); res.writeHead(200, { 'Content-Type': 'text/plain' });
@ -43,7 +43,7 @@ http.createServer(function (req, res) {
}).listen(8000); }).listen(8000);
// //
// Create the proxy server listening on port 443. // Create the proxy server listening on port 443
// //
httpProxy.createServer(8000, 'localhost', { httpProxy.createServer(8000, 'localhost', {
https: opts https: opts

View File

@ -34,7 +34,7 @@ var https = require('https'),
var opts = helpers.loadHttps(); var opts = helpers.loadHttps();
// //
// Crete the target HTTPS server // Create the target HTTPS server
// //
https.createServer(opts, function (req, res) { https.createServer(opts, function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' }); res.writeHead(200, { 'Content-Type': 'text/plain' });
@ -43,7 +43,7 @@ https.createServer(opts, function (req, res) {
}).listen(8000); }).listen(8000);
// //
// Create the proxy server listening on port 443. // Create the proxy server listening on port 443
// //
httpProxy.createServer(8000, 'localhost', { httpProxy.createServer(8000, 'localhost', {
https: opts, https: opts,

View File

@ -24,7 +24,7 @@
*/ */
var sys = require('sys'), var util = require('util'),
http = require('http'), http = require('http'),
colors = require('colors'), colors = require('colors'),
websocket = require('../../vendor/websocket'), websocket = require('../../vendor/websocket'),
@ -55,10 +55,10 @@ server.listen(8080);
// //
var socket = io.listen(server); var socket = io.listen(server);
socket.on('connection', function (client) { socket.on('connection', function (client) {
sys.debug('Got websocket connection'); util.debug('Got websocket connection');
client.on('message', function (msg) { client.on('message', function (msg) {
sys.debug('Got message from client: ' + msg); util.debug('Got message from client: ' + msg);
}); });
socket.broadcast('from server'); socket.broadcast('from server');
@ -101,5 +101,5 @@ ws.on('open', function () {
}); });
ws.on('message', function (msg) { ws.on('message', function (msg) {
sys.debug('Got message: ' + utils.decode(msg)); util.debug('Got message: ' + utils.decode(msg));
}); });

View File

@ -24,7 +24,7 @@
*/ */
var sys = require('sys'), var util = require('util'),
http = require('http'), http = require('http'),
colors = require('colors'), colors = require('colors'),
websocket = require('../../vendor/websocket'), websocket = require('../../vendor/websocket'),
@ -55,10 +55,10 @@ server.listen(8080);
// //
var socket = io.listen(server); var socket = io.listen(server);
socket.on('connection', function (client) { socket.on('connection', function (client) {
sys.debug('Got websocket connection'); util.debug('Got websocket connection');
client.on('message', function (msg) { client.on('message', function (msg) {
sys.debug('Got message from client: ' + msg); util.debug('Got message from client: ' + msg);
}); });
socket.broadcast('from server'); socket.broadcast('from server');
@ -97,5 +97,5 @@ ws.on('open', function () {
}); });
ws.on('message', function (msg) { ws.on('message', function (msg) {
sys.debug('Got message: ' + utils.decode(msg)); util.debug('Got message: ' + utils.decode(msg));
}); });

View File

@ -24,7 +24,7 @@
*/ */
var sys = require('sys'), var util = require('util'),
http = require('http'), http = require('http'),
colors = require('colors'), colors = require('colors'),
websocket = require('../../vendor/websocket'), websocket = require('../../vendor/websocket'),
@ -55,10 +55,10 @@ server.listen(8080);
// //
var socket = io.listen(server); var socket = io.listen(server);
socket.on('connection', function (client) { socket.on('connection', function (client) {
sys.debug('Got websocket connection'); util.debug('Got websocket connection');
client.on('message', function (msg) { client.on('message', function (msg) {
sys.debug('Got message from client: ' + msg); util.debug('Got message from client: ' + msg);
}); });
socket.broadcast('from server'); socket.broadcast('from server');
@ -80,5 +80,5 @@ ws.on('open', function () {
}); });
ws.on('message', function (msg) { ws.on('message', function (msg) {
sys.debug('Got message: ' + utils.decode(msg)); util.debug('Got message: ' + utils.decode(msg));
}); });

View File

@ -25,6 +25,7 @@
*/ */
var events = require('events'), var events = require('events'),
http = require('http'),
util = require('util'), util = require('util'),
httpProxy = require('../node-http-proxy'); httpProxy = require('../node-http-proxy');
@ -122,17 +123,37 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
// //
// Add common proxy headers to the request so that they can // Add common proxy headers to the request so that they can
// be availible to the proxy target server: // 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-for`: IP Address of the original request
// * `x-forwarded-proto`: Protocol of the original request // * `x-forwarded-proto`: Protocol of the original request
// * `x-forwarded-port`: Port of the original request. // * `x-forwarded-port`: Port of the original request.
// //
if (this.enable.xforward && req.connection && req.socket) { if (this.enable.xforward && req.connection && req.socket) {
req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.socket.remoteAddress; if (req.headers['x-forwarded-for']){
req.headers['x-forwarded-port'] = req.connection.remotePort || req.socket.remotePort; var addressToAppend = "," + req.connection.remoteAddress || req.socket.remoteAddress;
req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http'; 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';
}
} }
// //
@ -213,6 +234,15 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
delete response.headers['transfer-encoding']; 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 // Set the headers of the client response
res.writeHead(response.statusCode, response.headers); res.writeHead(response.statusCode, response.headers);
@ -271,11 +301,19 @@ HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
// //
reverseProxy.once('error', proxyError); 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 // For each data `chunk` received from the incoming
// `req` write it to the `reverseProxy` request. // `req` write it to the `reverseProxy` request.
// //
req.on('data', function (chunk) { req.on('data', function (chunk) {
if (!errState) { if (!errState) {
var flushed = reverseProxy.write(chunk); var flushed = reverseProxy.write(chunk);
if (!flushed) { if (!flushed) {
@ -352,16 +390,37 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer)
// //
// Add common proxy headers to the request so that they can // Add common proxy headers to the request so that they can
// be availible to the proxy target server: // 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-for`: IP Address of the original request
// * `x-forwarded-proto`: Protocol of the original request // * `x-forwarded-proto`: Protocol of the original request
// * `x-forwarded-port`: Port of the original request. // * `x-forwarded-port`: Port of the original request.
// //
if (this.enable.xforward && req.connection && req.connection.socket) { if (this.enable.xforward && req.connection && req.connection.socket) {
req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.connection.socket.remoteAddress; if (req.headers['x-forwarded-for']){
req.headers['x-forwarded-port'] = req.connection.remotePort || req.connection.socket.remotePort; var addressToAppend = "," + req.connection.remoteAddress || req.connection.socket.remoteAddress;
req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http'; 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 ? 'wss' : 'ws';
req.headers['x-forwarded-proto'] += protoToAppend;
}
else {
req.headers['x-forwarded-proto'] = req.connection.pair ? 'wss' : 'ws';
}
} }
// //
@ -527,11 +586,13 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer)
// //
outgoing.host = this.target.host; outgoing.host = this.target.host;
outgoing.port = this.target.port; outgoing.port = this.target.port;
outgoing.agent = agent;
outgoing.method = 'GET'; outgoing.method = 'GET';
outgoing.path = req.url; outgoing.path = req.url;
outgoing.headers = req.headers; outgoing.headers = req.headers;
outgoing.agent = agent;
var reverseProxy = agent.appendMessage(outgoing); var reverseProxy = this.target.protocol.request(outgoing);
// //
// On any errors from the `reverseProxy` emit the // On any errors from the `reverseProxy` emit the
@ -553,7 +614,6 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer)
// available to the `upgrade` event. This bookkeeping is not tracked anywhere // available to the `upgrade` event. This bookkeeping is not tracked anywhere
// in nodejs core and is **very** specific to proxying WebSockets. // in nodejs core and is **very** specific to proxying WebSockets.
// //
reverseProxy.agent = agent;
reverseProxy.incoming = { reverseProxy.incoming = {
request: req, request: req,
socket: socket, socket: socket,
@ -568,24 +628,22 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer)
// In addition, it's important to note the closure scope here. Since // 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. // there is no mapping of the socket to the request bound to it.
// //
if (!agent._events || agent._events['upgrade'].length === 0) { reverseProxy.on('upgrade', function (_, remoteSocket, head) {
agent.on('upgrade', function (_, remoteSocket, head) { //
// // Prepare the socket for the reverseProxy request and begin to
// Prepare the socket for the reverseProxy request and begin to // stream data between the two sockets. Here it is important to
// stream data between the two sockets. Here it is important to // note that `remoteSocket._httpMessage === reverseProxy`.
// note that `remoteSocket._httpMessage === reverseProxy`. //
// _socket(remoteSocket, true);
_socket(remoteSocket, true); onUpgrade(remoteSocket._httpMessage, remoteSocket);
onUpgrade(remoteSocket._httpMessage, remoteSocket); });
});
}
// //
// If the reverseProxy connection has an underlying socket, // If the reverseProxy connection has an underlying socket,
// then execute the WebSocket handshake. // then execute the WebSocket handshake.
// //
if (typeof reverseProxy.socket !== 'undefined') { reverseProxy.once('socket', function (revSocket) {
reverseProxy.socket.on('data', function handshake (data) { revSocket.on('data', function handshake (data) {
// //
// Ok, kind of harmfull part of code. Socket.IO sends a hash // Ok, kind of harmfull part of code. Socket.IO sends a hash
// at the end of handshake if protocol === 76, but we need // at the end of handshake if protocol === 76, but we need
@ -618,9 +676,9 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer)
socket.write(sdata); socket.write(sdata);
var flushed = socket.write(data); var flushed = socket.write(data);
if (!flushed) { if (!flushed) {
reverseProxy.socket.pause(); revSocket.pause();
socket.once('drain', function () { socket.once('drain', function () {
try { reverseProxy.socket.resume() } try { revSocket.resume() }
catch (er) { console.error("reverseProxy.socket.resume error: %s", er.message) } catch (er) { console.error("reverseProxy.socket.resume error: %s", er.message) }
}); });
@ -638,7 +696,7 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer)
// Remove data listener on socket error because the // Remove data listener on socket error because the
// 'handshake' has failed. // 'handshake' has failed.
// //
reverseProxy.socket.removeListener('data', handshake); revSocket.removeListener('data', handshake);
return proxyError(ex); return proxyError(ex);
} }
@ -648,9 +706,9 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer)
// //
// Remove data listener now that the 'handshake' is complete // Remove data listener now that the 'handshake' is complete
// //
reverseProxy.socket.removeListener('data', handshake); revSocket.removeListener('data', handshake);
}); });
} });
reverseProxy.on('error', proxyError); reverseProxy.on('error', proxyError);
@ -689,9 +747,11 @@ HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer)
HttpProxy.prototype.close = function () { HttpProxy.prototype.close = function () {
[this.forward, this.target].forEach(function (proxy) { [this.forward, this.target].forEach(function (proxy) {
if (proxy && proxy.agent) { if (proxy && proxy.agent) {
proxy.agent.sockets.forEach(function (socket) { for (var host in proxy.agent.sockets) {
socket.end(); proxy.agent.sockets[host].forEach(function (socket) {
}); socket.end();
});
}
} }
}); });
}; };

View File

@ -97,7 +97,7 @@ ProxyTable.prototype.setRoutes = function (router) {
this.routes = []; this.routes = [];
Object.keys(router).forEach(function (path) { Object.keys(router).forEach(function (path) {
var route = new RegExp(path, 'i'); var route = new RegExp('^' + path, 'i');
self.routes.push({ self.routes.push({
route: route, route: route,
@ -137,7 +137,6 @@ ProxyTable.prototype.getProxyLocation = function (req) {
for (var i in this.routes) { for (var i in this.routes) {
var route = this.routes[i]; var route = this.routes[i];
if (target.match(route.route)) { if (target.match(route.route)) {
var pathSegments = route.path.split('/'); var pathSegments = route.path.split('/');
if (pathSegments.length > 1) { if (pathSegments.length > 1) {

View File

@ -180,8 +180,8 @@ RoutingProxy.prototype.proxyRequest = function (req, res, options) {
options.host = location.host; options.host = location.host;
} }
var key = options.host + ':' + options.port, var key = this._getKey(options),
proxy; proxy;
if (!this.proxies[key]) { if (!this.proxies[key]) {
this.add(options); this.add(options);
@ -218,8 +218,8 @@ RoutingProxy.prototype.proxyWebSocketRequest = function (req, socket, head, opti
options.host = location.host; options.host = location.host;
} }
var key = options.host + ':' + options.port, var key = this._getKey(options),
proxy; proxy;
if (!this.proxies[key]) { if (!this.proxies[key]) {
this.add(options); this.add(options);

View File

@ -1,6 +1,6 @@
{ {
"name": "http-proxy", "name": "http-proxy",
"version": "0.7.6", "version": "0.8.0",
"description": "A full-featured http reverse proxy for node.js", "description": "A full-featured http reverse proxy for node.js",
"author": "Charlie Robbins <charlie.robbins@gmail.com>", "author": "Charlie Robbins <charlie.robbins@gmail.com>",
"contributors": [ "contributors": [
@ -22,14 +22,16 @@
"devDependencies": { "devDependencies": {
"request": "1.9.x", "request": "1.9.x",
"vows": "0.5.x", "vows": "0.5.x",
"async": "0.1.x",
"socket.io": "0.6.x" "socket.io": "0.6.x"
}, },
"main": "./lib/node-http-proxy", "main": "./lib/node-http-proxy",
"bin": { "node-http-proxy": "./bin/node-http-proxy" }, "bin": { "node-http-proxy": "./bin/node-http-proxy" },
"scripts": { "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-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" } "engines": { "node": ">= 0.6.6" }
} }

10
test/core/README.md Normal file
View File

@ -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.

145
test/core/common.js Normal file
View File

@ -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) {
};

View File

@ -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);
}
});

90
test/core/run Executable file
View File

@ -0,0 +1,90 @@
#!/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 fs = require('fs'),
path = require('path'),
spawn = require('child_process').spawn,
async = require('async'),
colors = require('colors'),
optimist = require('optimist');
optimist.argv.color && (colors.mode = optimist.argv.color);
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(' ' + path.basename(test).yellow + ' timed out'.red);
}, testTimeout);
child.on('exit', function (exitCode) {
clearTimeout(killTimeout);
console.log(' ' + ((exitCode) ? '✘'.red : '✔'.green) + ' ' +
path.basename(test) +
(exitCode ? (' (exit code: ' + exitCode + ')') : ''));
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).filter(function (test) {
return test.substr(0, 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.
//
}
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:'.bold);
console.log((' ' + ok.length + '\tpassed tests').green);
console.log((' ' + failed.length + '\tfailed tests').red);
});
// vim:filetype=javascript

62
test/core/run-single Executable file
View File

@ -0,0 +1,62 @@
#!/usr/bin/env node
/*
run-single.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.
*/
//
// 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'),
spawn = require('child_process').spawn,
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 () {
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

View File

@ -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();
});

View File

@ -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);
});

View File

@ -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();
});
});
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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();
});

View File

@ -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();
});
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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();
});
}

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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');
});

View File

@ -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);
});

View File

@ -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']
]
});
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

198
vendor/websocket.js vendored
View File

@ -496,7 +496,7 @@ var WebSocket = function(url, proto, opts) {
if (u.protocol === 'ws:' || u.protocol === 'wss:') { if (u.protocol === 'ws:' || u.protocol === 'wss:') {
protocol = u.protocol === 'ws:' ? http : https; protocol = u.protocol === 'ws:' ? http : https;
port = u.protocol === 'ws:' ? 80 : 443; 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, host: u.hostname,
port: u.port || port port: u.port || port
}); });
@ -514,105 +514,6 @@ var WebSocket = function(url, proto, opts) {
throw new Error('Invalid URL scheme \'' + urlScheme + '\' specified.'); 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({ var httpReq = protocol.request({
host: u.hostname, host: u.hostname,
method: 'GET', method: 'GET',
@ -622,6 +523,103 @@ var WebSocket = function(url, proto, opts) {
headers: httpHeaders 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.write(challenge, 'binary');
httpReq.end(); httpReq.end();
})(); })();