mirror of
https://github.com/http-party/node-http-proxy.git
synced 2025-12-08 20:59:18 +00:00
[api] Further work on refactor for node 0.4.0
This commit is contained in:
parent
9faa924a29
commit
e39a9f93d2
@ -25,11 +25,31 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
var util = require('util'),
|
var util = require('util'),
|
||||||
|
eyes = require('eyes'),
|
||||||
http = require('http'),
|
http = require('http'),
|
||||||
events = require('events'),
|
events = require('events'),
|
||||||
|
winston = require('winston'),
|
||||||
ProxyTable = require('./proxy-table').ProxyTable,
|
ProxyTable = require('./proxy-table').ProxyTable,
|
||||||
maxSockets = 100;
|
maxSockets = 100;
|
||||||
|
|
||||||
|
|
||||||
|
function _getAgent (host, port) {
|
||||||
|
//
|
||||||
|
// TODO (indexzero): Make this configurable for http / https
|
||||||
|
//
|
||||||
|
var agent = http.getAgent(host, port);
|
||||||
|
agent.maxSockets = maxSockets;
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getMaxSockets = function () {
|
||||||
|
return maxSockets;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.setMaxSockets = function (value) {
|
||||||
|
maxSockets = value;
|
||||||
|
};
|
||||||
|
|
||||||
exports.createServer = function () {
|
exports.createServer = function () {
|
||||||
var args, callback, port, host, forward,
|
var args, callback, port, host, forward,
|
||||||
silent, proxyTable, options = {};
|
silent, proxyTable, options = {};
|
||||||
@ -41,64 +61,45 @@ exports.createServer = function () {
|
|||||||
port = args[0];
|
port = args[0];
|
||||||
host = args[1];
|
host = args[1];
|
||||||
options = args[2] || {};
|
options = args[2] || {};
|
||||||
} else if (args.length === 1) {
|
}
|
||||||
|
else if (args.length === 1) {
|
||||||
options = args[0] || {};
|
options = args[0] || {};
|
||||||
if (!options.router && !callback) {
|
if (!options.router && !callback) {
|
||||||
throw new Error('Cannot create server with no router and no callback');
|
throw new Error('Cannot create server with no router and no callback');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
router = options.router;
|
var proxy = new HttpProxy(options);
|
||||||
forward = options.forward;
|
|
||||||
silent = typeof options.silent !== 'undefined' ? options.silent : true;
|
|
||||||
|
|
||||||
if (router) {
|
|
||||||
proxyTable = new ProxyTable(router, options.silent);
|
|
||||||
proxyTable.on('updateRoutes', function (routes) {
|
|
||||||
server.emit('updateRoutes', routes);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var server = http.createServer(function (req, res) {
|
var server = http.createServer(function (req, res) {
|
||||||
function log (message) {
|
winston.verbose('Incoming HTTP request to: ' + req.headers.host + req.url);
|
||||||
if (!silent) {
|
|
||||||
util.log(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var proxy = new HttpProxy(req, res);
|
|
||||||
log('Incoming HTTP request to: ' + req.headers.host + req.url);
|
|
||||||
|
|
||||||
if (forward) {
|
|
||||||
var forwardProxy = new HttpProxy(req, res);
|
|
||||||
log('Forwarding HTTP request to: ' + forward.host + ':' + forward.port);
|
|
||||||
forwardProxy.forwardRequest(forward.port, forward.host);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we were passed a callback to process the request
|
// If we were passed a callback to process the request
|
||||||
// or response in some way, then call it.
|
// or response in some way, then call it.
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(req, res, proxy);
|
callback(req, res, proxy);
|
||||||
} else if (port && host) {
|
}
|
||||||
log('Proxying HTTP request to: ' + host + ':' + port);
|
else if (port && host) {
|
||||||
proxy.proxyRequest(port, host);
|
winston.verbose('Proxying HTTP request to: ' + host + ':' + port);
|
||||||
} else if (proxyTable) {
|
proxy.proxyRequest(req, res, port, host);
|
||||||
proxyTable.proxyRequest(proxy);
|
}
|
||||||
} else {
|
else if (proxy.proxyTable) {
|
||||||
|
winston.verbose('Proxying request using proxy table');
|
||||||
|
proxy.proxyRequest(req, res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
throw new Error('Cannot proxy without port, host, or router.')
|
throw new Error('Cannot proxy without port, host, or router.')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on('close', function () {
|
server.on('close', function () {
|
||||||
if (proxyTable) proxyTable.close();
|
proxy.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
// WebSocket support: if callback is empty tunnel
|
// WebSocket support: if callback is empty tunnel
|
||||||
// websocket request automatically
|
// websocket request automatically
|
||||||
server.on('upgrade', function(req, socket, head) {
|
server.on('upgrade', function(req, socket, head) {
|
||||||
var proxy = new HttpProxy(req, socket, head);
|
|
||||||
|
|
||||||
// Tunnel websocket requests too
|
// Tunnel websocket requests too
|
||||||
proxy.proxyWebSocketRequest(port, host);
|
proxy.proxyWebSocketRequest(port, host);
|
||||||
});
|
});
|
||||||
@ -107,363 +108,402 @@ exports.createServer = function () {
|
|||||||
return server;
|
return server;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.setMaxSockets = function (value) {
|
var HttpProxy = exports.HttpProxy = function (options) {
|
||||||
maxSockets = value;
|
events.EventEmitter.call(this);
|
||||||
};
|
this.options = options;
|
||||||
|
|
||||||
exports.ProxyTable = ProxyTable;
|
if (options.router) {
|
||||||
|
var self = this;
|
||||||
var HttpProxy = exports.HttpProxy = function (req, res, head) {
|
this.proxyTable = new ProxyTable(options.router, options.silent || false);
|
||||||
this.events = {};
|
this.proxyTable.on('routes', function (routes) {
|
||||||
this.req = req;
|
self.emit('routes', routes);
|
||||||
|
});
|
||||||
// If this request is upgrade request
|
|
||||||
// No response will be passed
|
|
||||||
if (!req.headers.upgrade) {
|
|
||||||
this.res = res;
|
|
||||||
this.watch(req);
|
|
||||||
} else {
|
|
||||||
// Second argument will be socket
|
|
||||||
this.sock = res;
|
|
||||||
this.head = head;
|
|
||||||
this.watch(res);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
HttpProxy.prototype = {
|
util.inherits(HttpProxy, events.EventEmitter);
|
||||||
toArray: function (obj) {
|
|
||||||
var len = obj.length,
|
HttpProxy.prototype.close = function () {
|
||||||
arr = new Array(len);
|
if (this.proxyTable) this.proxyTable.close();
|
||||||
for (var i = 0; i < len; ++i) {
|
};
|
||||||
arr[i] = obj[i];
|
|
||||||
|
/**
|
||||||
|
* Pause `data` and `end` events on the given `obj`.
|
||||||
|
* Middleware performing async tasks _should_ utilize
|
||||||
|
* this utility (or similar), to re-emit data once
|
||||||
|
* the async operation has completed, otherwise these
|
||||||
|
* events may be lost.
|
||||||
|
*
|
||||||
|
* var pause = utils.pause(req);
|
||||||
|
* fs.readFile(path, function(){
|
||||||
|
* next();
|
||||||
|
* pause.resume();
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @param {Object} obj
|
||||||
|
* @return {Object}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
HttpProxy.prototype.pause = function (obj) {
|
||||||
|
var onData, onEnd, events = [];
|
||||||
|
|
||||||
|
// buffer data
|
||||||
|
obj.on('data', onData = function (data, encoding) {
|
||||||
|
events.push(['data', data, encoding]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// buffer end
|
||||||
|
obj.on('end', onEnd = function (data, encoding) {
|
||||||
|
events.push(['end', data, encoding]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
end: function () {
|
||||||
|
obj.removeListener('data', onData);
|
||||||
|
obj.removeListener('end', onEnd);
|
||||||
|
},
|
||||||
|
resume: function () {
|
||||||
|
this.end();
|
||||||
|
for (var i = 0, len = events.length; i < len; ++i) {
|
||||||
|
obj.emit.apply(obj, events[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return arr;
|
};
|
||||||
},
|
};
|
||||||
|
|
||||||
watch: function (req) {
|
HttpProxy.prototype.proxyRequest = function (req, res, port, host, paused) {
|
||||||
this.events = [];
|
var self = this, reverseProxy, location;
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.onData = function () {
|
//
|
||||||
self.events.push(['data'].concat(self.toArray(arguments)));
|
// Check the proxy table for this instance to see if we need
|
||||||
};
|
// to get the proxy location for the request supplied. We will
|
||||||
this.onEnd = function () {
|
// always ignore the proxyTable if an explicit `port` and `host`
|
||||||
self.events.push(['end'].concat(self.toArray(arguments)));
|
// arguments are supplied to `proxyRequest`.
|
||||||
|
//
|
||||||
|
if (this.proxyTable && !host) {
|
||||||
|
location = this.proxyTable.getProxyLocation(req);
|
||||||
|
|
||||||
|
if (!location) {
|
||||||
|
res.writeHead(400);
|
||||||
|
return res.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// When using the ProxyTable in conjunction with an HttpProxy instance
|
||||||
|
// only the following arguments are valid:
|
||||||
|
//
|
||||||
|
// * proxy.proxyRequest(req, res, port, host, paused): This will be skipped
|
||||||
|
// * proxy.proxyRequest(req, res, paused): Paused will get updated appropriately
|
||||||
|
// * proxy.proxyRequest(req, res): No effect `undefined = undefined`
|
||||||
|
//
|
||||||
|
paused = port;
|
||||||
|
port = location.port;
|
||||||
|
host = location.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.options.forward) {
|
||||||
|
winston.verbose('Forwarding HTTP request to: ' + this.options.forward.host + ':' + this.options.forward.port);
|
||||||
|
this._forwardRequest(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an error handler so we can use it temporarily
|
||||||
|
function error (obj) {
|
||||||
|
var fn = function (err) {
|
||||||
|
res.writeHead(500, {'Content-Type': 'text/plain'});
|
||||||
|
|
||||||
|
if (req.method !== 'HEAD') {
|
||||||
|
res.write('An error has occurred: ' + JSON.stringify(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response end may never come so removeListener here
|
||||||
|
obj.removeListener('error', fn);
|
||||||
|
res.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
req.addListener('data', this.onData);
|
return fn;
|
||||||
req.addListener('end', this.onEnd);
|
};
|
||||||
},
|
|
||||||
|
|
||||||
unwatch: function (req) {
|
// Open new HTTP request to internal resource with will act as a reverse proxy pass
|
||||||
req.removeListener('data', this.onData);
|
reverseProxy = http.request({
|
||||||
req.removeListener('end', this.onEnd);
|
host: host,
|
||||||
|
port: port,
|
||||||
|
agent: _getAgent(host, port),
|
||||||
|
method: req.method,
|
||||||
|
path: req.url,
|
||||||
|
headers: req.headers
|
||||||
|
}, function (response) {
|
||||||
|
|
||||||
// Rebroadcast any events that have been buffered
|
// Process the reverse_proxy response when it's received.
|
||||||
for (var i = 0, len = this.events.length; i < len; ++i) {
|
if (response.headers.connection) {
|
||||||
req.emit.apply(req, this.events[i]);
|
if (req.headers.connection) response.headers.connection = req.headers.connection;
|
||||||
}
|
else response.headers.connection = 'close';
|
||||||
},
|
|
||||||
|
|
||||||
proxyRequest: function (port, server) {
|
|
||||||
var self = this, req = this.req, res = this.res, reverseProxy;
|
|
||||||
|
|
||||||
// Create an error handler so we can use it temporarily
|
|
||||||
function error (obj) {
|
|
||||||
var fn = function (err) {
|
|
||||||
res.writeHead(500, {'Content-Type': 'text/plain'});
|
|
||||||
|
|
||||||
if (req.method !== 'HEAD') {
|
|
||||||
res.write('An error has occurred: ' + JSON.stringify(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Response end may never come so removeListener here
|
|
||||||
obj.removeListener('error', fn);
|
|
||||||
res.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
return fn;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Open new HTTP request to internal resource with will act as a reverse proxy pass
|
|
||||||
reverseProxy = http.request({
|
|
||||||
host: server,
|
|
||||||
port: port,
|
|
||||||
method: req.method,
|
|
||||||
path: req.url,
|
|
||||||
headers: req.headers
|
|
||||||
}, function (response) {
|
|
||||||
|
|
||||||
// Process the reverse_proxy response when it's received.
|
|
||||||
if (response.headers.connection) {
|
|
||||||
if (req.headers.connection) response.headers.connection = req.headers.connection;
|
|
||||||
else response.headers.connection = 'close';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the response headers of the client response
|
|
||||||
res.writeHead(response.statusCode, response.headers);
|
|
||||||
|
|
||||||
// Status code = 304
|
|
||||||
// No 'data' event and no 'end'
|
|
||||||
if (response.statusCode === 304) {
|
|
||||||
res.end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add event handler for the proxied response in chunks
|
|
||||||
response.addListener('data', function (chunk) {
|
|
||||||
if (req.method !== 'HEAD') {
|
|
||||||
res.write(chunk, 'binary');
|
|
||||||
self.body += chunk;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add event listener for end of proxied response
|
|
||||||
response.addListener('end', function () {
|
|
||||||
reverseProxy.removeListener('error', reverseProxyError);
|
|
||||||
res.end();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add a listener for the connection timeout event
|
|
||||||
var reverseProxyError = error(reverseProxy);
|
|
||||||
reverseProxy.addListener('error', reverseProxyError);
|
|
||||||
|
|
||||||
// Chunk the client request body as chunks from the proxied request come in
|
|
||||||
req.addListener('data', function (chunk) {
|
|
||||||
reverseProxy.write(chunk, 'binary');
|
|
||||||
})
|
|
||||||
|
|
||||||
// At the end of the client request, we are going to stop the proxied request
|
|
||||||
req.addListener('end', function () {
|
|
||||||
reverseProxy.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.unwatch(req);
|
|
||||||
},
|
|
||||||
|
|
||||||
forwardRequest: function (port, server) {
|
|
||||||
var self = this, req = this.req, forwardProxy;
|
|
||||||
|
|
||||||
// Open new HTTP request to internal resource with will act as a reverse proxy pass
|
|
||||||
forwardProxy = http.request({
|
|
||||||
host: server,
|
|
||||||
port: port,
|
|
||||||
method: req.method,
|
|
||||||
path: req.url,
|
|
||||||
headers: req.headers
|
|
||||||
}, function (response) {
|
|
||||||
//
|
|
||||||
// Ignore the response from the forward proxy since this is a 'fire-and-forget' proxy.
|
|
||||||
// Remark (indexzero): We will eventually emit a 'forward' event here for performance tuning.
|
|
||||||
//
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add a listener for the connection timeout event
|
|
||||||
forwardProxy.addListener('error', function (err) {
|
|
||||||
// Remark: Ignoring this error in the event
|
|
||||||
// forward target doesn't exist.
|
|
||||||
});
|
|
||||||
|
|
||||||
// Chunk the client request body as chunks from the proxied request come in
|
|
||||||
req.addListener('data', function (chunk) {
|
|
||||||
forwardProxy.write(chunk, 'binary');
|
|
||||||
})
|
|
||||||
|
|
||||||
// At the end of the client request, we are going to stop the proxied request
|
|
||||||
req.addListener('end', function () {
|
|
||||||
forwardProxy.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.unwatch(req);
|
|
||||||
},
|
|
||||||
|
|
||||||
proxyWebSocketRequest: function (port, server, host) {
|
|
||||||
var self = this, req = self.req, socket = self.sock, head = self.head,
|
|
||||||
headers = new _headers(req.headers), CRLF = '\r\n';
|
|
||||||
|
|
||||||
// Will generate clone of headers
|
|
||||||
// To not change original
|
|
||||||
function _headers(headers) {
|
|
||||||
var h = {};
|
|
||||||
for (var i in headers) {
|
|
||||||
h[i] = headers[i];
|
|
||||||
}
|
|
||||||
return h;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebSocket requests has method = GET
|
// Set the response headers of the client response
|
||||||
if (req.method !== 'GET' || headers.upgrade.toLowerCase() !== 'websocket') {
|
res.writeHead(response.statusCode, response.headers);
|
||||||
// This request is not WebSocket request
|
|
||||||
return;
|
// Status code = 304
|
||||||
|
// No 'data' event and no 'end'
|
||||||
|
if (response.statusCode === 304) {
|
||||||
|
return res.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn of all bufferings
|
// Add event handler for the proxied response in chunks
|
||||||
// For server set KeepAlive
|
response.on('data', function (chunk) {
|
||||||
// For client set encoding
|
if (req.method !== 'HEAD') {
|
||||||
function _socket(socket, server) {
|
res.write(chunk);
|
||||||
socket.setTimeout(0);
|
|
||||||
socket.setNoDelay(true);
|
|
||||||
if (server) {
|
|
||||||
socket.setKeepAlive(true, 0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
socket.setEncoding('utf8');
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add event listener for end of proxied response
|
||||||
|
response.on('end', function () {
|
||||||
|
reverseProxy.removeListener('error', reverseProxyError);
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a listener for the connection timeout event
|
||||||
|
var reverseProxyError = error(reverseProxy);
|
||||||
|
reverseProxy.on('error', reverseProxyError);
|
||||||
|
|
||||||
|
// Chunk the client request body as chunks from the proxied request come in
|
||||||
|
req.on('data', function (chunk) {
|
||||||
|
reverseProxy.write(chunk);
|
||||||
|
});
|
||||||
|
|
||||||
|
// At the end of the client request, we are going to stop the proxied request
|
||||||
|
req.on('end', function () {
|
||||||
|
reverseProxy.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (paused) {
|
||||||
|
paused.resume();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpProxy.prototype._forwardRequest = function (req) {
|
||||||
|
var self = this, port, host, forwardProxy;
|
||||||
|
|
||||||
|
port = this.options.forward.port;
|
||||||
|
host = this.options.forward.host;
|
||||||
|
|
||||||
|
// Open new HTTP request to internal resource with will act as a reverse proxy pass
|
||||||
|
forwardProxy = http.request({
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
agent: _getAgent(host, port),
|
||||||
|
method: req.method,
|
||||||
|
path: req.url,
|
||||||
|
headers: req.headers
|
||||||
|
}, function (response) {
|
||||||
|
//
|
||||||
|
// Ignore the response from the forward proxy since this is a 'fire-and-forget' proxy.
|
||||||
|
// Remark (indexzero): We will eventually emit a 'forward' event here for performance tuning.
|
||||||
|
//
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a listener for the connection timeout event
|
||||||
|
forwardProxy.on('error', function (err) {
|
||||||
|
// Remark: Ignoring this error in the event
|
||||||
|
// forward target doesn't exist.
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chunk the client request body as chunks from the proxied request come in
|
||||||
|
req.on('data', function (chunk) {
|
||||||
|
forwardProxy.write(chunk);
|
||||||
|
})
|
||||||
|
|
||||||
|
// At the end of the client request, we are going to stop the proxied request
|
||||||
|
req.on('end', function () {
|
||||||
|
forwardProxy.end();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpProxy.prototype.proxyWebSocketRequest = function (port, server, host, data) {
|
||||||
|
var self = this, req = self.req, socket = self.sock, head = self.head,
|
||||||
|
headers = new _headers(req.headers), CRLF = '\r\n';
|
||||||
|
|
||||||
|
// Will generate clone of headers
|
||||||
|
// To not change original
|
||||||
|
function _headers(headers) {
|
||||||
|
var h = {};
|
||||||
|
for (var i in headers) {
|
||||||
|
h[i] = headers[i];
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebSocket requests has method = GET
|
||||||
|
if (req.method !== 'GET' || headers.upgrade.toLowerCase() !== 'websocket') {
|
||||||
|
// This request is not WebSocket request
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn of all bufferings
|
||||||
|
// For server set KeepAlive
|
||||||
|
// For client set encoding
|
||||||
|
function _socket(socket, server) {
|
||||||
|
socket.setTimeout(0);
|
||||||
|
socket.setNoDelay(true);
|
||||||
|
if (server) {
|
||||||
|
socket.setKeepAlive(true, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
socket.setEncoding('utf8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client socket
|
||||||
|
_socket(socket);
|
||||||
|
|
||||||
|
// If host is undefined
|
||||||
|
// Get it from headers
|
||||||
|
if (!host) {
|
||||||
|
host = headers.Host;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remote host address
|
||||||
|
var remote_host = server + (port - 80 === 0 ? '' : ':' + port);
|
||||||
|
|
||||||
|
// Change headers
|
||||||
|
headers.Host = remote_host;
|
||||||
|
headers.Origin = 'http://' + remote_host;
|
||||||
|
|
||||||
|
// Open request
|
||||||
|
var p = manager.getPool(port, server);
|
||||||
|
|
||||||
|
p.getClient(function(client) {
|
||||||
|
// Based on 'pool/main.js'
|
||||||
|
var request = client.request('GET', req.url, headers);
|
||||||
|
|
||||||
|
var errorListener = function (error) {
|
||||||
|
client.removeListener('error', errorListener);
|
||||||
|
|
||||||
|
// Remove the client from the pool's available clients since it has errored
|
||||||
|
p.clients.splice(p.clients.indexOf(client), 1);
|
||||||
|
socket.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client socket
|
// Not disconnect on update
|
||||||
_socket(socket);
|
client.on('upgrade', function(request, remote_socket, head) {
|
||||||
|
// Prepare socket
|
||||||
|
_socket(remote_socket, true);
|
||||||
|
|
||||||
// If host is undefined
|
// Emit event
|
||||||
// Get it from headers
|
onUpgrade(remote_socket);
|
||||||
if (!host) {
|
});
|
||||||
host = headers.Host;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remote host address
|
client.on('error', errorListener);
|
||||||
var remote_host = server + (port - 80 === 0 ? '' : ':' + port);
|
request.on('response', function (response) {
|
||||||
|
response.on('end', function () {
|
||||||
// Change headers
|
|
||||||
headers.Host = remote_host;
|
|
||||||
headers.Origin = 'http://' + remote_host;
|
|
||||||
|
|
||||||
// Open request
|
|
||||||
var p = manager.getPool(port, server);
|
|
||||||
|
|
||||||
p.getClient(function(client) {
|
|
||||||
// Based on 'pool/main.js'
|
|
||||||
var request = client.request('GET', req.url, headers);
|
|
||||||
|
|
||||||
var errorListener = function (error) {
|
|
||||||
client.removeListener('error', errorListener);
|
client.removeListener('error', errorListener);
|
||||||
|
client.busy = false;
|
||||||
// Remove the client from the pool's available clients since it has errored
|
p.onFree(client);
|
||||||
p.clients.splice(p.clients.indexOf(client), 1);
|
|
||||||
socket.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not disconnect on update
|
|
||||||
client.on('upgrade', function(request, remote_socket, head) {
|
|
||||||
// Prepare socket
|
|
||||||
_socket(remote_socket, true);
|
|
||||||
|
|
||||||
// Emit event
|
|
||||||
onUpgrade(remote_socket);
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on('error', errorListener);
|
|
||||||
request.on('response', function (response) {
|
|
||||||
response.on('end', function () {
|
|
||||||
client.removeListener('error', errorListener);
|
|
||||||
client.busy = false;
|
|
||||||
p.onFree(client);
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
client.busy = true;
|
})
|
||||||
|
client.busy = true;
|
||||||
|
|
||||||
var handshake;
|
var handshake;
|
||||||
request.socket.on('data', handshake = function(data) {
|
request.socket.on('data', handshake = function(data) {
|
||||||
// Handshaking
|
// Handshaking
|
||||||
|
|
||||||
// Ok, kind of harmfull part of code
|
// Ok, kind of harmfull part of code
|
||||||
// Socket.IO is sending hash at the end of handshake
|
// Socket.IO is sending hash at the end of handshake
|
||||||
// If protocol = 76
|
// If protocol = 76
|
||||||
// But we need to replace 'host' and 'origin' in response
|
// But we need to replace 'host' and 'origin' in response
|
||||||
// So we split data to printable data and to non-printable
|
// So we split data to printable data and to non-printable
|
||||||
// (Non-printable will come after double-CRLF)
|
// (Non-printable will come after double-CRLF)
|
||||||
var sdata = data.toString();
|
var sdata = data.toString();
|
||||||
|
|
||||||
// Get Printable
|
// Get Printable
|
||||||
sdata = sdata.substr(0, sdata.search(CRLF + CRLF));
|
sdata = sdata.substr(0, sdata.search(CRLF + CRLF));
|
||||||
|
|
||||||
// Get Non-Printable
|
// Get Non-Printable
|
||||||
data = data.slice(Buffer.byteLength(sdata), data.length);
|
data = data.slice(Buffer.byteLength(sdata), data.length);
|
||||||
|
|
||||||
// Replace host and origin
|
// Replace host and origin
|
||||||
sdata = sdata.replace(remote_host, host)
|
sdata = sdata.replace(remote_host, host)
|
||||||
.replace(remote_host, host);
|
.replace(remote_host, host);
|
||||||
|
|
||||||
try {
|
|
||||||
// Write printable
|
|
||||||
socket.write(sdata);
|
|
||||||
|
|
||||||
// Write non-printable
|
|
||||||
socket.write(data);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
request.end();
|
|
||||||
socket.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Catch socket errors
|
|
||||||
socket.on('error', function() {
|
|
||||||
request.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove data listener now that the 'handshake' is complete
|
|
||||||
request.socket.removeListener('data', handshake);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Write upgrade-head
|
|
||||||
try {
|
try {
|
||||||
request.write(head);
|
// Write printable
|
||||||
|
socket.write(sdata);
|
||||||
|
|
||||||
|
// Write non-printable
|
||||||
|
socket.write(data);
|
||||||
}
|
}
|
||||||
catch(e) {
|
catch (e) {
|
||||||
request.end();
|
request.end();
|
||||||
socket.end();
|
socket.end();
|
||||||
}
|
}
|
||||||
self.unwatch(socket);
|
|
||||||
|
// Catch socket errors
|
||||||
|
socket.on('error', function() {
|
||||||
|
request.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove data listener now that the 'handshake' is complete
|
||||||
|
request.socket.removeListener('data', handshake);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Request
|
// Write upgrade-head
|
||||||
|
try {
|
||||||
|
request.write(head);
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
request.end();
|
||||||
|
socket.end();
|
||||||
|
}
|
||||||
|
self.unwatch(socket);
|
||||||
|
});
|
||||||
|
|
||||||
function onUpgrade(reverse_proxy) {
|
// Request
|
||||||
var listeners = {};
|
|
||||||
|
|
||||||
// We're now connected to the server, so lets change server socket
|
function onUpgrade(reverse_proxy) {
|
||||||
reverse_proxy.on('data', listeners._r_data = function(data) {
|
var listeners = {};
|
||||||
// Pass data to client
|
|
||||||
if (socket.writable) {
|
|
||||||
try {
|
|
||||||
socket.write(data);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
socket.end();
|
|
||||||
reverse_proxy.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('data', listeners._data = function(data) {
|
// We're now connected to the server, so lets change server socket
|
||||||
// Pass data from client to server
|
reverse_proxy.on('data', listeners._r_data = function(data) {
|
||||||
|
// Pass data to client
|
||||||
|
if (socket.writable) {
|
||||||
try {
|
try {
|
||||||
reverse_proxy.write(data);
|
socket.write(data);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
reverse_proxy.end();
|
|
||||||
socket.end();
|
socket.end();
|
||||||
|
reverse_proxy.end();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Detach event listeners from reverse_proxy
|
|
||||||
function detach() {
|
|
||||||
reverse_proxy.removeListener('close', listeners._r_close);
|
|
||||||
reverse_proxy.removeListener('data', listeners._r_data);
|
|
||||||
socket.removeListener('data', listeners._data);
|
|
||||||
socket.removeListener('close', listeners._close);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Hook disconnections
|
socket.on('data', listeners._data = function(data) {
|
||||||
reverse_proxy.on('end', listeners._r_close = function() {
|
// Pass data from client to server
|
||||||
socket.end();
|
try {
|
||||||
detach();
|
reverse_proxy.write(data);
|
||||||
});
|
}
|
||||||
|
catch (e) {
|
||||||
socket.on('end', listeners._close = function() {
|
|
||||||
reverse_proxy.end();
|
reverse_proxy.end();
|
||||||
detach();
|
socket.end();
|
||||||
});
|
}
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
// Detach event listeners from reverse_proxy
|
||||||
|
function detach() {
|
||||||
|
reverse_proxy.removeListener('close', listeners._r_close);
|
||||||
|
reverse_proxy.removeListener('data', listeners._r_data);
|
||||||
|
socket.removeListener('data', listeners._data);
|
||||||
|
socket.removeListener('close', listeners._close);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook disconnections
|
||||||
|
reverse_proxy.on('end', listeners._r_close = function() {
|
||||||
|
socket.end();
|
||||||
|
detach();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('end', listeners._close = function() {
|
||||||
|
reverse_proxy.end();
|
||||||
|
detach();
|
||||||
|
});
|
||||||
|
};
|
||||||
};
|
};
|
||||||
@ -28,27 +28,39 @@ var util = require('util'),
|
|||||||
events = require('events'),
|
events = require('events'),
|
||||||
fs = require('fs');
|
fs = require('fs');
|
||||||
|
|
||||||
var ProxyTable = function (router, silent) {
|
//
|
||||||
|
// ### function ProxyTable (router, silent)
|
||||||
|
// #### @router {Object} Object containing the host based routes
|
||||||
|
// #### @silent {Boolean} Value indicating whether we should suppress logs
|
||||||
|
// Constructor function for the ProxyTable responsible for getting
|
||||||
|
// locations of proxy targets based on ServerRequest headers; specifically
|
||||||
|
// the HTTP host header.
|
||||||
|
//
|
||||||
|
var ProxyTable = exports.ProxyTable = function (router, silent) {
|
||||||
events.EventEmitter.call(this);
|
events.EventEmitter.call(this);
|
||||||
this.silent = typeof silent !== 'undefined' ? silent : true;
|
this.silent = typeof silent !== 'undefined' ? silent : true;
|
||||||
|
|
||||||
if (typeof router === 'object') {
|
if (typeof router === 'object') {
|
||||||
|
//
|
||||||
// If we are passed an object literal setup
|
// If we are passed an object literal setup
|
||||||
// the routes with RegExps from the router
|
// the routes with RegExps from the router
|
||||||
this.updateRoutes(router);
|
//
|
||||||
|
this.setRoutes(router);
|
||||||
}
|
}
|
||||||
else if (typeof router === 'string') {
|
else if (typeof router === 'string') {
|
||||||
|
//
|
||||||
// If we are passed a string then assume it is a
|
// If we are passed a string then assume it is a
|
||||||
// file path, parse that file and watch it for changes
|
// file path, parse that file and watch it for changes
|
||||||
|
//
|
||||||
var self = this;
|
var self = this;
|
||||||
this.routeFile = router;
|
this.routeFile = router;
|
||||||
this.updateRoutes(JSON.parse(fs.readFileSync(router)).router);
|
this.setRoutes(JSON.parse(fs.readFileSync(router)).router);
|
||||||
|
|
||||||
fs.watchFile(this.routeFile, function (c,p) {
|
fs.watchFile(this.routeFile, function (c,p) {
|
||||||
fs.readFile(self.routeFile, function (err, data) {
|
fs.readFile(self.routeFile, function (err, data) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
self.updateRoutes(JSON.parse(data).router);
|
self.setRoutes(JSON.parse(data).router);
|
||||||
self.emit('updateRoutes', self.routes);
|
self.emit('routes', self.routes);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -57,9 +69,15 @@ var ProxyTable = function (router, silent) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Inherit from events.EventEmitter
|
||||||
util.inherits(ProxyTable, events.EventEmitter);
|
util.inherits(ProxyTable, events.EventEmitter);
|
||||||
|
|
||||||
ProxyTable.prototype.updateRoutes = function (router) {
|
//
|
||||||
|
// ### function setRoutes (router)
|
||||||
|
// #### @router {Object} Object containing the host based routes
|
||||||
|
// Sets the host-based routes to be used by this instance.
|
||||||
|
//
|
||||||
|
ProxyTable.prototype.setRoutes = function (router) {
|
||||||
if (!router) throw new Error('Cannot update ProxyTable routes without router.');
|
if (!router) throw new Error('Cannot update ProxyTable routes without router.');
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -76,8 +94,14 @@ ProxyTable.prototype.updateRoutes = function (router) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ProxyTable.prototype.proxyRequest = function (proxy) {
|
//
|
||||||
var target = proxy.req.headers.host.split(':')[0] + proxy.req.url;
|
// ### function getProxyLocation (req)
|
||||||
|
// #### @req {ServerRequest} The incoming server request to get proxy information about.
|
||||||
|
// Returns the proxy location based on the HTTP Headers in the ServerRequest `req`
|
||||||
|
// available to this instance.
|
||||||
|
//
|
||||||
|
ProxyTable.prototype.getProxyLocation = function (req) {
|
||||||
|
var target = req.headers.host.split(':')[0] + req.url;
|
||||||
for (var i in this.routes) {
|
for (var i in this.routes) {
|
||||||
var match, route = this.routes[i];
|
var match, route = this.routes[i];
|
||||||
if (match = target.match(route.route)) {
|
if (match = target.match(route.route)) {
|
||||||
@ -89,25 +113,23 @@ ProxyTable.prototype.proxyRequest = function (proxy) {
|
|||||||
util.log('Proxy Table proxying request to: ' + host + ':' + port);
|
util.log('Proxy Table proxying request to: ' + host + ':' + port);
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy.proxyRequest(port, host);
|
return {
|
||||||
return;
|
port: port,
|
||||||
|
host: host
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proxy.res) {
|
return null;
|
||||||
proxy.res.writeHead(404, {'Content-Type': 'text/plain'});
|
|
||||||
proxy.res.end();
|
|
||||||
}
|
|
||||||
else if (proxy.sock) {
|
|
||||||
// Remark: How do we perform '404' over a socket?
|
|
||||||
proxy.sock.destroy();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ### close function ()
|
||||||
|
// Cleans up the event listeneners maintained
|
||||||
|
// by this instance.
|
||||||
|
//
|
||||||
ProxyTable.prototype.close = function () {
|
ProxyTable.prototype.close = function () {
|
||||||
if (typeof this.routeFile === 'string') {
|
if (typeof this.routeFile === 'string') {
|
||||||
fs.unwatchFile(this.routeFile);
|
fs.unwatchFile(this.routeFile);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.ProxyTable = ProxyTable;
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* forward-proxy-test.js: Tests for node-http-proxy forwarding functionality.
|
|
||||||
*
|
|
||||||
* (C) 2010, Charlie Robbins
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
var fs = require('fs'),
|
|
||||||
vows = require('vows'),
|
|
||||||
util = require('util'),
|
|
||||||
path = require('path'),
|
|
||||||
request = require('request'),
|
|
||||||
assert = require('assert'),
|
|
||||||
helpers = require('./helpers');
|
|
||||||
|
|
||||||
var runner = new helpers.TestRunner(),
|
|
||||||
assertProxiedWithTarget = helpers.assertProxiedWithTarget,
|
|
||||||
assertProxiedWithNoTarget = helpers.assertProxiedWithNoTarget;
|
|
||||||
|
|
||||||
var forwardOptions = {
|
|
||||||
forward: {
|
|
||||||
port: 8300,
|
|
||||||
host: 'localhost'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var badForwardOptions = {
|
|
||||||
forward: {
|
|
||||||
port: 9000,
|
|
||||||
host: 'localhost'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
vows.describe('node-http-proxy/forward-proxy').addBatch({
|
|
||||||
"When using server created by httpProxy.createServer()": {
|
|
||||||
"with forwarding enabled": {
|
|
||||||
topic: function () {
|
|
||||||
runner.startTargetServer(8300, 'forward proxy');
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
"with no latency" : {
|
|
||||||
"and a valid target server": assertProxiedWithTarget(runner, 'localhost', 8120, 8121, function () {
|
|
||||||
runner.startProxyServerWithForwarding(8120, 8121, 'localhost', forwardOptions);
|
|
||||||
}),
|
|
||||||
"and without a valid forward server": assertProxiedWithTarget(runner, 'localhost', 8122, 8123, function () {
|
|
||||||
runner.startProxyServerWithForwarding(8122, 8123, 'localhost', badForwardOptions);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).addBatch({
|
|
||||||
"When the tests are over": {
|
|
||||||
topic: function () {
|
|
||||||
return runner.closeServers();
|
|
||||||
},
|
|
||||||
"the servers should clean up": function () {
|
|
||||||
assert.isTrue(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).export(module);
|
|
||||||
110
test/helpers.js
110
test/helpers.js
@ -17,7 +17,7 @@ exports.assertProxiedWithTarget = function (runner, host, proxyPort, port, creat
|
|||||||
|
|
||||||
var test = {
|
var test = {
|
||||||
topic: function () {
|
topic: function () {
|
||||||
var options = {
|
var that = this, options = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
uri: 'http://localhost:' + proxyPort,
|
uri: 'http://localhost:' + proxyPort,
|
||||||
headers: {
|
headers: {
|
||||||
@ -25,13 +25,22 @@ exports.assertProxiedWithTarget = function (runner, host, proxyPort, port, creat
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (createProxy) createProxy();
|
function startTest () {
|
||||||
if (port) runner.startTargetServer(port, output);
|
if (port) {
|
||||||
request(options, this.callback);
|
return runner.startTargetServer(port, output, function () {
|
||||||
|
request(options, that.callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
request(options, this.callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return createProxy ? createProxy(startTest) : startTest();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
test[assertion] = function (err, res, body) {
|
test[assertion] = function (err, res, body) {;
|
||||||
|
assert.isNull(err);
|
||||||
assert.equal(body, output);
|
assert.equal(body, output);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,7 +52,7 @@ exports.assertProxiedWithNoTarget = function (runner, proxyPort, statusCode, cre
|
|||||||
|
|
||||||
var test = {
|
var test = {
|
||||||
topic: function () {
|
topic: function () {
|
||||||
var options = {
|
var that = this, options = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
uri: 'http://localhost:' + proxyPort,
|
uri: 'http://localhost:' + proxyPort,
|
||||||
headers: {
|
headers: {
|
||||||
@ -51,12 +60,18 @@ exports.assertProxiedWithNoTarget = function (runner, proxyPort, statusCode, cre
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (createProxy) createProxy();
|
if (createProxy) {
|
||||||
|
return createProxy(function () {
|
||||||
|
request(options, that.callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
request(options, this.callback);
|
request(options, this.callback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
test[assertion] = function (err, res, body) {
|
test[assertion] = function (err, res, body) {
|
||||||
|
assert.isNull(err);
|
||||||
assert.equal(res.statusCode, statusCode);
|
assert.equal(res.statusCode, statusCode);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,89 +80,98 @@ exports.assertProxiedWithNoTarget = function (runner, proxyPort, statusCode, cre
|
|||||||
|
|
||||||
var TestRunner = exports.TestRunner = function () {
|
var TestRunner = exports.TestRunner = function () {
|
||||||
this.testServers = [];
|
this.testServers = [];
|
||||||
}
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Creates the reverse proxy server
|
// Creates the reverse proxy server
|
||||||
//
|
//
|
||||||
TestRunner.prototype.startProxyServer = function (port, targetPort, host) {
|
TestRunner.prototype.startProxyServer = function (port, targetPort, host, callback) {
|
||||||
var proxyServer = httpProxy.createServer(targetPort, host);
|
var that = this, proxyServer = httpProxy.createServer(targetPort, host);
|
||||||
proxyServer.listen(port);
|
|
||||||
this.testServers.push(proxyServer);
|
proxyServer.listen(port, function () {
|
||||||
return proxyServer;
|
that.testServers.push(proxyServer);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Creates the reverse proxy server with a specified latency
|
// Creates the reverse proxy server with a specified latency
|
||||||
//
|
//
|
||||||
TestRunner.prototype.startLatentProxyServer = function (port, targetPort, host, latency) {
|
TestRunner.prototype.startLatentProxyServer = function (port, targetPort, host, latency, callback) {
|
||||||
// Initialize the nodeProxy and start proxying the request
|
// Initialize the nodeProxy and start proxying the request
|
||||||
var proxyServer = httpProxy.createServer(function (req, res, proxy) {
|
var that = this, proxyServer = httpProxy.createServer(function (req, res, proxy) {
|
||||||
|
var data = proxy.pause(req);
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
proxy.proxyRequest(targetPort, host);
|
proxy.proxyRequest(req, res, targetPort, host, data);
|
||||||
}, latency);
|
}, latency);
|
||||||
});
|
});
|
||||||
|
|
||||||
proxyServer.listen(port);
|
proxyServer.listen(port, function () {
|
||||||
this.testServers.push(proxyServer);
|
that.testServers.push(proxyServer);
|
||||||
return proxyServer;
|
callback();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Creates the reverse proxy server with a ProxyTable
|
// Creates the reverse proxy server with a ProxyTable
|
||||||
//
|
//
|
||||||
TestRunner.prototype.startProxyServerWithTable = function (port, options) {
|
TestRunner.prototype.startProxyServerWithTable = function (port, options, callback) {
|
||||||
var proxyServer = httpProxy.createServer(options);
|
var that = this, proxyServer = httpProxy.createServer(options);
|
||||||
proxyServer.listen(port);
|
proxyServer.listen(port, function () {
|
||||||
this.testServers.push(proxyServer);
|
that.testServers.push(proxyServer);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
|
||||||
return proxyServer;
|
return proxyServer;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Creates a latent reverse proxy server using a ProxyTable
|
// Creates a latent reverse proxy server using a ProxyTable
|
||||||
//
|
//
|
||||||
TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, latency, router) {
|
TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, latency, options, callback) {
|
||||||
// Initialize the nodeProxy and start proxying the request
|
// Initialize the nodeProxy and start proxying the request
|
||||||
var proxyTable = new httpProxy.ProxyTable(router);
|
var proxyServer, that = this, proxy = new httpProxy.HttpProxy(options);
|
||||||
var proxyServer = http.createServer(function (req, res) {
|
proxyServer = http.createServer(function (req, res) {
|
||||||
var proxy = new httpProxy.HttpProxy(req, res);
|
var paused = proxy.pause(req);
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
proxyTable.proxyRequest(proxy);
|
proxy.proxyRequest(req, res, paused);
|
||||||
}, latency);
|
}, latency);
|
||||||
});
|
});
|
||||||
|
|
||||||
proxyServer.on('close', function () {
|
proxyServer.listen(port, function () {
|
||||||
proxyTable.close();
|
that.testServers.push(proxyServer);
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
proxyServer.listen(port);
|
|
||||||
this.testServers.push(proxyServer);
|
|
||||||
return proxyServer;
|
return proxyServer;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Creates proxy server forwarding to the specified options
|
// Creates proxy server forwarding to the specified options
|
||||||
//
|
//
|
||||||
TestRunner.prototype.startProxyServerWithForwarding = function (port, targetPort, host, options) {
|
TestRunner.prototype.startProxyServerWithForwarding = function (port, targetPort, host, options, callback) {
|
||||||
var proxyServer = httpProxy.createServer(targetPort, host, options);
|
var that = this, proxyServer = httpProxy.createServer(targetPort, host, options);
|
||||||
proxyServer.listen(port);
|
proxyServer.listen(port, function () {
|
||||||
this.testServers.push(proxyServer);
|
that.testServers.push(proxyServer);
|
||||||
return proxyServer;
|
callback();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Creates the 'hellonode' server
|
// Creates the 'hellonode' server
|
||||||
//
|
//
|
||||||
TestRunner.prototype.startTargetServer = function (port, output) {
|
TestRunner.prototype.startTargetServer = function (port, output, callback) {
|
||||||
var targetServer = http.createServer(function (req, res) {
|
var that = this, targetServer = http.createServer(function (req, res) {
|
||||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||||
res.write(output)
|
res.write(output);
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
targetServer.listen(port);
|
targetServer.listen(port, function () {
|
||||||
this.testServers.push(targetServer);
|
that.testServers.push(targetServer);
|
||||||
return targetServer;
|
callback();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@ -30,6 +30,20 @@ var vows = require('vows'),
|
|||||||
assert = require('assert'),
|
assert = require('assert'),
|
||||||
helpers = require('./helpers');
|
helpers = require('./helpers');
|
||||||
|
|
||||||
|
var forwardOptions = {
|
||||||
|
forward: {
|
||||||
|
port: 8300,
|
||||||
|
host: 'localhost'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var badForwardOptions = {
|
||||||
|
forward: {
|
||||||
|
port: 9000,
|
||||||
|
host: 'localhost'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var runner = new helpers.TestRunner(),
|
var runner = new helpers.TestRunner(),
|
||||||
assertProxiedWithTarget = helpers.assertProxiedWithTarget,
|
assertProxiedWithTarget = helpers.assertProxiedWithTarget,
|
||||||
assertProxiedWithNoTarget = helpers.assertProxiedWithNoTarget;
|
assertProxiedWithNoTarget = helpers.assertProxiedWithNoTarget;
|
||||||
@ -37,20 +51,33 @@ var runner = new helpers.TestRunner(),
|
|||||||
vows.describe('node-http-proxy').addBatch({
|
vows.describe('node-http-proxy').addBatch({
|
||||||
"When using server created by httpProxy.createServer()": {
|
"When using server created by httpProxy.createServer()": {
|
||||||
"with no latency" : {
|
"with no latency" : {
|
||||||
"and a valid target server": assertProxiedWithTarget(runner, 'localhost', 8080, 8081, function () {
|
"and a valid target server": assertProxiedWithTarget(runner, 'localhost', 8080, 8081, function (callback) {
|
||||||
runner.startProxyServer(8080, 8081, 'localhost');
|
runner.startProxyServer(8080, 8081, 'localhost', callback);
|
||||||
}),
|
}),
|
||||||
"and without a valid target server": assertProxiedWithNoTarget(runner, 8082, 500, function () {
|
"and without a valid target server": assertProxiedWithNoTarget(runner, 8082, 500, function (callback) {
|
||||||
runner.startProxyServer(8082, 9000, 'localhost');
|
runner.startProxyServer(8082, 9000, 'localhost', callback);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
"with latency": {
|
"with latency": {
|
||||||
"and a valid target server": assertProxiedWithTarget(runner, 'localhost', 8083, 8084, function () {
|
"and a valid target server": assertProxiedWithTarget(runner, 'localhost', 8083, 8084, function (callback) {
|
||||||
runner.startLatentProxyServer(8083, 8084, 'localhost', 1000);
|
runner.startLatentProxyServer(8083, 8084, 'localhost', 1000, callback);
|
||||||
}),
|
}),
|
||||||
"and without a valid target server": assertProxiedWithNoTarget(runner, 8085, 500, function () {
|
"and without a valid target server": assertProxiedWithNoTarget(runner, 8085, 500, function (callback) {
|
||||||
runner.startLatentProxyServer(8085, 9000, 'localhost', 1000);
|
runner.startLatentProxyServer(8085, 9000, 'localhost', 1000, callback);
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
"with forwarding enabled": {
|
||||||
|
topic: function () {
|
||||||
|
runner.startTargetServer(8300, 'forward proxy', this.callback);
|
||||||
|
},
|
||||||
|
"with no latency" : {
|
||||||
|
"and a valid target server": assertProxiedWithTarget(runner, 'localhost', 8120, 8121, function (callback) {
|
||||||
|
runner.startProxyServerWithForwarding(8120, 8121, 'localhost', forwardOptions, callback);
|
||||||
|
}),
|
||||||
|
"and without a valid forward server": assertProxiedWithTarget(runner, 'localhost', 8122, 8123, function (callback) {
|
||||||
|
runner.startProxyServerWithForwarding(8122, 8123, 'localhost', badForwardOptions, callback);
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).addBatch({
|
}).addBatch({
|
||||||
|
|||||||
@ -37,8 +37,7 @@ vows.describe('node-http-proxy/proxy-table').addBatch({
|
|||||||
"When using server created by httpProxy.createServer()": {
|
"When using server created by httpProxy.createServer()": {
|
||||||
"when passed a routing table": {
|
"when passed a routing table": {
|
||||||
topic: function () {
|
topic: function () {
|
||||||
this.server = runner.startProxyServerWithTable(8090, defaultOptions);
|
this.server = runner.startProxyServerWithTable(8090, defaultOptions, this.callback);
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
"an incoming request to foo.com": assertProxiedWithTarget(runner, 'foo.com', 8090, 8091),
|
"an incoming request to foo.com": assertProxiedWithTarget(runner, 'foo.com', 8090, 8091),
|
||||||
"an incoming request to bar.com": assertProxiedWithTarget(runner, 'bar.com', 8090, 8092),
|
"an incoming request to bar.com": assertProxiedWithTarget(runner, 'bar.com', 8090, 8092),
|
||||||
@ -49,9 +48,7 @@ vows.describe('node-http-proxy/proxy-table').addBatch({
|
|||||||
fs.writeFileSync(routeFile, JSON.stringify(fileOptions));
|
fs.writeFileSync(routeFile, JSON.stringify(fileOptions));
|
||||||
this.server = runner.startProxyServerWithTable(8100, {
|
this.server = runner.startProxyServerWithTable(8100, {
|
||||||
router: routeFile
|
router: routeFile
|
||||||
});
|
}, this.callback);
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
"an incoming request to foo.com": assertProxiedWithTarget(runner, 'foo.com', 8100, 8101),
|
"an incoming request to foo.com": assertProxiedWithTarget(runner, 'foo.com', 8100, 8101),
|
||||||
"an incoming request to bar.com": assertProxiedWithTarget(runner, 'bar.com', 8100, 8102),
|
"an incoming request to bar.com": assertProxiedWithTarget(runner, 'bar.com', 8100, 8102),
|
||||||
@ -67,7 +64,7 @@ vows.describe('node-http-proxy/proxy-table').addBatch({
|
|||||||
config.router['dynamic.com'] = "127.0.0.1:8103"
|
config.router['dynamic.com'] = "127.0.0.1:8103"
|
||||||
fs.writeFileSync(routeFile, JSON.stringify(config));
|
fs.writeFileSync(routeFile, JSON.stringify(config));
|
||||||
|
|
||||||
this.server.on('updateRoutes', function () {
|
this.server.on('routes', function () {
|
||||||
var options = {
|
var options = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
uri: 'http://localhost:8100',
|
uri: 'http://localhost:8100',
|
||||||
@ -93,8 +90,7 @@ vows.describe('node-http-proxy/proxy-table').addBatch({
|
|||||||
this.server = runner.startProxyServerWithTableAndLatency(8110, 100, {
|
this.server = runner.startProxyServerWithTableAndLatency(8110, 100, {
|
||||||
'foo.com': 'localhost:8111',
|
'foo.com': 'localhost:8111',
|
||||||
'bar.com': 'localhost:8112'
|
'bar.com': 'localhost:8112'
|
||||||
});
|
}, this.callback);
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
"an incoming request to foo.com": assertProxiedWithTarget(runner, 'foo.com', 8110, 8111),
|
"an incoming request to foo.com": assertProxiedWithTarget(runner, 'foo.com', 8110, 8111),
|
||||||
"an incoming request to bar.com": assertProxiedWithTarget(runner, 'bar.com', 8110, 8112),
|
"an incoming request to bar.com": assertProxiedWithTarget(runner, 'bar.com', 8110, 8112),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user