From e8ac2254a6886eb8de65f5374ab7d57a25b9e845 Mon Sep 17 00:00:00 2001 From: Quite Quiet Date: Wed, 8 Feb 2023 21:12:05 +0100 Subject: [PATCH 1/2] Support multiple cookies for follow_set_cookies It appears that cookies.js expects the format ofthe `Set Cookies` header to function. This sounds reasonable, but the original request cookies are stored as a string not as key-value pair strings, so it needs to be separated again to be forwarded correctly in the redirect. --- lib/needle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/needle.js b/lib/needle.js index e153b92eb..3d4651efd 100644 --- a/lib/needle.js +++ b/lib/needle.js @@ -542,7 +542,7 @@ Needle.prototype.send_request = function(count, method, uri, config, post_data, // if follow_set_cookies is true, insert cookies in the next request's headers. // we set both the original request cookies plus any response cookies we might have received. if (config.follow_set_cookies && utils.host_and_ports_match(headers.location, uri)) { - var request_cookies = cookies.read(config.headers['cookie']); + var request_cookies = cookies.read(config.headers['cookie'].split(';')); config.previous_resp_cookies = resp.cookies; if (Object.keys(request_cookies).length || Object.keys(resp.cookies || {}).length) { config.headers['cookie'] = cookies.write(extend(request_cookies, resp.cookies)); From ba7c1f47f441640043530337335553ada6d8ddc6 Mon Sep 17 00:00:00 2001 From: Quite Quiet Date: Sun, 7 Jan 2024 16:22:08 +0100 Subject: [PATCH 2/2] Add tests --- lib/cookies.js | 3 ++- lib/needle.js | 2 +- test/cookies_spec.js | 47 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/lib/cookies.js b/lib/cookies.js index 0f48afcff..18aefed81 100644 --- a/lib/cookies.js +++ b/lib/cookies.js @@ -20,7 +20,7 @@ var SEP_SEMICOLON = /\s*\x3B\s*/; var KEY_INDEX = 1; // index of key from COOKIE_PAIR match var VALUE_INDEX = 3; // index of value from COOKIE_PAIR match -// Returns a copy str trimmed and without trainling semicolon. +// Returns a copy str trimmed and without trailing semicolon. function cleanCookieString(str) { return str.trim().replace(/\x3B+$/, ''); } @@ -53,6 +53,7 @@ function parseSetCookieString(str) { // Each key represents the name of a cookie. function parseSetCookieHeader(header) { if (!header) return {}; + if (typeof header === 'string' || header instanceof String) header = header.split(';'); header = Array.isArray(header) ? header : [header]; return header.reduce(function(res, str) { diff --git a/lib/needle.js b/lib/needle.js index 3d4651efd..e153b92eb 100644 --- a/lib/needle.js +++ b/lib/needle.js @@ -542,7 +542,7 @@ Needle.prototype.send_request = function(count, method, uri, config, post_data, // if follow_set_cookies is true, insert cookies in the next request's headers. // we set both the original request cookies plus any response cookies we might have received. if (config.follow_set_cookies && utils.host_and_ports_match(headers.location, uri)) { - var request_cookies = cookies.read(config.headers['cookie'].split(';')); + var request_cookies = cookies.read(config.headers['cookie']); config.previous_resp_cookies = resp.cookies; if (Object.keys(request_cookies).length || Object.keys(resp.cookies || {}).length) { config.headers['cookie'] = cookies.write(extend(request_cookies, resp.cookies)); diff --git a/test/cookies_spec.js b/test/cookies_spec.js index 745f4c71f..9132bde6f 100644 --- a/test/cookies_spec.js +++ b/test/cookies_spec.js @@ -265,6 +265,41 @@ describe('cookies', function() { }) + describe('with multiple original request cookies', function() { + + var opts = { + follow_set_cookies: true, + follow_max: 4, + cookies: { 'xxx': 123, 'yyy': 456 } + }; + + it('request cookie is passed passed to redirects, and response cookies are added too', function(done) { + needle.get('localhost:' + testPort + '/0', opts, function(err, resp) { + requestCookies.should.eql([ + "xxx=123; yyy=456", + "xxx=123; yyy=456; wc=!'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=123", + "xxx=123; yyy=456; wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=123; fc=%20%3B%22%5C%2C; nc=12354342", + "xxx=123; yyy=456; wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=BAR; fc=%20%3B%22%5C%2C; nc=12354342", + "xxx=123; yyy=456; wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=BAR; fc=%20%3B%22%5C%2C; nc=12354342" + ]) + done(); + }); + }); + + it('response cookies are passed as well', function(done) { + needle.get('localhost:' + testPort + '/0', opts, function(err, resp) { + resp.cookies.should.have.property(WEIRD_COOKIE_NAME); + resp.cookies.should.have.property(BASE64_COOKIE_NAME); + resp.cookies.should.have.property(FORBIDDEN_COOKIE_NAME); + resp.cookies.should.have.property(NUMBER_COOKIE_NAME); + resp.cookies.should.have.property('FOO'); + resp.cookies.FOO.should.eql('BAR'); // should overwrite previous one + done(); + }); + }); + + }) + describe('without original request cookie', function() { var opts = { @@ -287,12 +322,12 @@ describe('cookies', function() { it('response cookies are passed as well', function(done) { needle.get('localhost:' + testPort + '/0', opts, function(err, resp) { - // resp.cookies.should.have.property(WEIRD_COOKIE_NAME); - // resp.cookies.should.have.property(BASE64_COOKIE_NAME); - // resp.cookies.should.have.property(FORBIDDEN_COOKIE_NAME); - // resp.cookies.should.have.property(NUMBER_COOKIE_NAME); - // resp.cookies.should.have.property('FOO'); - // resp.cookies.FOO.should.eql('BAR'); // should overwrite previous one + resp.cookies.should.have.property(WEIRD_COOKIE_NAME); + resp.cookies.should.have.property(BASE64_COOKIE_NAME); + resp.cookies.should.have.property(FORBIDDEN_COOKIE_NAME); + resp.cookies.should.have.property(NUMBER_COOKIE_NAME); + resp.cookies.should.have.property('FOO'); + resp.cookies.FOO.should.eql('BAR'); // should overwrite previous one done(); }); });