From 11480ab4c22b96aed2f31c0f33d763191cde378c Mon Sep 17 00:00:00 2001 From: Anton Griadchenko Date: Wed, 28 Aug 2019 11:16:17 +0300 Subject: [PATCH 1/2] Feature: Add 'megreCookies' options --- README.md | 14 +++++++++ lib/http-proxy.js | 1 + lib/http-proxy/common.js | 26 +++++++++++++++- lib/http-proxy/passes/web-outgoing.js | 4 +++ ...lib-http-proxy-passes-web-outgoing-test.js | 31 +++++++++++++++++++ 5 files changed, 75 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b2d0b3a2..011feb546 100644 --- a/README.md +++ b/README.md @@ -395,6 +395,20 @@ proxyServer.listen(8015); }; ``` +* **mergeCookies**: true/false, merges `set-cookie` header from a request passed to `httpProxy.web` and from response. + E.g.: + ``` + const http = require("http"); + const httpProxy = require("http-proxy"); + const proxy = httpProxy.createProxyServer({}); + + const gateway = http.createServer((req, res) => { + res.setHeader('set-cookie', ["gateway=true; Path=/"]); + proxy.web(req, res, {target: "http://localhost:3002"}); + }); + + gateway.listen(3003); + ``` **NOTE:** `options.ws` and `options.ssl` are optional. diff --git a/lib/http-proxy.js b/lib/http-proxy.js index 8ea278938..0a24789d6 100644 --- a/lib/http-proxy.js +++ b/lib/http-proxy.js @@ -40,6 +40,7 @@ function createProxyServer(options) { * hostRewrite: rewrites the location hostname on (201/301/302/307/308) redirects, Default: null. * 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. + * mergeCookies: allows to merge `set-cookie` headers from passed response and response from target. Default: false. * } * * NOTE: `options.ws` and `options.ssl` are optional. diff --git a/lib/http-proxy/common.js b/lib/http-proxy/common.js index 6513e81d8..10feca286 100644 --- a/lib/http-proxy/common.js +++ b/lib/http-proxy/common.js @@ -49,7 +49,7 @@ common.setupOutgoing = function(outgoing, options, req, forward) { if (options.auth) { outgoing.auth = options.auth; } - + if (options.ca) { outgoing.ca = options.ca; } @@ -236,6 +236,30 @@ common.rewriteCookieProperty = function rewriteCookieProperty(header, config, pr }); }; +/** + * Merges `Set-Cookie` header + * + * @param {string|[string]} setCookie + * @param {string|[string]} upstreamSetCookie + * @returns {[string]} + * + * @api private + */ +common.mergeSetCookie = function mergeCookie(setCookie, upstreamSetCookie) { + var existingCookieArray = setCookie || [], + upstreamCookieArray = upstreamSetCookie || []; + + if (!Array.isArray(existingCookieArray)) { + existingCookieArray = [existingCookieArray] + } + + if (!Array.isArray(upstreamCookieArray)) { + upstreamCookieArray = [upstreamCookieArray] + } + + return [].concat(existingCookieArray, upstreamCookieArray) +}; + /** * Check the host and see if it potentially has a port in it (keep it simple) * diff --git a/lib/http-proxy/passes/web-outgoing.js b/lib/http-proxy/passes/web-outgoing.js index 46352f6e3..6b6278e58 100644 --- a/lib/http-proxy/passes/web-outgoing.js +++ b/lib/http-proxy/passes/web-outgoing.js @@ -86,6 +86,7 @@ module.exports = { // <-- var rewriteCookieDomainConfig = options.cookieDomainRewrite, rewriteCookiePathConfig = options.cookiePathRewrite, preserveHeaderKeyCase = options.preserveHeaderKeyCase, + mergeCookiesConfig = options.mergeCookies, rawHeaderKeyMap, setHeader = function(key, header) { if (header == undefined) return; @@ -95,6 +96,9 @@ module.exports = { // <-- if (rewriteCookiePathConfig && key.toLowerCase() === 'set-cookie') { header = common.rewriteCookieProperty(header, rewriteCookiePathConfig, 'path'); } + if (mergeCookiesConfig && key.toLowerCase() === 'set-cookie') { + header = common.mergeSetCookie(res.getHeader("set-cookie"), header) + } res.setHeader(String(key).trim(), header); }; diff --git a/test/lib-http-proxy-passes-web-outgoing-test.js b/test/lib-http-proxy-passes-web-outgoing-test.js index a509cf1ae..c3b8ded1f 100644 --- a/test/lib-http-proxy-passes-web-outgoing-test.js +++ b/test/lib-http-proxy-passes-web-outgoing-test.js @@ -261,6 +261,9 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () { // Header names are lower-cased this.headers[k.toLowerCase()] = v; }, + getHeader: function (k) { + return this.headers[k.toLowerCase()] + }, headers: {} }; }); @@ -404,6 +407,34 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () { expect(this.res.headers['set-cookie']) .to.contain('hello-on-my.special.domain; domain=my.special.domain; path=/'); }); + + it('appends set-cookies header to an existing one', function () { + var options = { + mergeCookies: true, + }; + + this.res.setHeader("set-cookie", ['hello; domain=my.domain; path=/']); + + httpProxy.writeHeaders({}, this.res, this.proxyRes, options); + + expect(this.res.headers['set-cookie']).to.be.an(Array); + expect(this.res.headers['set-cookie']).to.have.length(3); + }); + + it('appends set-cookies header to an existing one (set-cookie is not an array)', function () { + var options = { + mergeCookies: true, + }; + + this.proxyRes.headers = Object.assign({}, this.proxyRes.headers, {'set-cookie': 'hello1; domain=my.domain; path=/'}); + + this.res.setHeader("set-cookie", 'hello; domain=my.domain; path=/'); + + httpProxy.writeHeaders({}, this.res, this.proxyRes, options); + + expect(this.res.headers['set-cookie']).to.be.an(Array); + expect(this.res.headers['set-cookie']).to.have.length(2); + }); }); From 3fcde39df6e0dcb603aff70a8f4c0d8e90e580de Mon Sep 17 00:00:00 2001 From: Anton Griadchenko Date: Wed, 28 Aug 2019 16:59:06 +0300 Subject: [PATCH 2/2] Replace quotations --- lib/http-proxy/passes/web-outgoing.js | 2 +- test/lib-http-proxy-passes-web-outgoing-test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/http-proxy/passes/web-outgoing.js b/lib/http-proxy/passes/web-outgoing.js index 6b6278e58..e1d52241f 100644 --- a/lib/http-proxy/passes/web-outgoing.js +++ b/lib/http-proxy/passes/web-outgoing.js @@ -97,7 +97,7 @@ module.exports = { // <-- header = common.rewriteCookieProperty(header, rewriteCookiePathConfig, 'path'); } if (mergeCookiesConfig && key.toLowerCase() === 'set-cookie') { - header = common.mergeSetCookie(res.getHeader("set-cookie"), header) + header = common.mergeSetCookie(res.getHeader('set-cookie'), header) } res.setHeader(String(key).trim(), header); }; diff --git a/test/lib-http-proxy-passes-web-outgoing-test.js b/test/lib-http-proxy-passes-web-outgoing-test.js index c3b8ded1f..524ad6955 100644 --- a/test/lib-http-proxy-passes-web-outgoing-test.js +++ b/test/lib-http-proxy-passes-web-outgoing-test.js @@ -413,7 +413,7 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () { mergeCookies: true, }; - this.res.setHeader("set-cookie", ['hello; domain=my.domain; path=/']); + this.res.setHeader('set-cookie', ['hello; domain=my.domain; path=/']); httpProxy.writeHeaders({}, this.res, this.proxyRes, options); @@ -428,7 +428,7 @@ describe('lib/http-proxy/passes/web-outgoing.js', function () { this.proxyRes.headers = Object.assign({}, this.proxyRes.headers, {'set-cookie': 'hello1; domain=my.domain; path=/'}); - this.res.setHeader("set-cookie", 'hello; domain=my.domain; path=/'); + this.res.setHeader('set-cookie', 'hello; domain=my.domain; path=/'); httpProxy.writeHeaders({}, this.res, this.proxyRes, options);