diff --git a/lib/cookies.js b/lib/cookies.js index 5619ce26..e5affba5 100644 --- a/lib/cookies.js +++ b/lib/cookies.js @@ -1,5 +1,4 @@ var URL = require('url'); -var libCookie = require('cookie'); var setCookie = require('set-cookie-parser'); var TLD = require('tld'); var Transform = require('stream').Transform; @@ -21,6 +20,54 @@ function cookies(config) { var REDIRECT_QUERY_PARAM = '__proxy_cookies_to'; + // Serializes cookie without changing it + function serializeCookie(cookie) { + var str = cookie.name + '=' + cookie.value; + + if (cookie.maxAge) { + str += '; Max-Age=' + cookie.maxAge; + } + if (cookie.domain) { + str += '; Domain=' + cookie.domain; + } + if (cookie.path) { + str += '; Path=' + cookie.path; + } + if (cookie.expires) { + str += '; Expires=' + cookie.expires.toUTCString(); + } + if (cookie.httpOnly) { + str += '; HttpOnly'; + } + if (cookie.secure) { + str += '; Secure'; + } + return str; + } + + // Parses the cookies without changing its value + function parseCookies(rawCookies) { + var obj = {}; + var cookies; + if(typeof rawCookies !== 'string') return {}; + if(rawCookies === '') return {}; + cookies = rawCookies.split(';'); + for (var i = 0, ii = cookies.length; i < ii; i++) { + // Removes first space if there is one + // V8 should always support trimLeft even though there is no standard for it. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimLeft + // https://www.npmjs.com/package/string could be a solution in case trimLeft() is dropped from V8. + cookies[i] = cookies[i].trimLeft(); + // Puts the name and value in the object obj. + if(cookies[i].indexOf('=') !== -1) { + obj[cookies[i].substring(0, cookies[i].indexOf('='))] = cookies[i].substring(cookies[i].indexOf('=') + 1); + } else { + obj[cookies[i]] = ''; + } + } + return obj; + } + // normally we do nothing here, but when the user is switching protocols or subdomains, the handleResponse function // will rewrite the links to start with the old protocol & domain (so that we get sent the cookies), and then it // will copy the old cookies to the new path @@ -29,10 +76,11 @@ function cookies(config) { if (uri.query[REDIRECT_QUERY_PARAM]) { var nextUri = URL.parse(uri.query[REDIRECT_QUERY_PARAM]); debug('copying cookies from %s to %s', data.url, uri.query[REDIRECT_QUERY_PARAM]); - var cookies = libCookie.parse(data.headers.cookie || ''); + var cookies = parseCookies(data.headers.cookie || ''); var setCookieHeaders = Object.keys(cookies).map(function(name) { - var value = cookies[name]; - return libCookie.serialize(name, value, { + return serializeCookie({ + name: name, + value: cookies[name], path: config.prefix + nextUri.protocol + '//' + nextUri.host + '/' }); }); @@ -62,8 +110,7 @@ function cookies(config) { cookie.path = config.prefix + targetUri.protocol + '//' + targetUri.host + (cookie.path || '/'); delete cookie.domain; delete cookie.secure; // todo: maybe leave this if we knot the proxy is being accessed over https? - cookie.value = decodeURIComponent(cookie.value); // Used to avoid double percent-encoding - return libCookie.serialize(cookie.name, cookie.value, cookie); + return serializeCookie(cookie); }); } @@ -75,9 +122,11 @@ function cookies(config) { debug('copying cookies from %s to %s', data.url, data.redirectUrl); // get all of the old cookies (from the request) indexed by name, and create set-cookie headers for each one - var oldCookies = libCookie.parse(data.clientRequest.headers.cookie || ''); + var oldCookies = parseCookies(data.clientRequest.headers.cookie || ''); var oldSetCookieHeaders = _.mapValues(oldCookies, function(value, name) { - return libCookie.serialize(name, value, { + return serializeCookie({ + name: name, + value: value, path: config.prefix + nextUri.protocol + '//' + nextUri.host + '/' }); }); diff --git a/package.json b/package.json index 593b6916..626810d8 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "dependencies": { "async": "^2.0.0", "content-type": "^1.0.1", - "cookie": "^0.3.1", "debug": "^2.2.0", "iconv-lite": "^0.4.13", "lodash": "^4.4.0", diff --git a/test/cookies_spec.js b/test/cookies_spec.js index 8c7b26f4..ba4ff007 100644 --- a/test/cookies_spec.js +++ b/test/cookies_spec.js @@ -67,6 +67,22 @@ test('should rewrite the cookie that is percent-encoded correctly', function(t) t.end(); }); +test('should rewrite the cookie that is weird but correct correctly', function(t) { + var instance = cookies({ + prefix: '/proxy/', + processContentTypes: [] + }); + var data = getData(); + data.headers['set-cookie'] = ['myCookie=is=called&john=doe&for=a&reason=#WeDontParseCookieValues&only=its¶meters=!; Path=/myAwesomePath; HttpOnly']; + instance.handleResponse(data); + var expected = [ + 'myCookie=is=called&john=doe&for=a&reason=#WeDontParseCookieValues&only=its¶meters=!; Path=/proxy/http://example.com/myAwesomePath; HttpOnly' + ]; + var actual = data.headers['set-cookie']; + t.same(actual, expected); + t.end(); +}); + test("should copy any missing cookies to a 3xx redirect", function(t) { t.plan(1); var instance = cookies({