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