mirror of
https://github.com/http-party/node-http-proxy.git
synced 2025-12-08 20:59:18 +00:00
[merge] Merge from significant internal refactor in v0.7.x. No external API changes
This commit is contained in:
commit
f7010e5169
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,3 @@
|
||||
test/config.json
|
||||
config.json
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
|
||||
@ -114,7 +114,7 @@ httpProxy.createServer(function (req, res, proxy) {
|
||||
// Buffer the request so that `data` and `end` events
|
||||
// are not lost during async operation(s).
|
||||
//
|
||||
var buffer = proxy.buffer(req);
|
||||
var buffer = httpProxy.buffer(req);
|
||||
|
||||
//
|
||||
// Wait for two seconds then respond: this simulates
|
||||
@ -357,6 +357,13 @@ server.on('upgrade', function(req, socket, head) {
|
||||
});
|
||||
```
|
||||
|
||||
### Configuring your Socket limits
|
||||
|
||||
By default, `node-http-proxy` will set a 100 socket limit for all `host:port` proxy targets. If you wish to change this you can two it in two ways:
|
||||
|
||||
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.
|
||||
|
||||
## 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:
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ var path = require('path'),
|
||||
fs = require('fs'),
|
||||
util = require('util'),
|
||||
argv = require('optimist').argv,
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../lib/node-http-proxy');
|
||||
|
||||
var help = [
|
||||
"usage: node-http-proxy [options] ",
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
|
||||
module.exports = Store
|
||||
//
|
||||
// just to make these example a little bit interesting,
|
||||
// make a little key value store with an http interface
|
||||
@ -20,12 +19,10 @@ module.exports = Store
|
||||
//
|
||||
// TODO: cached map-reduce views and auto-magic sharding.
|
||||
//
|
||||
var Store = module.exports = function Store () {
|
||||
this.store = {};
|
||||
};
|
||||
|
||||
|
||||
|
||||
function Store () {
|
||||
this.store = {}
|
||||
}
|
||||
Store.prototype = {
|
||||
get: function (key) {
|
||||
return this.store[key]
|
||||
@ -27,16 +27,17 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
var welcome = [
|
||||
'# # ##### ##### ##### ##### ##### #### # # # #',
|
||||
'# # # # # # # # # # # # # # # # ',
|
||||
'###### # # # # ##### # # # # # # ## # ',
|
||||
'# # # # ##### ##### ##### # # ## # ',
|
||||
'# # # # # # # # # # # # # ',
|
||||
'# # # # # # # # #### # # # '
|
||||
].join('\n');
|
||||
|
||||
// ascii art from http://github.com/marak/asciimo
|
||||
var welcome = '\
|
||||
# # ##### ##### ##### ##### ##### #### # # # # \n\
|
||||
# # # # # # # # # # # # # # # # \n\
|
||||
###### # # # # ##### # # # # # # ## # \n\
|
||||
# # # # ##### ##### ##### # # ## # \n\
|
||||
# # # # # # # # # # # # # \n\
|
||||
# # # # # # # # #### # # # \n';
|
||||
util.puts(welcome.rainbow.bold);
|
||||
|
||||
//
|
||||
@ -27,7 +27,7 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
//
|
||||
// Basic Http Proxy Server
|
||||
@ -42,23 +42,24 @@ httpProxy.createServer(9000, 'localhost').listen(8000);
|
||||
//
|
||||
|
||||
|
||||
var connections = []
|
||||
, go
|
||||
var connections = [],
|
||||
go;
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
|
||||
connections.push(function () {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
})
|
||||
process.stdout.write(connections.length + ', ')
|
||||
if (connections.length > 110 || go) {
|
||||
go = true
|
||||
while(connections.length)
|
||||
connections.shift()()
|
||||
}
|
||||
});
|
||||
|
||||
process.stdout.write(connections.length + ', ');
|
||||
|
||||
if (connections.length > 110 || go) {
|
||||
go = true;
|
||||
while (connections.length) {
|
||||
connections.shift()();
|
||||
}
|
||||
}
|
||||
}).listen(9000);
|
||||
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
||||
@ -27,17 +27,12 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
//
|
||||
// Http Proxy Server with Latency
|
||||
//
|
||||
var server = httpProxy.createServer(function (req, res, proxy) {
|
||||
proxy.proxyRequest(req, res, {
|
||||
port: 9000,
|
||||
host: 'localhost'
|
||||
});
|
||||
})
|
||||
var server = httpProxy.createServer(9000, 'localhost');
|
||||
|
||||
//
|
||||
// Tell the server to listen on port 8002
|
||||
@ -27,7 +27,7 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
//
|
||||
// Setup proxy server with forwarding
|
||||
@ -27,13 +27,13 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
//
|
||||
// Http Proxy Server with Latency
|
||||
//
|
||||
httpProxy.createServer(function (req, res, proxy) {
|
||||
var buffer = proxy.buffer(req);
|
||||
var buffer = httpProxy.buffer(req);
|
||||
setTimeout(function() {
|
||||
proxy.proxyRequest(req, res, {
|
||||
port: 9000,
|
||||
@ -28,7 +28,7 @@ var https = require('https'),
|
||||
http = require('http'),
|
||||
util = require('util'),
|
||||
colors = require('colors'),
|
||||
httpProxy = require('./../lib/node-http-proxy'),
|
||||
httpProxy = require('../../lib/node-http-proxy'),
|
||||
helpers = require('./../test/helpers');
|
||||
|
||||
var opts = helpers.loadHttps();
|
||||
@ -28,7 +28,7 @@ var https = require('https'),
|
||||
http = require('http'),
|
||||
util = require('util'),
|
||||
colors = require('colors'),
|
||||
httpProxy = require('./../lib/node-http-proxy'),
|
||||
httpProxy = require('../../lib/node-http-proxy'),
|
||||
helpers = require('./../test/helpers');
|
||||
|
||||
var opts = helpers.loadHttps();
|
||||
@ -27,7 +27,7 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
//
|
||||
// Http Proxy Server with Proxy Table
|
||||
@ -27,14 +27,14 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
//
|
||||
// Http Server with proxyRequest Handler and Latency
|
||||
//
|
||||
var proxy = new httpProxy.HttpProxy();
|
||||
http.createServer(function (req, res) {
|
||||
var buffer = proxy.buffer(req);
|
||||
var buffer = httpProxy.buffer(req);
|
||||
setTimeout(function() {
|
||||
proxy.proxyRequest(req, res, {
|
||||
port: 9000,
|
||||
@ -27,7 +27,7 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
//
|
||||
// Basic Http Proxy Server
|
||||
@ -19,8 +19,7 @@ httpProxy.createServer(
|
||||
//
|
||||
// Target Http Server (to listen for requests on 'localhost')
|
||||
//
|
||||
http.createServer(
|
||||
function (req, res) {
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "http-proxy-examples"
|
||||
, "description": "packages required to run the examples"
|
||||
, "version": "0.0.0"
|
||||
, "dependencies": {
|
||||
"connect": "1.6"
|
||||
, "connect-gzip": "0.1"
|
||||
, "connect-jsonp": "0.0.5"
|
||||
, "connect-restreamer": "1"
|
||||
, "proxy-by-url": ">= 0.0.1"
|
||||
"name": "http-proxy-examples",
|
||||
"description": "packages required to run the examples",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"connect": "1.6",
|
||||
"connect-gzip": "0.1",
|
||||
"connect-jsonp": "0.0.5",
|
||||
"connect-restreamer": "1",
|
||||
"proxy-by-url": ">= 0.0.1"
|
||||
}
|
||||
}
|
||||
@ -28,7 +28,7 @@ var sys = require('sys'),
|
||||
http = require('http'),
|
||||
colors = require('colors'),
|
||||
websocket = require('./../vendor/websocket'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
try {
|
||||
var utils = require('socket.io/lib/socket.io/utils'),
|
||||
@ -80,7 +80,7 @@ var proxyServer = http.createServer(function (req, res) {
|
||||
// WebSocket requests as well.
|
||||
//
|
||||
proxyServer.on('upgrade', function (req, socket, head) {
|
||||
var buffer = proxy.buffer(socket);
|
||||
var buffer = httpProxy.buffer(socket);
|
||||
|
||||
setTimeout(function () {
|
||||
proxy.proxyWebSocketRequest(req, socket, head, {
|
||||
@ -28,7 +28,7 @@ var sys = require('sys'),
|
||||
http = require('http'),
|
||||
colors = require('colors'),
|
||||
websocket = require('./../vendor/websocket'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
try {
|
||||
var utils = require('socket.io/lib/socket.io/utils'),
|
||||
@ -28,7 +28,7 @@ var sys = require('sys'),
|
||||
http = require('http'),
|
||||
colors = require('colors'),
|
||||
websocket = require('./../vendor/websocket'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
try {
|
||||
var utils = require('socket.io/lib/socket.io/utils'),
|
||||
File diff suppressed because it is too large
Load Diff
766
lib/node-http-proxy/http-proxy.js
Normal file
766
lib/node-http-proxy/http-proxy.js
Normal file
@ -0,0 +1,766 @@
|
||||
/*
|
||||
node-http-proxy.js: http proxy for node.js
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Marak Squires, Fedor Indutny
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
var events = require('events'),
|
||||
util = require('util'),
|
||||
httpProxy = require('../node-http-proxy');
|
||||
|
||||
//
|
||||
// ### function HttpProxy (options)
|
||||
// #### @options {Object} Options for this instance.
|
||||
// Constructor function for new instances of HttpProxy responsible
|
||||
// for managing the life-cycle of streaming reverse proxyied HTTP requests.
|
||||
//
|
||||
// Example options:
|
||||
//
|
||||
// {
|
||||
// target: {
|
||||
// host: 'localhost',
|
||||
// port: 9000
|
||||
// },
|
||||
// forward: {
|
||||
// host: 'localhost',
|
||||
// port: 9001
|
||||
// }
|
||||
// }
|
||||
//
|
||||
var HttpProxy = exports.HttpProxy = function (options) {
|
||||
if (!options || !options.target) {
|
||||
throw new Error('Both `options` and `options.target` are required.');
|
||||
}
|
||||
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
var self = this;
|
||||
|
||||
//
|
||||
// Setup basic proxying options:
|
||||
//
|
||||
// * forward {Object} Options for a forward-proxy (if-any)
|
||||
// * target {Object} Options for the **sole** proxy target of this instance
|
||||
//
|
||||
this.forward = options.forward;
|
||||
this.target = options.target;
|
||||
|
||||
//
|
||||
// Setup the necessary instances instance variables for
|
||||
// the `target` and `forward` `host:port` combinations
|
||||
// used by this instance.
|
||||
//
|
||||
// * agent {http[s].Agent} Agent to be used by this instance.
|
||||
// * protocol {http|https} Core node.js module to make requests with.
|
||||
// * base {Object} Base object to create when proxying containing any https settings.
|
||||
//
|
||||
function setupProxy (key) {
|
||||
self[key].agent = httpProxy._getAgent(self[key]);
|
||||
self[key].protocol = httpProxy._getProtocol(self[key]);
|
||||
self[key].base = httpProxy._getBase(self[key]);
|
||||
}
|
||||
|
||||
setupProxy('target');
|
||||
if (this.forward) {
|
||||
setupProxy('forward');
|
||||
}
|
||||
|
||||
//
|
||||
// Setup opt-in features
|
||||
//
|
||||
this.enable = options.enable || {};
|
||||
this.enable.xforward = typeof this.enable.xforward === 'boolean'
|
||||
? this.enable.xforward
|
||||
: true;
|
||||
|
||||
//
|
||||
// Setup additional options for WebSocket proxying. When forcing
|
||||
// the WebSocket handshake to change the `sec-websocket-location`
|
||||
// and `sec-websocket-origin` headers `options.source` **MUST**
|
||||
// be provided or the operation will fail with an `origin mismatch`
|
||||
// by definition.
|
||||
//
|
||||
this.source = options.source || { host: 'localhost', port: 8000 };
|
||||
this.source.https = this.source.https || options.https;
|
||||
this.changeOrigin = options.changeOrigin || false;
|
||||
};
|
||||
|
||||
// Inherit from events.EventEmitter
|
||||
util.inherits(HttpProxy, events.EventEmitter);
|
||||
|
||||
//
|
||||
// ### function proxyRequest (req, res, [port, host, paused])
|
||||
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
|
||||
// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
|
||||
// #### @buffer {Object} Result from `httpProxy.buffer(req)`
|
||||
//
|
||||
HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
|
||||
var self = this,
|
||||
errState = false,
|
||||
outgoing = new(this.target.base),
|
||||
reverseProxy;
|
||||
|
||||
//
|
||||
// Add common proxy headers to the request so that they can
|
||||
// be availible to the proxy target server:
|
||||
//
|
||||
// * `x-forwarded-for`: IP Address of the original request
|
||||
// * `x-forwarded-proto`: Protocol of the original request
|
||||
// * `x-forwarded-port`: Port of the original request.
|
||||
//
|
||||
if (this.enable.xforward && req.connection && req.connection.socket) {
|
||||
req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.connection.socket.remoteAddress;
|
||||
req.headers['x-forwarded-port'] = req.connection.remotePort || req.connection.socket.remotePort;
|
||||
req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http';
|
||||
}
|
||||
|
||||
//
|
||||
// Emit the `start` event indicating that we have begun the proxy operation.
|
||||
//
|
||||
this.emit('start', req, res, this.target);
|
||||
|
||||
//
|
||||
// If forwarding is enabled for this instance, foward proxy the
|
||||
// specified request to the address provided in `this.forward`
|
||||
//
|
||||
if (this.forward) {
|
||||
this.emit('forward', req, res, this.forward);
|
||||
this._forwardRequest(req);
|
||||
}
|
||||
|
||||
//
|
||||
// #### function proxyError (err)
|
||||
// #### @err {Error} Error contacting the proxy target
|
||||
// Short-circuits `res` in the event of any error when
|
||||
// contacting the proxy target at `host` / `port`.
|
||||
//
|
||||
function proxyError(err) {
|
||||
errState = true;
|
||||
|
||||
//
|
||||
// Emit an `error` event, allowing the application to use custom
|
||||
// error handling. The error handler should end the response.
|
||||
//
|
||||
if (self.emit('proxyError', err, req, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||
|
||||
if (req.method !== 'HEAD') {
|
||||
//
|
||||
// This NODE_ENV=production behavior is mimics Express and
|
||||
// Connect.
|
||||
//
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
res.write('Internal Server Error');
|
||||
}
|
||||
else {
|
||||
res.write('An error has occurred: ' + JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
|
||||
try { res.end() }
|
||||
catch (ex) { console.error("res.end error: %s", ex.message) }
|
||||
}
|
||||
|
||||
//
|
||||
// Setup outgoing proxy with relevant properties.
|
||||
//
|
||||
outgoing.host = this.target.host;
|
||||
outgoing.port = this.target.port;
|
||||
outgoing.agent = this.target.agent;
|
||||
outgoing.method = req.method;
|
||||
outgoing.path = req.url;
|
||||
outgoing.headers = req.headers;
|
||||
|
||||
//
|
||||
// Open new HTTP request to internal resource with will act
|
||||
// as a reverse proxy pass
|
||||
//
|
||||
reverseProxy = this.target.protocol.request(outgoing, function (response) {
|
||||
//
|
||||
// Process the `reverseProxy` `response` when it's received.
|
||||
//
|
||||
if (response.headers.connection) {
|
||||
if (req.headers.connection) { response.headers.connection = req.headers.connection }
|
||||
else { response.headers.connection = 'close' }
|
||||
}
|
||||
|
||||
// Set the headers of the client response
|
||||
res.writeHead(response.statusCode, response.headers);
|
||||
|
||||
// If `response.statusCode === 304`: No 'data' event and no 'end'
|
||||
if (response.statusCode === 304) {
|
||||
try { res.end() }
|
||||
catch (ex) { console.error("res.end error: %s", ex.message) }
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// For each data `chunk` received from the `reverseProxy`
|
||||
// `response` write it to the outgoing `res`.
|
||||
// If the res socket has been killed already, then write()
|
||||
// will throw. Nevertheless, try our best to end it nicely.
|
||||
//
|
||||
response.on('data', function (chunk) {
|
||||
if (req.method !== 'HEAD' && res.writable) {
|
||||
try {
|
||||
var flushed = res.write(chunk);
|
||||
}
|
||||
catch (ex) {
|
||||
console.error("res.write error: %s", ex.message);
|
||||
|
||||
try { res.end() }
|
||||
catch (ex) { console.error("res.end error: %s", ex.message) }
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!flushed) {
|
||||
response.pause();
|
||||
res.once('drain', function () {
|
||||
try { response.resume() }
|
||||
catch (er) { console.error("response.resume error: %s", er.message) }
|
||||
});
|
||||
|
||||
//
|
||||
// Force the `drain` event in 100ms if it hasn't
|
||||
// happened on its own.
|
||||
//
|
||||
setTimeout(function () {
|
||||
res.emit('drain');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// When the `reverseProxy` `response` ends, end the
|
||||
// corresponding outgoing `res` unless we have entered
|
||||
// an error state. In which case, assume `res.end()` has
|
||||
// already been called and the 'error' event listener
|
||||
// removed.
|
||||
//
|
||||
response.on('end', function () {
|
||||
if (!errState) {
|
||||
reverseProxy.removeListener('error', proxyError);
|
||||
|
||||
try { res.end() }
|
||||
catch (ex) { console.error("res.end error: %s", ex.message) }
|
||||
|
||||
// Emit the `end` event now that we have completed proxying
|
||||
self.emit('end', req, res);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
// Handle 'error' events from the `reverseProxy`.
|
||||
//
|
||||
reverseProxy.once('error', proxyError);
|
||||
|
||||
//
|
||||
// For each data `chunk` received from the incoming
|
||||
// `req` write it to the `reverseProxy` request.
|
||||
//
|
||||
req.on('data', function (chunk) {
|
||||
if (!errState) {
|
||||
var flushed = reverseProxy.write(chunk);
|
||||
if (!flushed) {
|
||||
req.pause();
|
||||
reverseProxy.once('drain', function () {
|
||||
try { req.resume() }
|
||||
catch (er) { console.error("req.resume error: %s", er.message) }
|
||||
});
|
||||
|
||||
//
|
||||
// Force the `drain` event in 100ms if it hasn't
|
||||
// happened on its own.
|
||||
//
|
||||
setTimeout(function () {
|
||||
reverseProxy.emit('drain');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// When the incoming `req` ends, end the corresponding `reverseProxy`
|
||||
// request unless we have entered an error state.
|
||||
//
|
||||
req.on('end', function () {
|
||||
if (!errState) {
|
||||
reverseProxy.end();
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// If we have been passed buffered data, resume it.
|
||||
//
|
||||
if (buffer) {
|
||||
return !errState
|
||||
? buffer.resume()
|
||||
: buffer.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// ### function proxyWebSocketRequest (req, socket, head, buffer)
|
||||
// #### @req {ServerRequest} Websocket request to proxy.
|
||||
// #### @socket {net.Socket} Socket for the underlying HTTP request
|
||||
// #### @head {string} Headers for the Websocket request.
|
||||
// #### @buffer {Object} Result from `httpProxy.buffer(req)`
|
||||
// Performs a WebSocket proxy operation to the location specified by
|
||||
// `this.target`.
|
||||
//
|
||||
HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, head, buffer) {
|
||||
var self = this,
|
||||
outgoing = new(this.target.base),
|
||||
listeners = {},
|
||||
errState = false,
|
||||
CRLF = '\r\n';
|
||||
|
||||
//
|
||||
// WebSocket requests must have the `GET` method and
|
||||
// the `upgrade:websocket` header
|
||||
//
|
||||
if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') {
|
||||
//
|
||||
// This request is not WebSocket request
|
||||
//
|
||||
return socket.destroy();
|
||||
}
|
||||
|
||||
//
|
||||
// Add common proxy headers to the request so that they can
|
||||
// be availible to the proxy target server:
|
||||
//
|
||||
// * `x-forwarded-for`: IP Address of the original request
|
||||
// * `x-forwarded-proto`: Protocol of the original request
|
||||
// * `x-forwarded-port`: Port of the original request.
|
||||
//
|
||||
if (this.enable.xforward && req.connection && req.connection.socket) {
|
||||
req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.connection.socket.remoteAddress;
|
||||
req.headers['x-forwarded-port'] = req.connection.remotePort || req.connection.socket.remotePort;
|
||||
req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http';
|
||||
}
|
||||
|
||||
//
|
||||
// Helper function for setting appropriate socket values:
|
||||
// 1. Turn of all bufferings
|
||||
// 2. For server set KeepAlive
|
||||
// 3. For client set encoding
|
||||
//
|
||||
function _socket(socket, keepAlive) {
|
||||
socket.setTimeout(0);
|
||||
socket.setNoDelay(true);
|
||||
|
||||
if (keepAlive) {
|
||||
if (socket.setKeepAlive) {
|
||||
socket.setKeepAlive(true, 0);
|
||||
}
|
||||
else if (socket.pair.cleartext.socket.setKeepAlive) {
|
||||
socket.pair.cleartext.socket.setKeepAlive(true, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
socket.setEncoding('utf8');
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Setup the incoming client socket.
|
||||
//
|
||||
_socket(socket);
|
||||
|
||||
//
|
||||
// On `upgrade` from the Agent socket, listen to
|
||||
// the appropriate events.
|
||||
//
|
||||
function onUpgrade (reverseProxy, proxySocket) {
|
||||
if (!reverseProxy) {
|
||||
proxySocket.end();
|
||||
socket.end();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Any incoming data on this WebSocket to the proxy target
|
||||
// will be written to the `reverseProxy` socket.
|
||||
//
|
||||
proxySocket.on('data', listeners.onIncoming = function (data) {
|
||||
if (reverseProxy.incoming.socket.writable) {
|
||||
try {
|
||||
self.emit('websocket:outgoing', req, socket, head, data);
|
||||
var flushed = reverseProxy.incoming.socket.write(data);
|
||||
if (!flushed) {
|
||||
proxySocket.pause();
|
||||
reverseProxy.incoming.socket.once('drain', function () {
|
||||
try { proxySocket.resume() }
|
||||
catch (er) { console.error("proxySocket.resume error: %s", er.message) }
|
||||
});
|
||||
|
||||
//
|
||||
// Force the `drain` event in 100ms if it hasn't
|
||||
// happened on its own.
|
||||
//
|
||||
setTimeout(function () {
|
||||
reverseProxy.incoming.socket.emit('drain');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
detach();
|
||||
reverseProxy.incoming.socket.end();
|
||||
proxySocket.end();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Any outgoing data on this Websocket from the proxy target
|
||||
// will be written to the `proxySocket` socket.
|
||||
//
|
||||
reverseProxy.incoming.socket.on('data', listeners.onOutgoing = function (data) {
|
||||
try {
|
||||
self.emit('websocket:incoming', reverseProxy, reverseProxy.incoming, head, data);
|
||||
var flushed = proxySocket.write(data);
|
||||
if (!flushed) {
|
||||
reverseProxy.incoming.socket.pause();
|
||||
proxySocket.once('drain', function () {
|
||||
try { reverseProxy.incoming.socket.resume() }
|
||||
catch (er) { console.error("reverseProxy.incoming.socket.resume error: %s", er.message) }
|
||||
});
|
||||
|
||||
//
|
||||
// Force the `drain` event in 100ms if it hasn't
|
||||
// happened on its own.
|
||||
//
|
||||
setTimeout(function () {
|
||||
proxySocket.emit('drain');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
detach();
|
||||
proxySocket.end();
|
||||
socket.end();
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Helper function to detach all event listeners
|
||||
// from `reverseProxy` and `proxySocket`.
|
||||
//
|
||||
function detach() {
|
||||
proxySocket.removeListener('end', listeners.onIncomingClose);
|
||||
proxySocket.removeListener('data', listeners.onIncoming);
|
||||
reverseProxy.incoming.socket.removeListener('end', listeners.onOutgoingClose);
|
||||
reverseProxy.incoming.socket.removeListener('data', listeners.onOutgoing);
|
||||
}
|
||||
|
||||
//
|
||||
// If the incoming `proxySocket` socket closes, then
|
||||
// detach all event listeners.
|
||||
//
|
||||
proxySocket.on('end', listeners.onIncomingClose = function() {
|
||||
reverseProxy.incoming.socket.end();
|
||||
detach();
|
||||
|
||||
// Emit the `end` event now that we have completed proxying
|
||||
self.emit('websocket:end', req, socket, head);
|
||||
});
|
||||
|
||||
//
|
||||
// If the `reverseProxy` socket closes, then detach all
|
||||
// event listeners.
|
||||
//
|
||||
reverseProxy.incoming.socket.on('end', listeners.onOutgoingClose = function() {
|
||||
proxySocket.end();
|
||||
detach();
|
||||
});
|
||||
};
|
||||
|
||||
function getPort (port) {
|
||||
port = port || 80;
|
||||
return port - 80 === 0 ? '' : ':' + port
|
||||
}
|
||||
|
||||
//
|
||||
// Get the protocol, and host for this request and create an instance
|
||||
// of `http.Agent` or `https.Agent` from the pool managed by `node-http-proxy`.
|
||||
//
|
||||
var agent = this.target.agent,
|
||||
protocolName = this.target.https ? 'https' : 'http',
|
||||
portUri = getPort(this.source.port),
|
||||
remoteHost = this.target.host + portUri;
|
||||
|
||||
//
|
||||
// Change headers (if requested).
|
||||
//
|
||||
if (this.changeOrigin) {
|
||||
req.headers.host = remoteHost;
|
||||
req.headers.origin = protocolName + '://' + remoteHost;
|
||||
}
|
||||
|
||||
//
|
||||
// Make the outgoing WebSocket request
|
||||
//
|
||||
outgoing.host = this.target.host;
|
||||
outgoing.port = this.target.port;
|
||||
outgoing.method = 'GET';
|
||||
outgoing.path = req.url;
|
||||
outgoing.headers = req.headers;
|
||||
|
||||
var reverseProxy = agent.appendMessage(outgoing);
|
||||
|
||||
//
|
||||
// On any errors from the `reverseProxy` emit the
|
||||
// `webSocketProxyError` and close the appropriate
|
||||
// connections.
|
||||
//
|
||||
function proxyError (err) {
|
||||
reverseProxy.end();
|
||||
if (self.emit('webSocketProxyError', req, socket, head)) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket.end();
|
||||
}
|
||||
|
||||
//
|
||||
// Here we set the incoming `req`, `socket` and `head` data to the outgoing
|
||||
// request so that we can reuse this data later on in the closure scope
|
||||
// available to the `upgrade` event. This bookkeeping is not tracked anywhere
|
||||
// in nodejs core and is **very** specific to proxying WebSockets.
|
||||
//
|
||||
reverseProxy.agent = agent;
|
||||
reverseProxy.incoming = {
|
||||
request: req,
|
||||
socket: socket,
|
||||
head: head
|
||||
};
|
||||
|
||||
//
|
||||
// If the agent for this particular `host` and `port` combination
|
||||
// is not already listening for the `upgrade` event, then do so once.
|
||||
// This will force us not to disconnect.
|
||||
//
|
||||
// In addition, it's important to note the closure scope here. Since
|
||||
// there is no mapping of the socket to the request bound to it.
|
||||
//
|
||||
if (!agent._events || agent._events['upgrade'].length === 0) {
|
||||
agent.on('upgrade', function (_, remoteSocket, head) {
|
||||
//
|
||||
// Prepare the socket for the reverseProxy request and begin to
|
||||
// stream data between the two sockets. Here it is important to
|
||||
// note that `remoteSocket._httpMessage === reverseProxy`.
|
||||
//
|
||||
_socket(remoteSocket, true);
|
||||
onUpgrade(remoteSocket._httpMessage, remoteSocket);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// If the reverseProxy connection has an underlying socket,
|
||||
// then execute the WebSocket handshake.
|
||||
//
|
||||
if (typeof reverseProxy.socket !== 'undefined') {
|
||||
reverseProxy.socket.on('data', function handshake (data) {
|
||||
//
|
||||
// Ok, kind of harmfull part of code. Socket.IO sends a hash
|
||||
// at the end of handshake if protocol === 76, but we need
|
||||
// to replace 'host' and 'origin' in response so we split
|
||||
// data to printable data and to non-printable. (Non-printable
|
||||
// will come after double-CRLF).
|
||||
//
|
||||
var sdata = data.toString();
|
||||
|
||||
// Get the Printable data
|
||||
sdata = sdata.substr(0, sdata.search(CRLF + CRLF));
|
||||
|
||||
// Get the Non-Printable data
|
||||
data = data.slice(Buffer.byteLength(sdata), data.length);
|
||||
|
||||
if (self.source.https && !self.target.https) {
|
||||
//
|
||||
// If the proxy server is running HTTPS but the client is running
|
||||
// HTTP then replace `ws` with `wss` in the data sent back to the client.
|
||||
//
|
||||
sdata = sdata.replace('ws:', 'wss:');
|
||||
}
|
||||
|
||||
try {
|
||||
//
|
||||
// Write the printable and non-printable data to the socket
|
||||
// from the original incoming request.
|
||||
//
|
||||
self.emit('websocket:handshake', req, socket, head, sdata, data);
|
||||
socket.write(sdata);
|
||||
var flushed = socket.write(data);
|
||||
if (!flushed) {
|
||||
reverseProxy.socket.pause();
|
||||
socket.once('drain', function () {
|
||||
try { reverseProxy.socket.resume() }
|
||||
catch (er) { console.error("reverseProxy.socket.resume error: %s", er.message) }
|
||||
});
|
||||
|
||||
//
|
||||
// Force the `drain` event in 100ms if it hasn't
|
||||
// happened on its own.
|
||||
//
|
||||
setTimeout(function () {
|
||||
socket.emit('drain');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
//
|
||||
// Remove data listener on socket error because the
|
||||
// 'handshake' has failed.
|
||||
//
|
||||
reverseProxy.socket.removeListener('data', handshake);
|
||||
return proxyError(ex);
|
||||
}
|
||||
|
||||
// Catch socket errors
|
||||
socket.on('error', proxyError);
|
||||
|
||||
//
|
||||
// Remove data listener now that the 'handshake' is complete
|
||||
//
|
||||
reverseProxy.socket.removeListener('data', handshake);
|
||||
});
|
||||
}
|
||||
|
||||
reverseProxy.on('error', proxyError);
|
||||
|
||||
try {
|
||||
//
|
||||
// Attempt to write the upgrade-head to the reverseProxy
|
||||
// request. This is small, and there's only ever one of
|
||||
// it; no need for pause/resume.
|
||||
//
|
||||
reverseProxy.write(head);
|
||||
}
|
||||
catch (ex) {
|
||||
return proxyError(ex);
|
||||
}
|
||||
|
||||
//
|
||||
// If we have been passed buffered data, resume it.
|
||||
//
|
||||
if (buffer) {
|
||||
return !errState
|
||||
? buffer.resume()
|
||||
: buffer.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// ### function close()
|
||||
// Closes all sockets associated with the Agents
|
||||
// belonging to this instance.
|
||||
//
|
||||
HttpProxy.prototype.close = function () {
|
||||
[this.forward, this.target].forEach(function (proxy) {
|
||||
if (proxy && proxy.agent) {
|
||||
proxy.agent.sockets.forEach(function (socket) {
|
||||
socket.end();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// ### @private function _forwardRequest (req)
|
||||
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
|
||||
// Forwards the specified `req` to the location specified
|
||||
// by `this.forward` ignoring errors and the subsequent response.
|
||||
//
|
||||
HttpProxy.prototype._forwardRequest = function (req) {
|
||||
var self = this,
|
||||
outgoing = new(this.forward.base),
|
||||
forwardProxy;
|
||||
|
||||
//
|
||||
// Setup outgoing proxy with relevant properties.
|
||||
//
|
||||
outgoing.host = this.forward.host;
|
||||
outgoing.port = this.forward.port,
|
||||
outgoing.agent = this.forward.agent;
|
||||
outgoing.method = req.method;
|
||||
outgoing.path = req.url;
|
||||
outgoing.headers = req.headers;
|
||||
|
||||
//
|
||||
// Open new HTTP request to internal resource with will
|
||||
// act as a reverse proxy pass.
|
||||
//
|
||||
forwardProxy = this.forward.protocol.request(outgoing, 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.
|
||||
//
|
||||
// Remark: Ignoring this error in the event
|
||||
// forward target doesn't exist.
|
||||
//
|
||||
forwardProxy.once('error', function (err) { });
|
||||
|
||||
//
|
||||
// Chunk the client request body as chunks from
|
||||
// the proxied request come in
|
||||
//
|
||||
req.on('data', function (chunk) {
|
||||
var flushed = forwardProxy.write(chunk);
|
||||
if (!flushed) {
|
||||
req.pause();
|
||||
forwardProxy.once('drain', function () {
|
||||
try { req.resume() }
|
||||
catch (er) { console.error("req.resume error: %s", er.message) }
|
||||
});
|
||||
|
||||
//
|
||||
// Force the `drain` event in 100ms if it hasn't
|
||||
// happened on its own.
|
||||
//
|
||||
setTimeout(function () {
|
||||
forwardProxy.emit('drain');
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// At the end of the client request, we are going to
|
||||
// stop the proxied request
|
||||
//
|
||||
req.on('end', function () {
|
||||
forwardProxy.end();
|
||||
});
|
||||
};
|
||||
@ -37,31 +37,34 @@ var util = require('util'),
|
||||
// locations of proxy targets based on ServerRequest headers; specifically
|
||||
// the HTTP host header.
|
||||
//
|
||||
var ProxyTable = exports.ProxyTable = function (router, silent, hostnameOnly) {
|
||||
var ProxyTable = exports.ProxyTable = function (options) {
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
this.silent = typeof silent !== 'undefined' ? silent : true;
|
||||
this.hostnameOnly = typeof hostnameOnly !== 'undefined' ? hostnameOnly : false;
|
||||
this.silent = options.silent || options.silent !== true;
|
||||
this.hostnameOnly = options.hostnameOnly === true;
|
||||
|
||||
if (typeof router === 'object') {
|
||||
if (typeof options.router === 'object') {
|
||||
//
|
||||
// If we are passed an object literal setup
|
||||
// the routes with RegExps from the router
|
||||
//
|
||||
this.setRoutes(router);
|
||||
this.setRoutes(options.router);
|
||||
}
|
||||
else if (typeof router === 'string') {
|
||||
else if (typeof options.router === 'string') {
|
||||
//
|
||||
// If we are passed a string then assume it is a
|
||||
// file path, parse that file and watch it for changes
|
||||
//
|
||||
var self = this;
|
||||
this.routeFile = router;
|
||||
this.setRoutes(JSON.parse(fs.readFileSync(router)).router);
|
||||
this.routeFile = options.router;
|
||||
this.setRoutes(JSON.parse(fs.readFileSync(options.router)).router);
|
||||
|
||||
fs.watchFile(this.routeFile, function () {
|
||||
fs.readFile(self.routeFile, function (err, data) {
|
||||
if (err) throw err;
|
||||
if (err) {
|
||||
self.emit('error', err);
|
||||
}
|
||||
|
||||
self.setRoutes(JSON.parse(data).router);
|
||||
self.emit('routes', self.hostnameOnly === false ? self.routes : self.router);
|
||||
});
|
||||
@ -72,7 +75,9 @@ var ProxyTable = exports.ProxyTable = function (router, silent, hostnameOnly) {
|
||||
}
|
||||
};
|
||||
|
||||
// Inherit from events.EventEmitter
|
||||
//
|
||||
// Inherit from `events.EventEmitter`
|
||||
//
|
||||
util.inherits(ProxyTable, events.EventEmitter);
|
||||
|
||||
//
|
||||
@ -81,7 +86,9 @@ util.inherits(ProxyTable, events.EventEmitter);
|
||||
// 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.');
|
||||
}
|
||||
|
||||
this.router = router;
|
||||
|
||||
@ -127,7 +134,9 @@ ProxyTable.prototype.getProxyLocation = function (req) {
|
||||
else {
|
||||
target += req.url;
|
||||
for (var i in this.routes) {
|
||||
var match, route = this.routes[i];
|
||||
var route = this.routes[i],
|
||||
match;
|
||||
|
||||
if (match = target.match(route.route)) {
|
||||
var location = route.target.split(':'),
|
||||
host = location[0],
|
||||
247
lib/node-http-proxy/routing-proxy.js
Normal file
247
lib/node-http-proxy/routing-proxy.js
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* routing-proxy.js: A routing proxy consuming a RoutingTable and multiple HttpProxy instances
|
||||
*
|
||||
* (C) 2011 Nodejitsu Inc.
|
||||
* MIT LICENCE
|
||||
*
|
||||
*/
|
||||
|
||||
var events = require('events'),
|
||||
util = require('util'),
|
||||
HttpProxy = require('./http-proxy').HttpProxy,
|
||||
ProxyTable = require('./proxy-table').ProxyTable;
|
||||
|
||||
//
|
||||
// ### function RoutingProxy (options)
|
||||
// #### @options {Object} Options for this instance
|
||||
// Constructor function for the RoutingProxy object, a higher level
|
||||
// reverse proxy Object which can proxy to multiple hosts and also interface
|
||||
// easily with a RoutingTable instance.
|
||||
//
|
||||
var RoutingProxy = exports.RoutingProxy = function (options) {
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
var self = this;
|
||||
options = options || {};
|
||||
|
||||
if (options.router) {
|
||||
this.proxyTable = new ProxyTable(options);
|
||||
this.proxyTable.on('routes', function (routes) {
|
||||
self.emit('routes', routes);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Create a set of `HttpProxy` objects to be used later on calls
|
||||
// to `.proxyRequest()` and `.proxyWebSocketRequest()`.
|
||||
//
|
||||
this.proxies = {};
|
||||
|
||||
//
|
||||
// Setup default target options (such as `https`).
|
||||
//
|
||||
this.target = {};
|
||||
this.target.https = options.target && options.target.https;
|
||||
|
||||
//
|
||||
// Setup other default options to be used for instances of
|
||||
// `HttpProxy` created by this `RoutingProxy` instance.
|
||||
//
|
||||
this.source = options.source || { host: 'localhost', port: 8000 };
|
||||
this.https = this.source.https || options.https;
|
||||
this.enable = options.enable;
|
||||
this.forward = options.forward;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Inherit from `events.EventEmitter`.
|
||||
//
|
||||
util.inherits(RoutingProxy, events.EventEmitter);
|
||||
|
||||
//
|
||||
// ### function add (options)
|
||||
// #### @options {Object} Options for the `HttpProxy` to add.
|
||||
// Adds a new instance of `HttpProxy` to this `RoutingProxy` instance
|
||||
// for the specified `options.host` and `options.port`.
|
||||
//
|
||||
RoutingProxy.prototype.add = function (options) {
|
||||
var self = this,
|
||||
key = this._getKey(options);
|
||||
|
||||
//
|
||||
// TODO: Consume properties in `options` related to the `ProxyTable`.
|
||||
//
|
||||
options.target = options.target || {};
|
||||
options.target.host = options.target.host || options.host;
|
||||
options.target.port = options.target.port || options.port;
|
||||
options.target.https = this.target && this.target.https ||
|
||||
options.target && options.target.https ||
|
||||
options.https;
|
||||
|
||||
//
|
||||
// Setup options to pass-thru to the new `HttpProxy` instance
|
||||
// for the specified `options.host` and `options.port` pair.
|
||||
//
|
||||
['https', 'enable', 'forward'].forEach(function (key) {
|
||||
if (options[key] !== false && self[key]) {
|
||||
options[key] = self[key];
|
||||
}
|
||||
});
|
||||
|
||||
this.proxies[key] = new HttpProxy(options);
|
||||
};
|
||||
|
||||
//
|
||||
// ### function remove (options)
|
||||
// #### @options {Object} Options mapping to the `HttpProxy` to remove.
|
||||
// Removes an instance of `HttpProxy` from this `RoutingProxy` instance
|
||||
// for the specified `options.host` and `options.port` (if they exist).
|
||||
//
|
||||
RoutingProxy.prototype.remove = function (options) {
|
||||
var key = this._getKey(options);
|
||||
};
|
||||
|
||||
//
|
||||
// ### function close()
|
||||
// Cleans up any state left behind (sockets, timeouts, etc)
|
||||
// associated with this instance.
|
||||
//
|
||||
RoutingProxy.prototype.close = function () {
|
||||
var self = this;
|
||||
|
||||
if (this.proxyTable) {
|
||||
//
|
||||
// Close the `RoutingTable` associated with
|
||||
// this instance (if any).
|
||||
//
|
||||
this.proxyTable.close();
|
||||
}
|
||||
|
||||
//
|
||||
// Close all sockets for all `HttpProxy` object(s)
|
||||
// associated with this instance.
|
||||
//
|
||||
Object.keys(this.proxies).forEach(function (key) {
|
||||
self.proxies[key].close();
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// ### function proxyRequest (req, res, [port, host, paused])
|
||||
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
|
||||
// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
|
||||
// #### @options {Object} Options for the outgoing proxy request.
|
||||
//
|
||||
// options.port {number} Port to use on the proxy target host.
|
||||
// options.host {string} Host of the proxy target.
|
||||
// options.buffer {Object} Result from `httpProxy.buffer(req)`
|
||||
// options.https {Object|boolean} Settings for https.
|
||||
//
|
||||
RoutingProxy.prototype.proxyRequest = function (req, res, options) {
|
||||
options = options || {};
|
||||
|
||||
//
|
||||
// Check the proxy table for this instance to see if we need
|
||||
// to get the proxy location for the request supplied. We will
|
||||
// always ignore the proxyTable if an explicit `port` and `host`
|
||||
// arguments are supplied to `proxyRequest`.
|
||||
//
|
||||
if (this.proxyTable && !options.host) {
|
||||
location = this.proxyTable.getProxyLocation(req);
|
||||
|
||||
//
|
||||
// If no location is returned from the ProxyTable instance
|
||||
// then respond with `404` since we do not have a valid proxy target.
|
||||
//
|
||||
if (!location) {
|
||||
try {
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
}
|
||||
catch (er) {
|
||||
console.error("res.writeHead/res.end error: %s", er.message);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// When using the ProxyTable in conjunction with an HttpProxy instance
|
||||
// only the following arguments are valid:
|
||||
//
|
||||
// * `proxy.proxyRequest(req, res, { host: 'localhost' })`: This will be skipped
|
||||
// * `proxy.proxyRequest(req, res, { buffer: buffer })`: Buffer will get updated appropriately
|
||||
// * `proxy.proxyRequest(req, res)`: Options will be assigned appropriately.
|
||||
//
|
||||
options.port = location.port;
|
||||
options.host = location.host;
|
||||
}
|
||||
|
||||
var key = options.host + ':' + options.port,
|
||||
proxy;
|
||||
|
||||
if (!this.proxies[key]) {
|
||||
this.add(options);
|
||||
}
|
||||
|
||||
proxy = this.proxies[key];
|
||||
proxy.proxyRequest(req, res, options.buffer);
|
||||
};
|
||||
|
||||
//
|
||||
// ### function proxyWebSocketRequest (req, socket, head, options)
|
||||
// #### @req {ServerRequest} Websocket request to proxy.
|
||||
// #### @socket {net.Socket} Socket for the underlying HTTP request
|
||||
// #### @head {string} Headers for the Websocket request.
|
||||
// #### @options {Object} Options to use when proxying this request.
|
||||
//
|
||||
// options.port {number} Port to use on the proxy target host.
|
||||
// options.host {string} Host of the proxy target.
|
||||
// options.buffer {Object} Result from `httpProxy.buffer(req)`
|
||||
// options.https {Object|boolean} Settings for https.
|
||||
//
|
||||
RoutingProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options) {
|
||||
options = options || {};
|
||||
|
||||
if (this.proxyTable && !options.host) {
|
||||
location = this.proxyTable.getProxyLocation(req);
|
||||
|
||||
if (!location) {
|
||||
return socket.destroy();
|
||||
}
|
||||
|
||||
options.port = location.port;
|
||||
options.host = location.host;
|
||||
}
|
||||
|
||||
var key = options.host + ':' + options.port,
|
||||
proxy;
|
||||
|
||||
if (!this.proxies[key]) {
|
||||
this.add(options);
|
||||
}
|
||||
|
||||
proxy = this.proxies[key];
|
||||
proxy.proxyWebSocketRequest(req, socket, head, options.buffer);
|
||||
};
|
||||
|
||||
//
|
||||
// ### @private function _getKey (options)
|
||||
// #### @options {Object} Options to extract the key from
|
||||
// Ensures that the appropriate options are present in the `options`
|
||||
// provided and responds with a string key representing the `host`, `port`
|
||||
// combination contained within.
|
||||
//
|
||||
RoutingProxy.prototype._getKey = function (options) {
|
||||
if (!options || ((!options.host || !options.port)
|
||||
&& (!options.target || !options.target.host || !options.target.port))) {
|
||||
throw new Error('options.host and options.port or options.target are required.');
|
||||
return;
|
||||
}
|
||||
|
||||
return [
|
||||
options.host || options.target.host,
|
||||
options.port || options.target.port
|
||||
].join(':');
|
||||
}
|
||||
11
package.json
11
package.json
@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "http-proxy",
|
||||
"description": "A full-featured http reverse proxy for node.js",
|
||||
"version": "0.6.6",
|
||||
"version": "0.7.0",
|
||||
"author": "Charlie Robbins <charlie.robbins@gmail.com>",
|
||||
"contributors": [
|
||||
{ "name": "Mikeal Rogers", "email": "mikeal.rogers@gmail.com" },
|
||||
{ "name": "Marak Squires", "email": "marak.squires@gmail.com" },
|
||||
{ "name": "Fedor Indutny", "email": "fedor.indutny@gmail.com" }
|
||||
{ "name": "Fedor Indutny", "email": "fedor.indutny@gmail.com" },
|
||||
{ "name": "Dominic Tarr", "email": "dominic@nodejitsu.com" }
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -25,6 +26,10 @@
|
||||
},
|
||||
"main": "./lib/node-http-proxy",
|
||||
"bin": { "node-http-proxy": "./bin/node-http-proxy" },
|
||||
"scripts": { "test": "vows test/*-test.js --spec && vows test/*-test.js --spec --https" },
|
||||
"scripts": {
|
||||
"test": "npm run-script test-http && npm run-script test-https",
|
||||
"test-http": "vows --spec && vows --spec --target=secure",
|
||||
"test-https": "vows --spec --source=secure && vows --spec --source=secure --target=secure"
|
||||
},
|
||||
"engines": { "node": "0.4.x || 0.5.x" }
|
||||
}
|
||||
|
||||
166
test/helpers.js
166
test/helpers.js
@ -5,27 +5,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
var fs = require('fs'),
|
||||
var assert = require('assert'),
|
||||
fs = require('fs'),
|
||||
http = require('http'),
|
||||
https = require('https'),
|
||||
path = require('path'),
|
||||
vows = require('vows'),
|
||||
assert = require('assert'),
|
||||
argv = require('optimist').argv,
|
||||
request = require('request'),
|
||||
websocket = require('./../vendor/websocket'),
|
||||
httpProxy = require('./../lib/node-http-proxy');
|
||||
|
||||
function merge (target) {
|
||||
var objs = Array.prototype.slice.call(arguments, 1);
|
||||
objs.forEach(function(o) {
|
||||
Object.keys(o).forEach(function (attr) {
|
||||
if (! o.__lookupGetter__(attr)) {
|
||||
target[attr] = o[attr];
|
||||
}
|
||||
});
|
||||
});
|
||||
return target;
|
||||
}
|
||||
vows = require('vows'),
|
||||
websocket = require('../vendor/websocket'),
|
||||
httpProxy = require('../lib/node-http-proxy');
|
||||
|
||||
var loadHttps = exports.loadHttps = function () {
|
||||
return {
|
||||
@ -34,22 +23,36 @@ var loadHttps = exports.loadHttps = function () {
|
||||
};
|
||||
};
|
||||
|
||||
var TestRunner = exports.TestRunner = function (protocol, target) {
|
||||
this.options = {};
|
||||
this.options.target = {};
|
||||
this.protocol = protocol;
|
||||
this.target = target;
|
||||
this.testServers = [];
|
||||
|
||||
if (protocol === 'https') {
|
||||
this.options.https = loadHttps();
|
||||
var parseProtocol = exports.parseProtocol = function () {
|
||||
function setupProtocol (secure) {
|
||||
return {
|
||||
secure: secure,
|
||||
protocols: {
|
||||
http: secure ? 'https' : 'http',
|
||||
ws: secure ? 'wss' : 'ws'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target === 'https') {
|
||||
this.options.target = {
|
||||
https: loadHttps()
|
||||
return {
|
||||
source: setupProtocol(argv.source === 'secure'),
|
||||
target: setupProtocol(argv.target === 'secure')
|
||||
};
|
||||
}
|
||||
|
||||
var TestRunner = exports.TestRunner = function (options) {
|
||||
options = options || {};
|
||||
this.source = options.source || {};
|
||||
this.target = options.target || {};
|
||||
this.testServers = [];
|
||||
|
||||
if (this.source.secure) {
|
||||
this.source.https = loadHttps();
|
||||
}
|
||||
|
||||
if (this.target.secure) {
|
||||
this.target.https = loadHttps();
|
||||
}
|
||||
};
|
||||
|
||||
TestRunner.prototype.assertProxied = function (host, proxyPort, port, createProxy) {
|
||||
@ -59,14 +62,18 @@ TestRunner.prototype.assertProxied = function (host, proxyPort, port, createProx
|
||||
|
||||
var test = {
|
||||
topic: function () {
|
||||
var that = this, options = {
|
||||
var that = this,
|
||||
options;
|
||||
|
||||
options = {
|
||||
method: 'GET',
|
||||
uri: self.protocol + '://localhost:' + proxyPort,
|
||||
uri: self.source.protocols.http + '://localhost:' + proxyPort,
|
||||
headers: {
|
||||
host: host
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function startTest () {
|
||||
if (port) {
|
||||
return self.startTargetServer(port, output, function () {
|
||||
@ -91,7 +98,7 @@ TestRunner.prototype.assertProxied = function (host, proxyPort, port, createProx
|
||||
|
||||
TestRunner.prototype.assertResponseCode = function (proxyPort, statusCode, createProxy) {
|
||||
var assertion = "should receive " + statusCode + " responseCode",
|
||||
protocol = this.protocol;
|
||||
protocol = this.source.protocols.http;
|
||||
|
||||
var test = {
|
||||
topic: function () {
|
||||
@ -124,7 +131,6 @@ TestRunner.prototype.assertResponseCode = function (proxyPort, statusCode, creat
|
||||
//
|
||||
// WebSocketTest
|
||||
//
|
||||
|
||||
TestRunner.prototype.webSocketTest = function (options) {
|
||||
var self = this;
|
||||
|
||||
@ -161,7 +167,6 @@ TestRunner.prototype.webSocketTest = function (options) {
|
||||
//
|
||||
// WebSocketTestWithTable
|
||||
//
|
||||
|
||||
TestRunner.prototype.webSocketTestWithTable = function (options) {
|
||||
var self = this;
|
||||
|
||||
@ -199,8 +204,7 @@ TestRunner.prototype.webSocketTestWithTable = function (options) {
|
||||
//
|
||||
TestRunner.prototype.startProxyServer = function (port, targetPort, host, callback) {
|
||||
var that = this,
|
||||
options = that.options,
|
||||
proxyServer = httpProxy.createServer(targetPort, host, options);
|
||||
proxyServer = httpProxy.createServer(host, targetPort, this.getOptions());
|
||||
|
||||
proxyServer.listen(port, function () {
|
||||
that.testServers.push(proxyServer);
|
||||
@ -212,18 +216,19 @@ TestRunner.prototype.startProxyServer = function (port, targetPort, host, callba
|
||||
// Creates the reverse proxy server with a specified latency
|
||||
//
|
||||
TestRunner.prototype.startLatentProxyServer = function (port, targetPort, host, latency, callback) {
|
||||
//
|
||||
// Initialize the nodeProxy and start proxying the request
|
||||
var that = this, proxyServer = httpProxy.createServer(function (req, res, proxy) {
|
||||
var buffer = proxy.buffer(req);
|
||||
//
|
||||
var that = this,
|
||||
proxyServer;
|
||||
|
||||
proxyServer = httpProxy.createServer(host, targetPort, function (req, res, proxy) {
|
||||
var buffer = httpProxy.buffer(req);
|
||||
|
||||
setTimeout(function () {
|
||||
proxy.proxyRequest(req, res, {
|
||||
port: targetPort,
|
||||
host: host,
|
||||
buffer: buffer
|
||||
});
|
||||
proxy.proxyRequest(req, res, buffer);
|
||||
}, latency);
|
||||
}, this.options);
|
||||
}, this.getOptions());
|
||||
|
||||
proxyServer.listen(port, function () {
|
||||
that.testServers.push(proxyServer);
|
||||
@ -235,7 +240,9 @@ TestRunner.prototype.startLatentProxyServer = function (port, targetPort, host,
|
||||
// Creates the reverse proxy server with a ProxyTable
|
||||
//
|
||||
TestRunner.prototype.startProxyServerWithTable = function (port, options, callback) {
|
||||
var that = this, proxyServer = httpProxy.createServer(merge({}, options, this.options));
|
||||
var that = this,
|
||||
proxyServer = httpProxy.createServer(merge({}, options, this.getOptions()));
|
||||
|
||||
proxyServer.listen(port, function () {
|
||||
that.testServers.push(proxyServer);
|
||||
callback();
|
||||
@ -248,13 +255,15 @@ TestRunner.prototype.startProxyServerWithTable = function (port, options, callba
|
||||
// Creates a latent reverse proxy server using a ProxyTable
|
||||
//
|
||||
TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, latency, options, callback) {
|
||||
//
|
||||
// Initialize the nodeProxy and start proxying the request
|
||||
var proxyServer,
|
||||
that = this,
|
||||
proxy = new httpProxy.HttpProxy(merge({}, options, that.options));
|
||||
//
|
||||
var that = this,
|
||||
proxy = new httpProxy.RoutingProxy(merge({}, options, this.getOptions())),
|
||||
proxyServer;
|
||||
|
||||
var handler = function (req, res) {
|
||||
var buffer = proxy.buffer(req);
|
||||
var buffer = httpProxy.buffer(req);
|
||||
setTimeout(function () {
|
||||
proxy.proxyRequest(req, res, {
|
||||
buffer: buffer
|
||||
@ -262,9 +271,9 @@ TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, laten
|
||||
}, latency);
|
||||
};
|
||||
|
||||
proxyServer = that.options.https
|
||||
? https.createServer(that.options.https, handler, that.options)
|
||||
: http.createServer(handler, that.options);
|
||||
proxyServer = this.source.https
|
||||
? https.createServer(this.source.https, handler)
|
||||
: http.createServer(handler);
|
||||
|
||||
proxyServer.listen(port, function () {
|
||||
that.testServers.push(proxyServer);
|
||||
@ -278,7 +287,9 @@ TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, laten
|
||||
// Creates proxy server forwarding to the specified options
|
||||
//
|
||||
TestRunner.prototype.startProxyServerWithForwarding = function (port, targetPort, host, options, callback) {
|
||||
var that = this, proxyServer = httpProxy.createServer(targetPort, host, merge({}, options, this.options));
|
||||
var that = this,
|
||||
proxyServer = httpProxy.createServer(targetPort, host, merge({}, options, this.getOptions()));
|
||||
|
||||
proxyServer.listen(port, function () {
|
||||
that.testServers.push(proxyServer);
|
||||
callback(null, proxyServer);
|
||||
@ -289,14 +300,18 @@ TestRunner.prototype.startProxyServerWithForwarding = function (port, targetPort
|
||||
// Creates the 'hellonode' server
|
||||
//
|
||||
TestRunner.prototype.startTargetServer = function (port, output, callback) {
|
||||
var that = this, targetServer, handler = function (req, res) {
|
||||
var that = this,
|
||||
targetServer,
|
||||
handler;
|
||||
|
||||
handler = function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write(output);
|
||||
res.end();
|
||||
};
|
||||
|
||||
targetServer = this.options.target.https
|
||||
? https.createServer(this.options.target.https, handler)
|
||||
targetServer = this.target.https
|
||||
? https.createServer(this.target.https, handler)
|
||||
: http.createServer(handler);
|
||||
|
||||
targetServer.listen(port, function () {
|
||||
@ -315,3 +330,42 @@ TestRunner.prototype.closeServers = function () {
|
||||
|
||||
return this.testServers;
|
||||
};
|
||||
|
||||
//
|
||||
// Creates a new instance of the options to
|
||||
// pass to `httpProxy.createServer()`
|
||||
//
|
||||
TestRunner.prototype.getOptions = function () {
|
||||
return {
|
||||
https: clone(this.source.https),
|
||||
target: {
|
||||
https: clone(this.target.https)
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// ### @private function clone (object)
|
||||
// #### @object {Object} Object to clone
|
||||
// Shallow clones the specified object.
|
||||
//
|
||||
function clone (object) {
|
||||
if (!object) { return null }
|
||||
|
||||
return Object.keys(object).reduce(function (obj, k) {
|
||||
obj[k] = object[k];
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function merge (target) {
|
||||
var objs = Array.prototype.slice.call(arguments, 1);
|
||||
objs.forEach(function(o) {
|
||||
Object.keys(o).forEach(function (attr) {
|
||||
if (! o.__lookupGetter__(attr)) {
|
||||
target[attr] = o[attr];
|
||||
}
|
||||
});
|
||||
});
|
||||
return target;
|
||||
}
|
||||
@ -26,10 +26,9 @@
|
||||
|
||||
var assert = require('assert'),
|
||||
util = require('util'),
|
||||
argv = require('optimist').argv,
|
||||
request = require('request'),
|
||||
vows = require('vows'),
|
||||
helpers = require('./helpers');
|
||||
helpers = require('../helpers');
|
||||
|
||||
var forwardOptions = {
|
||||
forward: {
|
||||
@ -45,11 +44,11 @@ var badForwardOptions = {
|
||||
}
|
||||
};
|
||||
|
||||
var protocol = argv.https ? 'https' : 'http',
|
||||
target = argv.target ? argv.target : 'http',
|
||||
runner = new helpers.TestRunner(protocol, target);
|
||||
var options = helpers.parseProtocol(),
|
||||
testName = [options.source.protocols.http, options.target.protocols.http].join('-to-'),
|
||||
runner = new helpers.TestRunner(options);
|
||||
|
||||
vows.describe('node-http-proxy/' + protocol).addBatch({
|
||||
vows.describe('node-http-proxy/http-proxy/' + testName).addBatch({
|
||||
"When using server created by httpProxy.createServer()": {
|
||||
"with no latency" : {
|
||||
"and a valid target server": runner.assertProxied('localhost', 8080, 8081, function (callback) {
|
||||
@ -12,11 +12,11 @@ var assert = require('assert'),
|
||||
argv = require('optimist').argv,
|
||||
request = require('request'),
|
||||
vows = require('vows'),
|
||||
helpers = require('./helpers'),
|
||||
TestRunner = helpers.TestRunner;
|
||||
helpers = require('../helpers');
|
||||
|
||||
var protocol = argv.https ? 'https' : 'http',
|
||||
runner = new TestRunner(protocol),
|
||||
var options = helpers.parseProtocol(),
|
||||
testName = [options.source.protocols.http, options.target.protocols.http].join('-to-'),
|
||||
runner = new helpers.TestRunner(options),
|
||||
routeFile = path.join(__dirname, 'config.json');
|
||||
|
||||
var fileOptions = {
|
||||
@ -41,7 +41,7 @@ var hostnameOptions = {
|
||||
}
|
||||
};
|
||||
|
||||
vows.describe('node-http-proxy/proxy-table/' + protocol).addBatch({
|
||||
vows.describe('node-http-proxy/routing-proxy/' + testName).addBatch({
|
||||
"When using server created by httpProxy.createServer()": {
|
||||
"when passed a routing table": {
|
||||
"and routing by RegExp": {
|
||||
@ -82,16 +82,14 @@ vows.describe('node-http-proxy/proxy-table/' + protocol).addBatch({
|
||||
fs.writeFileSync(routeFile, JSON.stringify(config));
|
||||
|
||||
this.server.on('routes', function () {
|
||||
var options = {
|
||||
runner.startTargetServer(8103, 'hello dynamic.com', function () {
|
||||
request({
|
||||
method: 'GET',
|
||||
uri: protocol + '://localhost:8100',
|
||||
uri: options.source.protocols.http + '://localhost:8100',
|
||||
headers: {
|
||||
host: 'dynamic.com'
|
||||
}
|
||||
};
|
||||
|
||||
runner.startTargetServer(8103, 'hello dynamic.com', function () {
|
||||
request(options, that.callback);
|
||||
}, that.callback);
|
||||
});
|
||||
});
|
||||
},
|
||||
@ -30,8 +30,8 @@ var util = require('util'),
|
||||
colors = require('colors'),
|
||||
request = require('request'),
|
||||
vows = require('vows'),
|
||||
websocket = require('./../vendor/websocket'),
|
||||
helpers = require('./helpers');
|
||||
websocket = require('../../vendor/websocket'),
|
||||
helpers = require('../helpers');
|
||||
|
||||
try {
|
||||
var utils = require('socket.io/lib/socket.io/utils'),
|
||||
@ -43,55 +43,11 @@ catch (ex) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var protocol = argv.https ? 'https' : 'http',
|
||||
wsprotocol = argv.https ? 'wss' : 'ws',
|
||||
runner = new helpers.TestRunner(protocol);
|
||||
var options = helpers.parseProtocol(),
|
||||
testName = [options.source.protocols.ws, options.target.protocols.ws].join('-to-'),
|
||||
runner = new helpers.TestRunner(options);
|
||||
|
||||
vows.describe('node-http-proxy/websocket/' + wsprotocol).addBatch({
|
||||
"when using proxy table":{
|
||||
"with no latency" : {
|
||||
"when an inbound message is sent from a WebSocket client": {
|
||||
topic: function () {
|
||||
var that = this
|
||||
headers = {};
|
||||
|
||||
runner.webSocketTestWithTable({
|
||||
io: io,
|
||||
host: 'localhost',
|
||||
wsprotocol: wsprotocol,
|
||||
protocol: protocol,
|
||||
router: {'localhost':'localhost:8230'},
|
||||
ports: {
|
||||
target: 8230,
|
||||
proxy: 8231
|
||||
},
|
||||
onListen: function (socket) {
|
||||
socket.on('connection', function (client) {
|
||||
client.on('message', function (msg) {
|
||||
that.callback(null, msg, headers);
|
||||
});
|
||||
});
|
||||
},
|
||||
onWsupgrade: function (req, res) {
|
||||
headers.request = req;
|
||||
headers.response = res.headers;
|
||||
},
|
||||
onOpen: function (ws) {
|
||||
ws.send(utils.encode('from client'));
|
||||
}
|
||||
});
|
||||
},
|
||||
"the target server should receive the message": function (err, msg, headers) {
|
||||
assert.equal(msg, 'from client');
|
||||
},
|
||||
"the origin and sec-websocket-origin headers should match": function (err, msg, headers) {
|
||||
assert.isString(headers.response['sec-websocket-location']);
|
||||
assert.isTrue(headers.response['sec-websocket-location'].indexOf(wsprotocol) !== -1);
|
||||
assert.equal(headers.request.Origin, headers.response['sec-websocket-origin']);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
vows.describe('node-http-proxy/http-proxy/' + testName).addBatch({
|
||||
"When using server created by httpProxy.createServer()": {
|
||||
"with no latency" : {
|
||||
"when an inbound message is sent from a WebSocket client": {
|
||||
@ -102,8 +58,8 @@ vows.describe('node-http-proxy/websocket/' + wsprotocol).addBatch({
|
||||
runner.webSocketTest({
|
||||
io: io,
|
||||
host: 'localhost',
|
||||
wsprotocol: wsprotocol,
|
||||
protocol: protocol,
|
||||
wsprotocol: options.source.protocols.ws,
|
||||
protocol: options.source.protocols.http,
|
||||
ports: {
|
||||
target: 8130,
|
||||
proxy: 8131
|
||||
@ -129,7 +85,7 @@ vows.describe('node-http-proxy/websocket/' + wsprotocol).addBatch({
|
||||
},
|
||||
"the origin and sec-websocket-origin headers should match": function (err, msg, headers) {
|
||||
assert.isString(headers.response['sec-websocket-location']);
|
||||
assert.isTrue(headers.response['sec-websocket-location'].indexOf(wsprotocol) !== -1);
|
||||
assert.isTrue(headers.response['sec-websocket-location'].indexOf(options.source.protocols.ws) !== -1);
|
||||
assert.equal(headers.request.Origin, headers.response['sec-websocket-origin']);
|
||||
}
|
||||
},
|
||||
@ -141,8 +97,8 @@ vows.describe('node-http-proxy/websocket/' + wsprotocol).addBatch({
|
||||
runner.webSocketTest({
|
||||
io: io,
|
||||
host: 'localhost',
|
||||
wsprotocol: wsprotocol,
|
||||
protocol: protocol,
|
||||
wsprotocol: options.source.protocols.ws,
|
||||
protocol: options.source.protocols.http,
|
||||
ports: {
|
||||
target: 8132,
|
||||
proxy: 8133
|
||||
@ -170,8 +126,8 @@ vows.describe('node-http-proxy/websocket/' + wsprotocol).addBatch({
|
||||
runner.webSocketTest({
|
||||
io: io,
|
||||
host: 'localhost',
|
||||
wsprotocol: wsprotocol,
|
||||
protocol: protocol,
|
||||
wsprotocol: options.source.protocols.ws,
|
||||
protocol: options.source.protocols.http,
|
||||
ports: {
|
||||
target: 8134,
|
||||
proxy: 8135
|
||||
@ -198,7 +154,7 @@ vows.describe('node-http-proxy/websocket/' + wsprotocol).addBatch({
|
||||
},
|
||||
"the origin and sec-websocket-origin headers should match": function (err, msg, headers) {
|
||||
assert.isString(headers.response['sec-websocket-location']);
|
||||
assert.isTrue(headers.response['sec-websocket-location'].indexOf(wsprotocol) !== -1);
|
||||
assert.isTrue(headers.response['sec-websocket-location'].indexOf(options.source.protocols.ws) !== -1);
|
||||
assert.equal(headers.request.Origin, headers.response['sec-websocket-origin']);
|
||||
}
|
||||
}
|
||||
104
test/websocket/websocket-routing-proxy-test.js
Normal file
104
test/websocket/websocket-routing-proxy-test.js
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
node-http-proxy-test.js: http proxy for node.js
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Marak Squires and Fedor Indutny
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
var util = require('util'),
|
||||
assert = require('assert'),
|
||||
argv = require('optimist').argv,
|
||||
colors = require('colors'),
|
||||
request = require('request'),
|
||||
vows = require('vows'),
|
||||
websocket = require('../../vendor/websocket'),
|
||||
helpers = require('../helpers');
|
||||
|
||||
try {
|
||||
var utils = require('socket.io/lib/socket.io/utils'),
|
||||
io = require('socket.io');
|
||||
}
|
||||
catch (ex) {
|
||||
console.error('Socket.io is required for this example:');
|
||||
console.error('npm ' + 'install'.green + ' socket.io@0.6.18'.magenta);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var options = helpers.parseProtocol(),
|
||||
testName = [options.source.protocols.ws, options.target.protocols.ws].join('-to-'),
|
||||
runner = new helpers.TestRunner(options);
|
||||
|
||||
vows.describe('node-http-proxy/routing-proxy/' + testName).addBatch({
|
||||
"When using server created by httpProxy.createServer()": {
|
||||
"using proxy table with no latency": {
|
||||
"when an inbound message is sent from a WebSocket client": {
|
||||
topic: function () {
|
||||
var that = this
|
||||
headers = {};
|
||||
|
||||
runner.webSocketTestWithTable({
|
||||
io: io,
|
||||
host: 'localhost',
|
||||
wsprotocol: options.source.protocols.ws,
|
||||
protocol: options.source.protocols.http,
|
||||
router: { 'localhost' : 'localhost:8230' },
|
||||
ports: {
|
||||
target: 8230,
|
||||
proxy: 8231
|
||||
},
|
||||
onListen: function (socket) {
|
||||
socket.on('connection', function (client) {
|
||||
client.on('message', function (msg) {
|
||||
that.callback(null, msg, headers);
|
||||
});
|
||||
});
|
||||
},
|
||||
onWsupgrade: function (req, res) {
|
||||
headers.request = req;
|
||||
headers.response = res.headers;
|
||||
},
|
||||
onOpen: function (ws) {
|
||||
ws.send(utils.encode('from client'));
|
||||
}
|
||||
});
|
||||
},
|
||||
"the target server should receive the message": function (err, msg, headers) {
|
||||
assert.equal(msg, 'from client');
|
||||
},
|
||||
"the origin and sec-websocket-origin headers should match": function (err, msg, headers) {
|
||||
assert.isString(headers.response['sec-websocket-location']);
|
||||
assert.isTrue(headers.response['sec-websocket-location'].indexOf(options.source.protocols.ws) !== -1);
|
||||
assert.equal(headers.request.Origin, headers.response['sec-websocket-origin']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).addBatch({
|
||||
"When the tests are over": {
|
||||
topic: function () {
|
||||
return runner.closeServers();
|
||||
},
|
||||
"the servers should clean up": function () {
|
||||
assert.isTrue(true);
|
||||
}
|
||||
}
|
||||
}).export(module);
|
||||
Loading…
x
Reference in New Issue
Block a user