Skip to content

Commit

Permalink
cookieDomainRewrite option (#1009)
Browse files Browse the repository at this point in the history
  • Loading branch information
Volune authored and jcrugzz committed Aug 12, 2016
1 parent 9d06ca9 commit 1e52f66
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 30 deletions.
12 changes: 12 additions & 0 deletions README.md
Expand Up @@ -337,6 +337,18 @@ proxyServer.listen(8015);
* **hostRewrite**: rewrites the location hostname on (201/301/302/307/308) redirects.
* **autoRewrite**: rewrites the location host/port on (201/301/302/307/308) redirects based on requested host/port. Default: false.
* **protocolRewrite**: rewrites the location protocol on (201/301/302/307/308) redirects to 'http' or 'https'. Default: null.
* **cookieDomainRewrite**: rewrites domain of `set-cookie` headers. Possible values:
* `false` (default): disable cookie rewriting
* String: new domain, for example `cookieDomainRewrite: "new.domain"`. To remove the domain, use `cookieDomainRewrite: ""`.
* Object: mapping of domains to new domains, use `"*"` to match all domains.
For example keep one domain unchanged, rewrite one domain and remove other domains:
```
cookieDomainRewrite: {
"unchanged.domain": "unchanged.domain",
"old.domain": "new.domain",
"*": ""
}
```
* **headers**: object with extra headers to be added to target requests.

**NOTE:**
Expand Down
38 changes: 37 additions & 1 deletion lib/http-proxy/common.js
Expand Up @@ -4,7 +4,8 @@ var common = exports,
required = require('requires-port');

var upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i,
isSSL = /^https|wss/;
isSSL = /^https|wss/,
cookieDomainRegex = /(;\s*domain=)([^;]+)/i;

/**
* Simple Regex for testing if protocol is https
Expand Down Expand Up @@ -201,6 +202,41 @@ common.urlJoin = function() {
return retSegs.join('?')
};

/**
* Rewrites or removes the domain of a cookie header
*
* @param {String|Array} Header
* @param {Object} Config, mapping of domain to rewritten domain.
* '*' key to match any domain, null value to remove the domain.
*
* @api private
*/
common.rewriteCookieDomain = function rewriteCookieDomain(header, config) {
if (Array.isArray(header)) {
return header.map(function (headerElement) {
return rewriteCookieDomain(headerElement, config);
});
}
return header.replace(cookieDomainRegex, function(match, prefix, previousDomain) {
var newDomain;
if (previousDomain in config) {
newDomain = config[previousDomain];
} else if ('*' in config) {
newDomain = config['*'];
} else {
//no match, return previous domain
return match;
}
if (newDomain) {
//replace domain
return prefix + newDomain;
} else {
//remove domain
return '';
}
});
};

/**
* Check the host and see if it potentially has a port in it (keep it simple)
*
Expand Down
16 changes: 13 additions & 3 deletions lib/http-proxy/passes/web-outgoing.js
@@ -1,4 +1,5 @@
var url = require('url'),
common = require('../common'),
passes = exports;

var redirectRegex = /^201|30(1|2|7|8)$/;
Expand Down Expand Up @@ -77,13 +78,22 @@ var redirectRegex = /^201|30(1|2|7|8)$/;
* @param {ClientRequest} Req Request object
* @param {IncomingMessage} Res Response object
* @param {proxyResponse} Res Response object from the proxy request
* @param {Object} Options options.cookieDomainRewrite: Config to rewrite cookie domain
*
* @api private
*/
function writeHeaders(req, res, proxyRes) {
function writeHeaders(req, res, proxyRes, options) {
var rewriteCookieDomainConfig = options.cookieDomainRewrite;
if (typeof rewriteCookieDomainConfig === 'string') { //also test for ''
rewriteCookieDomainConfig = { '*': rewriteCookieDomainConfig };
}
Object.keys(proxyRes.headers).forEach(function(key) {
if(proxyRes.headers[key] != undefined){
res.setHeader(String(key).trim(), proxyRes.headers[key]);
var header = proxyRes.headers[key];
if (header != undefined) {
if (rewriteCookieDomainConfig && key.toLowerCase() === 'set-cookie') {
header = common.rewriteCookieDomain(header, rewriteCookieDomainConfig);
}
res.setHeader(String(key).trim(), header);
}
});
},
Expand Down
110 changes: 84 additions & 26 deletions test/lib-http-proxy-passes-web-outgoing-test.js
Expand Up @@ -6,23 +6,23 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {
beforeEach(function() {
this.req = {
headers: {
host: "ext-auto.com"
host: 'ext-auto.com'
}
};
this.proxyRes = {
statusCode: 301,
headers: {
location: "http://backend.com/"
location: 'http://backend.com/'
}
};
this.options = {
target: "http://backend.com"
target: 'http://backend.com'
};
});

context('rewrites location host with hostRewrite', function() {
beforeEach(function() {
this.options.hostRewrite = "ext-manual.com";
this.options.hostRewrite = 'ext-manual.com';
});
[201, 301, 302, 307, 308].forEach(function(code) {
it('on ' + code, function() {
Expand Down Expand Up @@ -52,14 +52,14 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {

it('not when the redirected location does not match target host', function() {
this.proxyRes.statusCode = 302;
this.proxyRes.headers.location = "http://some-other/";
this.proxyRes.headers.location = 'http://some-other/';
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://some-other/');
});

it('not when the redirected location does not match target port', function() {
this.proxyRes.statusCode = 302;
this.proxyRes.headers.location = "http://backend.com:8080/";
this.proxyRes.headers.location = 'http://backend.com:8080/';
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://backend.com:8080/');
});
Expand Down Expand Up @@ -91,14 +91,14 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {

it('not when the redirected location does not match target host', function() {
this.proxyRes.statusCode = 302;
this.proxyRes.headers.location = "http://some-other/";
this.proxyRes.headers.location = 'http://some-other/';
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://some-other/');
});

it('not when the redirected location does not match target port', function() {
this.proxyRes.statusCode = 302;
this.proxyRes.headers.location = "http://backend.com:8080/";
this.proxyRes.headers.location = 'http://backend.com:8080/';
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('http://backend.com:8080/');
});
Expand Down Expand Up @@ -129,13 +129,13 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {
});

it('works together with hostRewrite', function() {
this.options.hostRewrite = 'ext-manual.com'
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('works together with autoRewrite', function() {
this.options.autoRewrite = true
this.options.autoRewrite = true;
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
expect(this.proxyRes.headers.location).to.eql('https://ext-auto.com/');
});
Expand Down Expand Up @@ -199,31 +199,89 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () {
writeHead: function(n) {
expect(n).to.eql(200);
}
}
};

httpProxy.writeStatusCode({}, res, { statusCode: 200 });
});
});

describe('#writeHeaders', function() {
var proxyRes = {
headers: {
hey: 'hello',
how: 'are you?'
}
};
beforeEach(function() {
this.proxyRes = {
headers: {
hey: 'hello',
how: 'are you?',
'set-cookie': 'hello; domain=my.domain; path=/'
}
};
this.res = {
setHeader: function(k, v) {
this.headers[k] = v;
},
headers: {}
};
});

var res = {
setHeader: function(k, v) {
this.headers[k] = v;
},
headers: {}
};
it('writes headers', function() {
var options = {};

httpProxy.writeHeaders({}, res, proxyRes);
httpProxy.writeHeaders({}, this.res, this.proxyRes, options);

expect(this.res.headers.hey).to.eql('hello');
expect(this.res.headers.how).to.eql('are you?');
});

expect(res.headers.hey).to.eql('hello');
expect(res.headers.how).to.eql('are you?');
it('does not rewrite domain', function() {
var options = {};

httpProxy.writeHeaders({}, this.res, this.proxyRes, options);

expect(this.res.headers['set-cookie']).to.eql('hello; domain=my.domain; path=/');
});

it('rewrites domain', function() {
var options = {
cookieDomainRewrite: 'my.new.domain'
};

httpProxy.writeHeaders({}, this.res, this.proxyRes, options);

expect(this.res.headers['set-cookie']).to.eql('hello; domain=my.new.domain; path=/');
});

it('removes domain', function() {
var options = {
cookieDomainRewrite: ''
};

httpProxy.writeHeaders({}, this.res, this.proxyRes, options);

expect(this.res.headers['set-cookie']).to.eql('hello; path=/');
});

it('rewrites headers with advanced configuration', function() {
var options = {
cookieDomainRewrite: {
'*': '',
'my.old.domain': 'my.new.domain',
'my.special.domain': 'my.special.domain'
}
};
this.proxyRes.headers['set-cookie'] = [
'hello-on-my.domain; domain=my.domain; path=/',
'hello-on-my.old.domain; domain=my.old.domain; path=/',
'hello-on-my.special.domain; domain=my.special.domain; path=/'
];

httpProxy.writeHeaders({}, this.res, this.proxyRes, options);

expect(this.res.headers['set-cookie'])
.to.contain('hello-on-my.domain; path=/');
expect(this.res.headers['set-cookie'])
.to.contain('hello-on-my.old.domain; domain=my.new.domain; path=/');
expect(this.res.headers['set-cookie'])
.to.contain('hello-on-my.special.domain; domain=my.special.domain; path=/');
});
});


Expand Down

0 comments on commit 1e52f66

Please sign in to comment.