Add support for auto host rewriting and protocol rewriting

auto host rewriting allows rewriting to work as expected in most
cases without extra cumbersome configuration

protocol rewriting allows node-http-proxy to be able to listen
over HTTPS and properly reverse-proxy to sites running over HTTP
(to avoid doing SSL twice)
This commit is contained in:
Matt Hauck 2015-02-05 12:17:26 -08:00
parent 9ece52fac4
commit 7f2f3ac35c
3 changed files with 113 additions and 40 deletions

View File

@ -42,6 +42,8 @@ module.exports.createProxyServer =
* localAddress : <Local interface string to bind for outgoing connections>
* changeOrigin: <true/false, Default: false - changes the origin of the host header to the target URL>
* hostRewrite: rewrites the location hostname on (301/302/307/308) redirects, Default: null.
* autoRewrite: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false.
* protocolRewrite: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
* }
*
* NOTE: `options.ws` and `options.ssl` are optional.

View File

@ -47,11 +47,19 @@ var redirectRegex = /^30(1|2|7|8)$/;
},
function setRedirectHostRewrite(req, res, proxyRes, options) {
if (options.hostRewrite
if ((options.hostRewrite || options.autoRewrite || options.protocolRewrite)
&& proxyRes.headers['location']
&& redirectRegex.test(proxyRes.statusCode)) {
var u = url.parse(proxyRes.headers['location']);
u.host = options.hostRewrite;
if (options.hostRewrite) {
u.host = options.hostRewrite;
} else if (options.autoRewrite) {
u.host = req.headers['host'];
}
if (options.protocolRewrite) {
u.protocol = options.protocolRewrite;
}
proxyRes.headers['location'] = u.format();
}
},

View File

@ -3,54 +3,117 @@ var httpProxy = require('../lib/http-proxy/passes/web-outgoing'),
describe('lib/http-proxy/passes/web-outgoing.js', function () {
describe('#setRedirectHostRewrite', function () {
context('rewrites location host to option', function() {
beforeEach(function() {
this.proxyRes = {
statusCode: 301,
headers: {
location: "http://f.com/"
}
};
beforeEach(function() {
this.req = {
headers: {
host: "x2.com"
}
};
this.proxyRes = {
statusCode: 301,
headers: {
location: "http://f.com/"
}
};
});
context('rewrites location host with hostRewrite', function() {
beforeEach(function() {
this.options = {
hostRewrite: "x.com"
};
});
it('on 301', function() {
this.proxyRes.statusCode = 301;
httpProxy.setRedirectHostRewrite({}, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://'+this.options.hostRewrite+'/');
});
it('on 302', function() {
this.proxyRes.statusCode = 302;
httpProxy.setRedirectHostRewrite({}, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://'+this.options.hostRewrite+'/');
});
it('on 307', function() {
this.proxyRes.statusCode = 307;
httpProxy.setRedirectHostRewrite({}, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://'+this.options.hostRewrite+'/');
});
it('on 308', function() {
this.proxyRes.statusCode = 308;
httpProxy.setRedirectHostRewrite({}, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://'+this.options.hostRewrite+'/');
[301, 302, 307, 308].forEach(function(code) {
it('on ' + code, function() {
this.proxyRes.statusCode = code;
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://'+this.options.hostRewrite+'/');
});
});
it('not on 200', function() {
this.proxyRes.statusCode = 200;
httpProxy.setRedirectHostRewrite({}, {}, this.proxyRes, this.options);
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://f.com/');
});
it('not when hostRewrite is unset', function() {
httpProxy.setRedirectHostRewrite({}, {}, this.proxyRes, {});
delete this.options.hostRewrite;
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://f.com/');
});
it('takes precedence over autoRewrite', function() {
this.options.autoRewrite = true;
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://'+this.options.hostRewrite+'/');
});
});
context('rewrites location host with autoRewrite', function() {
beforeEach(function() {
this.options = {
autoRewrite: true,
};
});
[301, 302, 307, 308].forEach(function(code) {
it('on ' + code, function() {
this.proxyRes.statusCode = code;
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://'+this.req.headers.host+'/');
});
});
it('not on 200', function() {
this.proxyRes.statusCode = 200;
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://f.com/');
});
it('not when autoRewrite is unset', function() {
delete this.options.autoRewrite;
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://f.com/');
});
});
context('rewrites location protocol with protocolRewrite', function() {
beforeEach(function() {
this.options = {
protocolRewrite: 'https',
};
});
[301, 302, 307, 308].forEach(function(code) {
it('on ' + code, function() {
this.proxyRes.statusCode = code;
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('https://f.com/');
});
});
it('not on 200', function() {
this.proxyRes.statusCode = 200;
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://f.com/');
});
it('not when protocolRewrite is unset', function() {
delete this.options.protocolRewrite;
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://f.com/');
});
it('works together with hostRewrite', function() {
this.options.hostRewrite = 'x.com'
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('https://x.com/');
});
it('works together with autoRewrite', function() {
this.options.autoRewrite = true
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('https://x2.com/');
});
});
});
@ -64,7 +127,7 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {
}
}, {}, proxyRes);
expect(proxyRes.headers.connection).to.eql('close');
expect(proxyRes.headers.connection).to.eql('close');
});
it('set the right connection with 1.0 - req.connection', function() {
@ -76,7 +139,7 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {
}
}, {}, proxyRes);
expect(proxyRes.headers.connection).to.eql('hey');
expect(proxyRes.headers.connection).to.eql('hey');
});
it('set the right connection - req.connection', function() {
@ -88,7 +151,7 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {
}
}, {}, proxyRes);
expect(proxyRes.headers.connection).to.eql('hola');
expect(proxyRes.headers.connection).to.eql('hola');
});
it('set the right connection - `keep-alive`', function() {
@ -100,7 +163,7 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {
}
}, {}, proxyRes);
expect(proxyRes.headers.connection).to.eql('keep-alive');
expect(proxyRes.headers.connection).to.eql('keep-alive');
});
});
@ -153,4 +216,4 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {
});
});