Commit 0cde9ad2 authored by Dave Brotherstone's avatar Dave Brotherstone
Browse files

Adds support for protocol relative URLs in location header

`url.parse` doesn't parse protocol relative URLs, but they are valid
in location headers according to RFC7231
(https://tools.ietf.org/html/rfc7231#section-7.1.2, and specified in
RFC3986 https://tools.ietf.org/html/rfc3986#section-4.2)

This commit fixes that by temporarily adding an `http:` to a URL that
begins with `//` before parsing and removing the protocol before
formatting.
1 merge request!1298Adds support for protocol relative URLs in location header
Pipeline #1323 failed with stages
Showing with 41 additions and 1 deletion
+41 -1
......@@ -51,8 +51,15 @@ module.exports = { // <--
if ((options.hostRewrite || options.autoRewrite || options.protocolRewrite)
&& proxyRes.headers['location']
&& redirectRegex.test(proxyRes.statusCode)) {
var protocolPrefix = '';
if (proxyRes.headers['location'].substr(0, 2) === '//') {
// Make protocol relative URLs simply http
// This just enables correct parsing in url.parse,
// we will strip the protocol on the end result anyway
protocolPrefix = 'http:';
}
var target = url.parse(options.target);
var u = url.parse(proxyRes.headers['location']);
var u = url.parse(protocolPrefix + proxyRes.headers['location']);
// make sure the redirected host matches the target host before rewriting
if (target.host != u.host) {
......@@ -68,6 +75,13 @@ module.exports = { // <--
u.protocol = options.protocolRewrite;
}
// If we had to prefix the URL with a protocol,
// remove it so we retain the protocol relative
// of the response URL
if (protocolPrefix) {
u.protocol = '';
}
proxyRes.headers['location'] = u.format();
}
},
......
......@@ -63,6 +63,12 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://backend.com:8080/');
});
it('handles protocol relative URLs', function() {
this.proxyRes.headers.location = '//backend.com';
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('//ext-manual.com/');
});
});
context('rewrites location host with autoRewrite', function() {
......@@ -128,17 +134,37 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {
expect(this.proxyRes.headers.location).to.eql('http://backend.com/');
});
it('not when protocol relative URL is used', function() {
this.proxyRes.headers.location = '//backend.com';
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('//backend.com/');
});
it('works together with hostRewrite', function() {
this.options.hostRewrite = 'ext-manual.com';
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('https://ext-manual.com/');
});
it('not with hostRewrite when a protocol relative URL is used', function() {
this.options.hostRewrite = 'ext-manual.com';
this.proxyRes.headers.location = '//backend.com';
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('//ext-manual.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://ext-auto.com/');
});
it('not with autoRewrite when a protocol relative URL is used', function() {
this.options.autoRewrite = true;
this.proxyRes.headers.location = '//backend.com/';
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('//ext-auto.com/');
});
});
});
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment