cookies_spec.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. var needle = require('../'),
  2. cookies = require('../lib/cookies'),
  3. sinon = require('sinon'),
  4. http = require('http'),
  5. should = require('should');
  6. var WEIRD_COOKIE_NAME = 'wc',
  7. BASE64_COOKIE_NAME = 'bc',
  8. FORBIDDEN_COOKIE_NAME = 'fc',
  9. NUMBER_COOKIE_NAME = 'nc';
  10. var WEIRD_COOKIE_VALUE = '!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~',
  11. BASE64_COOKIE_VALUE = 'Y29va2llCg==',
  12. FORBIDDEN_COOKIE_VALUE = ' ;"\\,',
  13. NUMBER_COOKIE_VALUE = 12354342;
  14. var NO_COOKIES_TEST_PORT = 11112,
  15. ALL_COOKIES_TEST_PORT = 11113;
  16. describe('cookies', function() {
  17. var setCookieHeader, headers, server, opts;
  18. function decode(str) {
  19. return decodeURIComponent(str);
  20. }
  21. function encode(str) {
  22. str = str.toString().replace(/[\x00-\x1F\x7F]/g, encodeURIComponent);
  23. return str.replace(/[\s\"\,;\\%]/g, encodeURIComponent);
  24. }
  25. before(function() {
  26. setCookieHeader = [
  27. WEIRD_COOKIE_NAME + '=' + encode(WEIRD_COOKIE_VALUE) + ';',
  28. BASE64_COOKIE_NAME + '=' + encode(BASE64_COOKIE_VALUE) + ';',
  29. FORBIDDEN_COOKIE_NAME + '=' + encode(FORBIDDEN_COOKIE_VALUE) + ';',
  30. NUMBER_COOKIE_NAME + '=' + encode(NUMBER_COOKIE_VALUE) + ';'
  31. ];
  32. });
  33. before(function(done) {
  34. serverAllCookies = http.createServer(function(req, res) {
  35. res.setHeader('Content-Type', 'text/html');
  36. res.setHeader('Set-Cookie', setCookieHeader);
  37. res.end('200');
  38. }).listen(ALL_COOKIES_TEST_PORT, done);
  39. });
  40. after(function(done) {
  41. serverAllCookies.close(done);
  42. });
  43. describe('with default options', function() {
  44. it('no cookie header is set on request', function(done) {
  45. needle.get(
  46. 'localhost:' + ALL_COOKIES_TEST_PORT, function(err, response) {
  47. should.not.exist(response.req._headers.cookie);
  48. done();
  49. });
  50. });
  51. });
  52. describe('if response does not contain cookies', function() {
  53. before(function(done) {
  54. serverNoCookies = http.createServer(function(req, res) {
  55. res.setHeader('Content-Type', 'text/html');
  56. res.end('200');
  57. }).listen(NO_COOKIES_TEST_PORT, done);
  58. });
  59. it('response.cookies is undefined', function(done) {
  60. needle.get(
  61. 'localhost:' + NO_COOKIES_TEST_PORT, function(error, response) {
  62. should.not.exist(response.cookies);
  63. done();
  64. });
  65. });
  66. after(function(done) {
  67. serverNoCookies.close(done);
  68. });
  69. });
  70. describe('if response contains cookies', function() {
  71. it('puts them on resp.cookies', function(done) {
  72. needle.get(
  73. 'localhost:' + ALL_COOKIES_TEST_PORT, function(error, response) {
  74. response.should.have.property('cookies');
  75. done();
  76. });
  77. });
  78. it('parses them as a object', function(done) {
  79. needle.get(
  80. 'localhost:' + ALL_COOKIES_TEST_PORT, function(error, response) {
  81. response.cookies.should.be.an.instanceOf(Object)
  82. .and.have.property(WEIRD_COOKIE_NAME);
  83. response.cookies.should.have.property(BASE64_COOKIE_NAME);
  84. response.cookies.should.have.property(FORBIDDEN_COOKIE_NAME);
  85. response.cookies.should.have.property(NUMBER_COOKIE_NAME);
  86. done();
  87. });
  88. });
  89. it('must decode it', function(done) {
  90. needle.get(
  91. 'localhost:' + ALL_COOKIES_TEST_PORT, function(error, response) {
  92. response.cookies.wc.should.be.eql(WEIRD_COOKIE_VALUE);
  93. response.cookies.bc.should.be.eql(BASE64_COOKIE_VALUE);
  94. response.cookies.fc.should.be.eql(FORBIDDEN_COOKIE_VALUE);
  95. response.cookies.nc.should.be.eql(NUMBER_COOKIE_VALUE.toString());
  96. done();
  97. });
  98. });
  99. describe('when a cookie value is invalid', function() {
  100. before(function() {
  101. setCookieHeader = [
  102. 'geo_city=%D1%E0%ED%EA%F2-%CF%E5%F2%E5%F0%E1%F3%F0%E3'
  103. ];
  104. })
  105. it('doesnt blow up', function(done) {
  106. needle.get('localhost:' + ALL_COOKIES_TEST_PORT, function(error, response) {
  107. should.not.exist(error)
  108. var whatever = 'efbfbdefbfbdefbfbdefbfbdefbfbd2defbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbd';
  109. Buffer.from(response.cookies.geo_city).toString('hex').should.eql(whatever)
  110. done();
  111. });
  112. })
  113. })
  114. describe('and response is a redirect', function() {
  115. var redirectServer, testPort = 22222;
  116. var requestCookies = [];
  117. var responseCookies = [
  118. [ // first req
  119. WEIRD_COOKIE_NAME + '=' + encode(WEIRD_COOKIE_VALUE) + ';',
  120. BASE64_COOKIE_NAME + '=' + encode(BASE64_COOKIE_VALUE) + ';',
  121. 'FOO=123;'
  122. ], [ // second req
  123. FORBIDDEN_COOKIE_NAME + '=' + encode(FORBIDDEN_COOKIE_VALUE) + ';',
  124. NUMBER_COOKIE_NAME + '=' + encode(NUMBER_COOKIE_VALUE) + ';'
  125. ], [ // third red
  126. 'FOO=BAR;'
  127. ]
  128. ]
  129. before(function(done) {
  130. redirectServer = http.createServer(function(req, res) {
  131. var number = parseInt(req.url.replace('/', ''));
  132. var nextUrl = 'http://' + 'localhost:' + testPort + '/' + (number + 1);
  133. if (number == 0) requestCookies = []; // reset
  134. requestCookies.push(req.headers['cookie']);
  135. if (responseCookies[number]) { // we should send cookies for this request
  136. res.statusCode = 302;
  137. res.setHeader('Set-Cookie', responseCookies[number]);
  138. res.setHeader('Location', nextUrl);
  139. } else if (number == 3) {
  140. res.statusCode = 302; // redirect but without cookies
  141. res.setHeader('Location', nextUrl);
  142. }
  143. res.end('OK');
  144. }).listen(22222, done);
  145. });
  146. after(function(done) {
  147. redirectServer.close(done);
  148. })
  149. describe('and follow_set_cookies is false', function() {
  150. describe('with original request cookie', function() {
  151. var opts = {
  152. follow_set_cookies: false,
  153. follow_max: 4,
  154. cookies: { 'xxx': 123 }
  155. };
  156. it('request cookie is not passed to redirects', function(done) {
  157. needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
  158. requestCookies.should.eql(["xxx=123", undefined, undefined, undefined, undefined])
  159. done();
  160. });
  161. });
  162. it('response cookies are not passed either', function(done) {
  163. needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
  164. should.not.exist(resp.cookies);
  165. done();
  166. });
  167. });
  168. })
  169. describe('without original request cookie', function() {
  170. var opts = {
  171. follow_set_cookies: false,
  172. follow_max: 4,
  173. };
  174. it('no request cookies are sent', function(done) {
  175. needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
  176. requestCookies.should.eql([undefined, undefined, undefined, undefined, undefined])
  177. done();
  178. });
  179. });
  180. it('response cookies are not passed either', function(done) {
  181. needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
  182. should.not.exist(resp.cookies);
  183. done();
  184. });
  185. });
  186. })
  187. });
  188. describe('and follow_set_cookies is true', function() {
  189. describe('with original request cookie', function() {
  190. var opts = {
  191. follow_set_cookies: true,
  192. follow_max: 4,
  193. cookies: { 'xxx': 123 }
  194. };
  195. it('request cookie is passed passed to redirects, and response cookies are added too', function(done) {
  196. needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
  197. requestCookies.should.eql([
  198. "xxx=123",
  199. "xxx=123; wc=!'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=123",
  200. "xxx=123; wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=123; fc=%20%3B%22%5C%2C; nc=12354342",
  201. "xxx=123; wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=BAR; fc=%20%3B%22%5C%2C; nc=12354342",
  202. "xxx=123; wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=BAR; fc=%20%3B%22%5C%2C; nc=12354342"
  203. ])
  204. done();
  205. });
  206. });
  207. it('response cookies are passed as well', function(done) {
  208. needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
  209. resp.cookies.should.have.property(WEIRD_COOKIE_NAME);
  210. resp.cookies.should.have.property(BASE64_COOKIE_NAME);
  211. resp.cookies.should.have.property(FORBIDDEN_COOKIE_NAME);
  212. resp.cookies.should.have.property(NUMBER_COOKIE_NAME);
  213. resp.cookies.should.have.property('FOO');
  214. resp.cookies.FOO.should.eql('BAR'); // should overwrite previous one
  215. done();
  216. });
  217. });
  218. })
  219. describe('without original request cookie', function() {
  220. var opts = {
  221. follow_set_cookies: true,
  222. follow_max: 4,
  223. };
  224. it('response cookies are passed to redirects', function(done) {
  225. needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
  226. requestCookies.should.eql([
  227. undefined,
  228. "wc=!'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=123",
  229. "wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=123; fc=%20%3B%22%5C%2C; nc=12354342",
  230. "wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=BAR; fc=%20%3B%22%5C%2C; nc=12354342",
  231. "wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=BAR; fc=%20%3B%22%5C%2C; nc=12354342"
  232. ])
  233. done();
  234. });
  235. });
  236. it('response cookies are passed as well', function(done) {
  237. needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
  238. // resp.cookies.should.have.property(WEIRD_COOKIE_NAME);
  239. // resp.cookies.should.have.property(BASE64_COOKIE_NAME);
  240. // resp.cookies.should.have.property(FORBIDDEN_COOKIE_NAME);
  241. // resp.cookies.should.have.property(NUMBER_COOKIE_NAME);
  242. // resp.cookies.should.have.property('FOO');
  243. // resp.cookies.FOO.should.eql('BAR'); // should overwrite previous one
  244. done();
  245. });
  246. });
  247. })
  248. });
  249. });
  250. describe('with parse_cookies = false', function() {
  251. it('does not parse them', function(done) {
  252. needle.get(
  253. 'localhost:' + ALL_COOKIES_TEST_PORT, { parse_cookies: false }, function(error, response) {
  254. should.not.exist(response.cookies);
  255. done();
  256. });
  257. });
  258. });
  259. });
  260. describe('if request contains cookie header', function() {
  261. var opts = {
  262. cookies: {}
  263. };
  264. before(function() {
  265. opts.cookies[WEIRD_COOKIE_NAME] = WEIRD_COOKIE_VALUE;
  266. opts.cookies[BASE64_COOKIE_NAME] = BASE64_COOKIE_VALUE;
  267. opts.cookies[FORBIDDEN_COOKIE_NAME] = FORBIDDEN_COOKIE_VALUE;
  268. opts.cookies[NUMBER_COOKIE_NAME] = NUMBER_COOKIE_VALUE;
  269. });
  270. it('must be a valid cookie string', function(done) {
  271. var COOKIE_PAIR = /^([^=\s]+)\s*=\s*("?)\s*(.*)\s*\2\s*$/;
  272. var full_header = [
  273. WEIRD_COOKIE_NAME + '=' + WEIRD_COOKIE_VALUE,
  274. BASE64_COOKIE_NAME + '=' + BASE64_COOKIE_VALUE,
  275. FORBIDDEN_COOKIE_NAME + '=' + encode(FORBIDDEN_COOKIE_VALUE),
  276. NUMBER_COOKIE_NAME + '=' + NUMBER_COOKIE_VALUE
  277. ].join('; ')
  278. needle.get('localhost:' + ALL_COOKIES_TEST_PORT, opts, function(error, response) {
  279. var cookieString = response.req._headers.cookie;
  280. cookieString.should.be.type('string');
  281. cookieString.split(/\s*;\s*/).forEach(function(pair) {
  282. COOKIE_PAIR.test(pair).should.be.exactly(true);
  283. });
  284. cookieString.should.be.exactly(full_header);
  285. done();
  286. });
  287. });
  288. it('dont have to encode allowed characters', function(done) {
  289. var COOKIE_PAIR = /^([^=\s]+)\s*=\s*("?)\s*(.*)\s*\2\s*$/,
  290. KEY_INDEX = 1,
  291. VALUE_INEX = 3;
  292. needle.get('localhost:' + ALL_COOKIES_TEST_PORT, opts, function(error, response) {
  293. var cookieObj = {},
  294. cookieString = response.req._headers.cookie;
  295. cookieString.split(/\s*;\s*/).forEach(function(str) {
  296. var pair = COOKIE_PAIR.exec(str);
  297. cookieObj[pair[KEY_INDEX]] = pair[VALUE_INEX];
  298. });
  299. cookieObj[WEIRD_COOKIE_NAME].should.be.exactly(WEIRD_COOKIE_VALUE);
  300. cookieObj[BASE64_COOKIE_NAME].should.be.exactly(BASE64_COOKIE_VALUE);
  301. done();
  302. });
  303. });
  304. it('must encode forbidden characters', function(done) {
  305. var COOKIE_PAIR = /^([^=\s]+)\s*=\s*("?)\s*(.*)\s*\2\s*$/,
  306. KEY_INDEX = 1,
  307. VALUE_INEX = 3;
  308. needle.get('localhost:' + ALL_COOKIES_TEST_PORT, opts, function(error, response) {
  309. var cookieObj = {},
  310. cookieString = response.req._headers.cookie;
  311. cookieString.split(/\s*;\s*/).forEach(function(str) {
  312. var pair = COOKIE_PAIR.exec(str);
  313. cookieObj[pair[KEY_INDEX]] = pair[VALUE_INEX];
  314. });
  315. cookieObj[FORBIDDEN_COOKIE_NAME].should.not.be.eql(
  316. FORBIDDEN_COOKIE_VALUE);
  317. cookieObj[FORBIDDEN_COOKIE_NAME].should.be.exactly(
  318. encode(FORBIDDEN_COOKIE_VALUE));
  319. cookieObj[FORBIDDEN_COOKIE_NAME].should.be.exactly(
  320. encodeURIComponent(FORBIDDEN_COOKIE_VALUE));
  321. done();
  322. });
  323. });
  324. });
  325. });