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/
|
node_modules/
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|||||||
@ -114,7 +114,7 @@ httpProxy.createServer(function (req, res, proxy) {
|
|||||||
// Buffer the request so that `data` and `end` events
|
// Buffer the request so that `data` and `end` events
|
||||||
// are not lost during async operation(s).
|
// 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
|
// 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
|
## Using node-http-proxy from the command line
|
||||||
When you install this package with npm, a node-http-proxy binary will become available to you. Using this binary is easy with some simple options:
|
When you install this package with npm, a node-http-proxy binary will become available to you. Using this binary is easy with some simple options:
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ var path = require('path'),
|
|||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
util = require('util'),
|
util = require('util'),
|
||||||
argv = require('optimist').argv,
|
argv = require('optimist').argv,
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
httpProxy = require('../lib/node-http-proxy');
|
||||||
|
|
||||||
var help = [
|
var help = [
|
||||||
"usage: node-http-proxy [options] ",
|
"usage: node-http-proxy [options] ",
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
|
|
||||||
module.exports = Store
|
|
||||||
//
|
//
|
||||||
// just to make these example a little bit interesting,
|
// just to make these example a little bit interesting,
|
||||||
// make a little key value store with an http interface
|
// 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.
|
// TODO: cached map-reduce views and auto-magic sharding.
|
||||||
//
|
//
|
||||||
|
var Store = module.exports = function Store () {
|
||||||
|
this.store = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function Store () {
|
|
||||||
this.store = {}
|
|
||||||
}
|
|
||||||
Store.prototype = {
|
Store.prototype = {
|
||||||
get: function (key) {
|
get: function (key) {
|
||||||
return this.store[key]
|
return this.store[key]
|
||||||
@ -27,16 +27,17 @@
|
|||||||
var util = require('util'),
|
var util = require('util'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
http = require('http'),
|
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);
|
util.puts(welcome.rainbow.bold);
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -27,7 +27,7 @@
|
|||||||
var util = require('util'),
|
var util = require('util'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
http = require('http'),
|
http = require('http'),
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
httpProxy = require('../../lib/node-http-proxy');
|
||||||
|
|
||||||
//
|
//
|
||||||
// Basic Http Proxy Server
|
// Basic Http Proxy Server
|
||||||
@ -42,23 +42,24 @@ httpProxy.createServer(9000, 'localhost').listen(8000);
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
var connections = []
|
var connections = [],
|
||||||
, go
|
go;
|
||||||
|
|
||||||
http.createServer(function (req, res) {
|
http.createServer(function (req, res) {
|
||||||
|
connections.push(function () {
|
||||||
connections.push (function (){
|
|
||||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||||
res.end();
|
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);
|
}).listen(9000);
|
||||||
|
|
||||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
||||||
@ -27,17 +27,12 @@
|
|||||||
var util = require('util'),
|
var util = require('util'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
http = require('http'),
|
http = require('http'),
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
httpProxy = require('../../lib/node-http-proxy');
|
||||||
|
|
||||||
//
|
//
|
||||||
// Http Proxy Server with Latency
|
// Http Proxy Server with Latency
|
||||||
//
|
//
|
||||||
var server = httpProxy.createServer(function (req, res, proxy) {
|
var server = httpProxy.createServer(9000, 'localhost');
|
||||||
proxy.proxyRequest(req, res, {
|
|
||||||
port: 9000,
|
|
||||||
host: 'localhost'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Tell the server to listen on port 8002
|
// Tell the server to listen on port 8002
|
||||||
@ -27,7 +27,7 @@
|
|||||||
var util = require('util'),
|
var util = require('util'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
http = require('http'),
|
http = require('http'),
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
httpProxy = require('../../lib/node-http-proxy');
|
||||||
|
|
||||||
//
|
//
|
||||||
// Setup proxy server with forwarding
|
// Setup proxy server with forwarding
|
||||||
@ -27,13 +27,13 @@
|
|||||||
var util = require('util'),
|
var util = require('util'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
http = require('http'),
|
http = require('http'),
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
httpProxy = require('../../lib/node-http-proxy');
|
||||||
|
|
||||||
//
|
//
|
||||||
// Http Proxy Server with Latency
|
// Http Proxy Server with Latency
|
||||||
//
|
//
|
||||||
httpProxy.createServer(function (req, res, proxy) {
|
httpProxy.createServer(function (req, res, proxy) {
|
||||||
var buffer = proxy.buffer(req);
|
var buffer = httpProxy.buffer(req);
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
proxy.proxyRequest(req, res, {
|
proxy.proxyRequest(req, res, {
|
||||||
port: 9000,
|
port: 9000,
|
||||||
@ -28,7 +28,7 @@ var https = require('https'),
|
|||||||
http = require('http'),
|
http = require('http'),
|
||||||
util = require('util'),
|
util = require('util'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
httpProxy = require('./../lib/node-http-proxy'),
|
httpProxy = require('../../lib/node-http-proxy'),
|
||||||
helpers = require('./../test/helpers');
|
helpers = require('./../test/helpers');
|
||||||
|
|
||||||
var opts = helpers.loadHttps();
|
var opts = helpers.loadHttps();
|
||||||
@ -28,7 +28,7 @@ var https = require('https'),
|
|||||||
http = require('http'),
|
http = require('http'),
|
||||||
util = require('util'),
|
util = require('util'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
httpProxy = require('./../lib/node-http-proxy'),
|
httpProxy = require('../../lib/node-http-proxy'),
|
||||||
helpers = require('./../test/helpers');
|
helpers = require('./../test/helpers');
|
||||||
|
|
||||||
var opts = helpers.loadHttps();
|
var opts = helpers.loadHttps();
|
||||||
@ -27,7 +27,7 @@
|
|||||||
var util = require('util'),
|
var util = require('util'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
http = require('http'),
|
http = require('http'),
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
httpProxy = require('../../lib/node-http-proxy');
|
||||||
|
|
||||||
//
|
//
|
||||||
// Http Proxy Server with Proxy Table
|
// Http Proxy Server with Proxy Table
|
||||||
@ -27,14 +27,14 @@
|
|||||||
var util = require('util'),
|
var util = require('util'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
http = require('http'),
|
http = require('http'),
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
httpProxy = require('../../lib/node-http-proxy');
|
||||||
|
|
||||||
//
|
//
|
||||||
// Http Server with proxyRequest Handler and Latency
|
// Http Server with proxyRequest Handler and Latency
|
||||||
//
|
//
|
||||||
var proxy = new httpProxy.HttpProxy();
|
var proxy = new httpProxy.HttpProxy();
|
||||||
http.createServer(function (req, res) {
|
http.createServer(function (req, res) {
|
||||||
var buffer = proxy.buffer(req);
|
var buffer = httpProxy.buffer(req);
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
proxy.proxyRequest(req, res, {
|
proxy.proxyRequest(req, res, {
|
||||||
port: 9000,
|
port: 9000,
|
||||||
@ -27,7 +27,7 @@
|
|||||||
var util = require('util'),
|
var util = require('util'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
http = require('http'),
|
http = require('http'),
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
httpProxy = require('../../lib/node-http-proxy');
|
||||||
|
|
||||||
//
|
//
|
||||||
// Basic Http Proxy Server
|
// Basic Http Proxy Server
|
||||||
@ -37,7 +37,7 @@ httpProxy.createServer(
|
|||||||
// This is where our middlewares go, with any options desired - in this case,
|
// This is where our middlewares go, with any options desired - in this case,
|
||||||
// the list of routes/URLs and their destinations.
|
// the list of routes/URLs and their destinations.
|
||||||
//
|
//
|
||||||
require('proxy-by-url')({
|
require('proxy-by-url')({
|
||||||
'/hello': { port: 9000, host: 'localhost' },
|
'/hello': { port: 9000, host: 'localhost' },
|
||||||
'/charlie': { port: 80, host: 'charlieistheman.com' },
|
'/charlie': { port: 80, host: 'charlieistheman.com' },
|
||||||
'/google': { port: 80, host: 'google.com' }
|
'/google': { port: 80, host: 'google.com' }
|
||||||
@ -19,12 +19,11 @@ httpProxy.createServer(
|
|||||||
//
|
//
|
||||||
// Target Http Server (to listen for requests on 'localhost')
|
// Target Http Server (to listen for requests on 'localhost')
|
||||||
//
|
//
|
||||||
http.createServer(
|
http.createServer(function (req, res) {
|
||||||
function (req, res) {
|
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
res.end();
|
||||||
res.end();
|
}).listen(9000);
|
||||||
}).listen(9000);
|
|
||||||
|
|
||||||
// And finally, some colored startup output.
|
// And finally, some colored startup output.
|
||||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
||||||
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "http-proxy-examples"
|
"name": "http-proxy-examples",
|
||||||
, "description": "packages required to run the examples"
|
"description": "packages required to run the examples",
|
||||||
, "version": "0.0.0"
|
"version": "0.0.0",
|
||||||
, "dependencies": {
|
"dependencies": {
|
||||||
"connect": "1.6"
|
"connect": "1.6",
|
||||||
, "connect-gzip": "0.1"
|
"connect-gzip": "0.1",
|
||||||
, "connect-jsonp": "0.0.5"
|
"connect-jsonp": "0.0.5",
|
||||||
, "connect-restreamer": "1"
|
"connect-restreamer": "1",
|
||||||
, "proxy-by-url": ">= 0.0.1"
|
"proxy-by-url": ">= 0.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@ var sys = require('sys'),
|
|||||||
http = require('http'),
|
http = require('http'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
websocket = require('./../vendor/websocket'),
|
websocket = require('./../vendor/websocket'),
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
httpProxy = require('../../lib/node-http-proxy');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var utils = require('socket.io/lib/socket.io/utils'),
|
var utils = require('socket.io/lib/socket.io/utils'),
|
||||||
@ -80,7 +80,7 @@ var proxyServer = http.createServer(function (req, res) {
|
|||||||
// WebSocket requests as well.
|
// WebSocket requests as well.
|
||||||
//
|
//
|
||||||
proxyServer.on('upgrade', function (req, socket, head) {
|
proxyServer.on('upgrade', function (req, socket, head) {
|
||||||
var buffer = proxy.buffer(socket);
|
var buffer = httpProxy.buffer(socket);
|
||||||
|
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
proxy.proxyWebSocketRequest(req, socket, head, {
|
proxy.proxyWebSocketRequest(req, socket, head, {
|
||||||
@ -28,7 +28,7 @@ var sys = require('sys'),
|
|||||||
http = require('http'),
|
http = require('http'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
websocket = require('./../vendor/websocket'),
|
websocket = require('./../vendor/websocket'),
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
httpProxy = require('../../lib/node-http-proxy');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var utils = require('socket.io/lib/socket.io/utils'),
|
var utils = require('socket.io/lib/socket.io/utils'),
|
||||||
@ -28,7 +28,7 @@ var sys = require('sys'),
|
|||||||
http = require('http'),
|
http = require('http'),
|
||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
websocket = require('./../vendor/websocket'),
|
websocket = require('./../vendor/websocket'),
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
httpProxy = require('../../lib/node-http-proxy');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var utils = require('socket.io/lib/socket.io/utils'),
|
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
|
// locations of proxy targets based on ServerRequest headers; specifically
|
||||||
// the HTTP host header.
|
// the HTTP host header.
|
||||||
//
|
//
|
||||||
var ProxyTable = exports.ProxyTable = function (router, silent, hostnameOnly) {
|
var ProxyTable = exports.ProxyTable = function (options) {
|
||||||
events.EventEmitter.call(this);
|
events.EventEmitter.call(this);
|
||||||
|
|
||||||
this.silent = typeof silent !== 'undefined' ? silent : true;
|
this.silent = options.silent || options.silent !== true;
|
||||||
this.hostnameOnly = typeof hostnameOnly !== 'undefined' ? hostnameOnly : false;
|
this.hostnameOnly = options.hostnameOnly === true;
|
||||||
|
|
||||||
if (typeof router === 'object') {
|
if (typeof options.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.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
|
// 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 = options.router;
|
||||||
this.setRoutes(JSON.parse(fs.readFileSync(router)).router);
|
this.setRoutes(JSON.parse(fs.readFileSync(options.router)).router);
|
||||||
|
|
||||||
fs.watchFile(this.routeFile, function () {
|
fs.watchFile(this.routeFile, function () {
|
||||||
fs.readFile(self.routeFile, function (err, data) {
|
fs.readFile(self.routeFile, function (err, data) {
|
||||||
if (err) throw err;
|
if (err) {
|
||||||
|
self.emit('error', err);
|
||||||
|
}
|
||||||
|
|
||||||
self.setRoutes(JSON.parse(data).router);
|
self.setRoutes(JSON.parse(data).router);
|
||||||
self.emit('routes', self.hostnameOnly === false ? self.routes : self.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);
|
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.
|
// Sets the host-based routes to be used by this instance.
|
||||||
//
|
//
|
||||||
ProxyTable.prototype.setRoutes = function (router) {
|
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;
|
this.router = router;
|
||||||
|
|
||||||
@ -127,7 +134,9 @@ ProxyTable.prototype.getProxyLocation = function (req) {
|
|||||||
else {
|
else {
|
||||||
target += req.url;
|
target += req.url;
|
||||||
for (var i in this.routes) {
|
for (var i in this.routes) {
|
||||||
var match, route = this.routes[i];
|
var route = this.routes[i],
|
||||||
|
match;
|
||||||
|
|
||||||
if (match = target.match(route.route)) {
|
if (match = target.match(route.route)) {
|
||||||
var location = route.target.split(':'),
|
var location = route.target.split(':'),
|
||||||
host = location[0],
|
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",
|
"name": "http-proxy",
|
||||||
"description": "A full-featured http reverse proxy for node.js",
|
"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>",
|
"author": "Charlie Robbins <charlie.robbins@gmail.com>",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
{ "name": "Mikeal Rogers", "email": "mikeal.rogers@gmail.com" },
|
{ "name": "Mikeal Rogers", "email": "mikeal.rogers@gmail.com" },
|
||||||
{ "name": "Marak Squires", "email": "marak.squires@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": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -25,6 +26,10 @@
|
|||||||
},
|
},
|
||||||
"main": "./lib/node-http-proxy",
|
"main": "./lib/node-http-proxy",
|
||||||
"bin": { "node-http-proxy": "./bin/node-http-proxy" },
|
"bin": { "node-http-proxy": "./bin/node-http-proxy" },
|
||||||
"scripts": { "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" }
|
"engines": { "node": "0.4.x || 0.5.x" }
|
||||||
}
|
}
|
||||||
|
|||||||
172
test/helpers.js
172
test/helpers.js
@ -5,27 +5,16 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var fs = require('fs'),
|
var assert = require('assert'),
|
||||||
|
fs = require('fs'),
|
||||||
http = require('http'),
|
http = require('http'),
|
||||||
https = require('https'),
|
https = require('https'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
vows = require('vows'),
|
argv = require('optimist').argv,
|
||||||
assert = require('assert'),
|
|
||||||
request = require('request'),
|
request = require('request'),
|
||||||
websocket = require('./../vendor/websocket'),
|
vows = require('vows'),
|
||||||
httpProxy = require('./../lib/node-http-proxy');
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
var loadHttps = exports.loadHttps = function () {
|
var loadHttps = exports.loadHttps = function () {
|
||||||
return {
|
return {
|
||||||
@ -34,21 +23,35 @@ var loadHttps = exports.loadHttps = function () {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var TestRunner = exports.TestRunner = function (protocol, target) {
|
var parseProtocol = exports.parseProtocol = function () {
|
||||||
this.options = {};
|
function setupProtocol (secure) {
|
||||||
this.options.target = {};
|
return {
|
||||||
this.protocol = protocol;
|
secure: secure,
|
||||||
this.target = target;
|
protocols: {
|
||||||
this.testServers = [];
|
http: secure ? 'https' : 'http',
|
||||||
|
ws: secure ? 'wss' : 'ws'
|
||||||
if (protocol === 'https') {
|
}
|
||||||
this.options.https = loadHttps();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target === 'https') {
|
return {
|
||||||
this.options.target = {
|
source: setupProtocol(argv.source === 'secure'),
|
||||||
https: loadHttps()
|
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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,14 +62,18 @@ TestRunner.prototype.assertProxied = function (host, proxyPort, port, createProx
|
|||||||
|
|
||||||
var test = {
|
var test = {
|
||||||
topic: function () {
|
topic: function () {
|
||||||
var that = this, options = {
|
var that = this,
|
||||||
|
options;
|
||||||
|
|
||||||
|
options = {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
uri: self.protocol + '://localhost:' + proxyPort,
|
uri: self.source.protocols.http + '://localhost:' + proxyPort,
|
||||||
headers: {
|
headers: {
|
||||||
host: host
|
host: host
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
function startTest () {
|
function startTest () {
|
||||||
if (port) {
|
if (port) {
|
||||||
return self.startTargetServer(port, output, function () {
|
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) {
|
TestRunner.prototype.assertResponseCode = function (proxyPort, statusCode, createProxy) {
|
||||||
var assertion = "should receive " + statusCode + " responseCode",
|
var assertion = "should receive " + statusCode + " responseCode",
|
||||||
protocol = this.protocol;
|
protocol = this.source.protocols.http;
|
||||||
|
|
||||||
var test = {
|
var test = {
|
||||||
topic: function () {
|
topic: function () {
|
||||||
@ -124,7 +131,6 @@ TestRunner.prototype.assertResponseCode = function (proxyPort, statusCode, creat
|
|||||||
//
|
//
|
||||||
// WebSocketTest
|
// WebSocketTest
|
||||||
//
|
//
|
||||||
|
|
||||||
TestRunner.prototype.webSocketTest = function (options) {
|
TestRunner.prototype.webSocketTest = function (options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -161,7 +167,6 @@ TestRunner.prototype.webSocketTest = function (options) {
|
|||||||
//
|
//
|
||||||
// WebSocketTestWithTable
|
// WebSocketTestWithTable
|
||||||
//
|
//
|
||||||
|
|
||||||
TestRunner.prototype.webSocketTestWithTable = function (options) {
|
TestRunner.prototype.webSocketTestWithTable = function (options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -172,9 +177,9 @@ TestRunner.prototype.webSocketTestWithTable = function (options) {
|
|||||||
options.onListen(socket);
|
options.onListen(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.startProxyServerWithTable(
|
self.startProxyServerWithTable(
|
||||||
options.ports.proxy,
|
options.ports.proxy,
|
||||||
{router: options.router},
|
{ router: options.router },
|
||||||
function (err, proxy) {
|
function (err, proxy) {
|
||||||
if (options.onServer) { options.onServer(proxy) }
|
if (options.onServer) { options.onServer(proxy) }
|
||||||
|
|
||||||
@ -199,8 +204,7 @@ TestRunner.prototype.webSocketTestWithTable = function (options) {
|
|||||||
//
|
//
|
||||||
TestRunner.prototype.startProxyServer = function (port, targetPort, host, callback) {
|
TestRunner.prototype.startProxyServer = function (port, targetPort, host, callback) {
|
||||||
var that = this,
|
var that = this,
|
||||||
options = that.options,
|
proxyServer = httpProxy.createServer(host, targetPort, this.getOptions());
|
||||||
proxyServer = httpProxy.createServer(targetPort, host, options);
|
|
||||||
|
|
||||||
proxyServer.listen(port, function () {
|
proxyServer.listen(port, function () {
|
||||||
that.testServers.push(proxyServer);
|
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
|
// Creates the reverse proxy server with a specified latency
|
||||||
//
|
//
|
||||||
TestRunner.prototype.startLatentProxyServer = function (port, targetPort, host, latency, callback) {
|
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 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 () {
|
setTimeout(function () {
|
||||||
proxy.proxyRequest(req, res, {
|
proxy.proxyRequest(req, res, buffer);
|
||||||
port: targetPort,
|
|
||||||
host: host,
|
|
||||||
buffer: buffer
|
|
||||||
});
|
|
||||||
}, latency);
|
}, latency);
|
||||||
}, this.options);
|
}, this.getOptions());
|
||||||
|
|
||||||
proxyServer.listen(port, function () {
|
proxyServer.listen(port, function () {
|
||||||
that.testServers.push(proxyServer);
|
that.testServers.push(proxyServer);
|
||||||
@ -235,7 +240,9 @@ TestRunner.prototype.startLatentProxyServer = function (port, targetPort, host,
|
|||||||
// Creates the reverse proxy server with a ProxyTable
|
// Creates the reverse proxy server with a ProxyTable
|
||||||
//
|
//
|
||||||
TestRunner.prototype.startProxyServerWithTable = function (port, options, callback) {
|
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 () {
|
proxyServer.listen(port, function () {
|
||||||
that.testServers.push(proxyServer);
|
that.testServers.push(proxyServer);
|
||||||
callback();
|
callback();
|
||||||
@ -248,13 +255,15 @@ TestRunner.prototype.startProxyServerWithTable = function (port, options, callba
|
|||||||
// Creates a latent reverse proxy server using a ProxyTable
|
// Creates a latent reverse proxy server using a ProxyTable
|
||||||
//
|
//
|
||||||
TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, latency, options, callback) {
|
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 proxyServer,
|
//
|
||||||
that = this,
|
var that = this,
|
||||||
proxy = new httpProxy.HttpProxy(merge({}, options, that.options));
|
proxy = new httpProxy.RoutingProxy(merge({}, options, this.getOptions())),
|
||||||
|
proxyServer;
|
||||||
|
|
||||||
var handler = function (req, res) {
|
var handler = function (req, res) {
|
||||||
var buffer = proxy.buffer(req);
|
var buffer = httpProxy.buffer(req);
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
proxy.proxyRequest(req, res, {
|
proxy.proxyRequest(req, res, {
|
||||||
buffer: buffer
|
buffer: buffer
|
||||||
@ -262,9 +271,9 @@ TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, laten
|
|||||||
}, latency);
|
}, latency);
|
||||||
};
|
};
|
||||||
|
|
||||||
proxyServer = that.options.https
|
proxyServer = this.source.https
|
||||||
? https.createServer(that.options.https, handler, that.options)
|
? https.createServer(this.source.https, handler)
|
||||||
: http.createServer(handler, that.options);
|
: http.createServer(handler);
|
||||||
|
|
||||||
proxyServer.listen(port, function () {
|
proxyServer.listen(port, function () {
|
||||||
that.testServers.push(proxyServer);
|
that.testServers.push(proxyServer);
|
||||||
@ -278,7 +287,9 @@ TestRunner.prototype.startProxyServerWithTableAndLatency = function (port, laten
|
|||||||
// Creates proxy server forwarding to the specified options
|
// Creates proxy server forwarding to the specified options
|
||||||
//
|
//
|
||||||
TestRunner.prototype.startProxyServerWithForwarding = function (port, targetPort, host, options, callback) {
|
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 () {
|
proxyServer.listen(port, function () {
|
||||||
that.testServers.push(proxyServer);
|
that.testServers.push(proxyServer);
|
||||||
callback(null, proxyServer);
|
callback(null, proxyServer);
|
||||||
@ -289,14 +300,18 @@ TestRunner.prototype.startProxyServerWithForwarding = function (port, targetPort
|
|||||||
// Creates the 'hellonode' server
|
// Creates the 'hellonode' server
|
||||||
//
|
//
|
||||||
TestRunner.prototype.startTargetServer = function (port, output, callback) {
|
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.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||||
res.write(output);
|
res.write(output);
|
||||||
res.end();
|
res.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
targetServer = this.options.target.https
|
targetServer = this.target.https
|
||||||
? https.createServer(this.options.target.https, handler)
|
? https.createServer(this.target.https, handler)
|
||||||
: http.createServer(handler);
|
: http.createServer(handler);
|
||||||
|
|
||||||
targetServer.listen(port, function () {
|
targetServer.listen(port, function () {
|
||||||
@ -315,3 +330,42 @@ TestRunner.prototype.closeServers = function () {
|
|||||||
|
|
||||||
return this.testServers;
|
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'),
|
var assert = require('assert'),
|
||||||
util = require('util'),
|
util = require('util'),
|
||||||
argv = require('optimist').argv,
|
|
||||||
request = require('request'),
|
request = require('request'),
|
||||||
vows = require('vows'),
|
vows = require('vows'),
|
||||||
helpers = require('./helpers');
|
helpers = require('../helpers');
|
||||||
|
|
||||||
var forwardOptions = {
|
var forwardOptions = {
|
||||||
forward: {
|
forward: {
|
||||||
@ -45,11 +44,11 @@ var badForwardOptions = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var protocol = argv.https ? 'https' : 'http',
|
var options = helpers.parseProtocol(),
|
||||||
target = argv.target ? argv.target : 'http',
|
testName = [options.source.protocols.http, options.target.protocols.http].join('-to-'),
|
||||||
runner = new helpers.TestRunner(protocol, target);
|
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()": {
|
"When using server created by httpProxy.createServer()": {
|
||||||
"with no latency" : {
|
"with no latency" : {
|
||||||
"and a valid target server": runner.assertProxied('localhost', 8080, 8081, function (callback) {
|
"and a valid target server": runner.assertProxied('localhost', 8080, 8081, function (callback) {
|
||||||
@ -12,11 +12,11 @@ var assert = require('assert'),
|
|||||||
argv = require('optimist').argv,
|
argv = require('optimist').argv,
|
||||||
request = require('request'),
|
request = require('request'),
|
||||||
vows = require('vows'),
|
vows = require('vows'),
|
||||||
helpers = require('./helpers'),
|
helpers = require('../helpers');
|
||||||
TestRunner = helpers.TestRunner;
|
|
||||||
|
|
||||||
var protocol = argv.https ? 'https' : 'http',
|
var options = helpers.parseProtocol(),
|
||||||
runner = new TestRunner(protocol),
|
testName = [options.source.protocols.http, options.target.protocols.http].join('-to-'),
|
||||||
|
runner = new helpers.TestRunner(options),
|
||||||
routeFile = path.join(__dirname, 'config.json');
|
routeFile = path.join(__dirname, 'config.json');
|
||||||
|
|
||||||
var fileOptions = {
|
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 using server created by httpProxy.createServer()": {
|
||||||
"when passed a routing table": {
|
"when passed a routing table": {
|
||||||
"and routing by RegExp": {
|
"and routing by RegExp": {
|
||||||
@ -82,16 +82,14 @@ vows.describe('node-http-proxy/proxy-table/' + protocol).addBatch({
|
|||||||
fs.writeFileSync(routeFile, JSON.stringify(config));
|
fs.writeFileSync(routeFile, JSON.stringify(config));
|
||||||
|
|
||||||
this.server.on('routes', function () {
|
this.server.on('routes', function () {
|
||||||
var options = {
|
|
||||||
method: 'GET',
|
|
||||||
uri: protocol + '://localhost:8100',
|
|
||||||
headers: {
|
|
||||||
host: 'dynamic.com'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
runner.startTargetServer(8103, 'hello dynamic.com', function () {
|
runner.startTargetServer(8103, 'hello dynamic.com', function () {
|
||||||
request(options, that.callback);
|
request({
|
||||||
|
method: 'GET',
|
||||||
|
uri: options.source.protocols.http + '://localhost:8100',
|
||||||
|
headers: {
|
||||||
|
host: 'dynamic.com'
|
||||||
|
}
|
||||||
|
}, that.callback);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -30,8 +30,8 @@ var util = require('util'),
|
|||||||
colors = require('colors'),
|
colors = require('colors'),
|
||||||
request = require('request'),
|
request = require('request'),
|
||||||
vows = require('vows'),
|
vows = require('vows'),
|
||||||
websocket = require('./../vendor/websocket'),
|
websocket = require('../../vendor/websocket'),
|
||||||
helpers = require('./helpers');
|
helpers = require('../helpers');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var utils = require('socket.io/lib/socket.io/utils'),
|
var utils = require('socket.io/lib/socket.io/utils'),
|
||||||
@ -43,55 +43,11 @@ catch (ex) {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var protocol = argv.https ? 'https' : 'http',
|
var options = helpers.parseProtocol(),
|
||||||
wsprotocol = argv.https ? 'wss' : 'ws',
|
testName = [options.source.protocols.ws, options.target.protocols.ws].join('-to-'),
|
||||||
runner = new helpers.TestRunner(protocol);
|
runner = new helpers.TestRunner(options);
|
||||||
|
|
||||||
vows.describe('node-http-proxy/websocket/' + wsprotocol).addBatch({
|
vows.describe('node-http-proxy/http-proxy/' + testName).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']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"When using server created by httpProxy.createServer()": {
|
"When using server created by httpProxy.createServer()": {
|
||||||
"with no latency" : {
|
"with no latency" : {
|
||||||
"when an inbound message is sent from a WebSocket client": {
|
"when an inbound message is sent from a WebSocket client": {
|
||||||
@ -102,8 +58,8 @@ vows.describe('node-http-proxy/websocket/' + wsprotocol).addBatch({
|
|||||||
runner.webSocketTest({
|
runner.webSocketTest({
|
||||||
io: io,
|
io: io,
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
wsprotocol: wsprotocol,
|
wsprotocol: options.source.protocols.ws,
|
||||||
protocol: protocol,
|
protocol: options.source.protocols.http,
|
||||||
ports: {
|
ports: {
|
||||||
target: 8130,
|
target: 8130,
|
||||||
proxy: 8131
|
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) {
|
"the origin and sec-websocket-origin headers should match": function (err, msg, headers) {
|
||||||
assert.isString(headers.response['sec-websocket-location']);
|
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']);
|
assert.equal(headers.request.Origin, headers.response['sec-websocket-origin']);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -141,8 +97,8 @@ vows.describe('node-http-proxy/websocket/' + wsprotocol).addBatch({
|
|||||||
runner.webSocketTest({
|
runner.webSocketTest({
|
||||||
io: io,
|
io: io,
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
wsprotocol: wsprotocol,
|
wsprotocol: options.source.protocols.ws,
|
||||||
protocol: protocol,
|
protocol: options.source.protocols.http,
|
||||||
ports: {
|
ports: {
|
||||||
target: 8132,
|
target: 8132,
|
||||||
proxy: 8133
|
proxy: 8133
|
||||||
@ -170,8 +126,8 @@ vows.describe('node-http-proxy/websocket/' + wsprotocol).addBatch({
|
|||||||
runner.webSocketTest({
|
runner.webSocketTest({
|
||||||
io: io,
|
io: io,
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
wsprotocol: wsprotocol,
|
wsprotocol: options.source.protocols.ws,
|
||||||
protocol: protocol,
|
protocol: options.source.protocols.http,
|
||||||
ports: {
|
ports: {
|
||||||
target: 8134,
|
target: 8134,
|
||||||
proxy: 8135
|
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) {
|
"the origin and sec-websocket-origin headers should match": function (err, msg, headers) {
|
||||||
assert.isString(headers.response['sec-websocket-location']);
|
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']);
|
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