2 * Copyright GoInstant, Inc. and other contributors. All rights reserved.
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 var vows = require('vows');
23 var assert = require('assert');
24 var async = require('async');
26 // NOTE use require("tough-cookie") in your own code:
27 var tough = require('./lib/cookie');
28 var Cookie = tough.Cookie;
29 var CookieJar = tough.CookieJar;
32 function dateVows(table) {
34 var keys = Object.keys(table).forEach(function(date) {
35 var expect = table[date];
36 theVows[date] = function() {
37 var got = tough.parseDate(date) ? 'valid' : 'invalid';
38 assert.equal(got, expect ? 'valid' : 'invalid');
41 return { "date parsing": theVows };
44 function matchVows(func,table) {
46 table.forEach(function(item) {
50 var label = str+(expect?" matches ":" doesn't match ")+dom;
51 theVows[label] = function() {
52 assert.equal(func(str,dom),expect);
58 function defaultPathVows(table) {
60 table.forEach(function(item) {
63 var label = str+" gives "+expect;
64 theVows[label] = function() {
65 assert.equal(tough.defaultPath(str),expect);
71 var atNow = Date.now();
72 function at(offset) { return {now: new Date(atNow+offset)} }
74 vows.describe('Cookie Jar')
76 "all defined": function() {
83 "Wed, 09 Jun 2021 10:18:14 GMT": true,
84 "Wed, 09 Jun 2021 22:18:14 GMT": true,
85 "Tue, 18 Oct 2011 07:42:42.123 GMT": true,
86 "18 Oct 2011 07:42:42 GMT": true,
87 "8 Oct 2011 7:42:42 GMT": true,
88 "8 Oct 2011 7:2:42 GMT": false,
89 "Oct 18 2011 07:42:42 GMT": true,
90 "Tue Oct 18 2011 07:05:03 GMT+0000 (GMT)": true,
91 "09 Jun 2021 10:18:14 GMT": true,
92 "99 Jix 3038 48:86:72 ZMT": false,
93 '01 Jan 1970 00:00:00 GMT': true,
94 '01 Jan 1600 00:00:00 GMT': false, // before 1601
95 '01 Jan 1601 00:00:00 GMT': true,
96 '10 Feb 81 13:00:00 GMT': true, // implicit year
97 'Thu, 01 Jan 1970 00:00:010 GMT': true, // strange time, non-strict OK
98 'Thu, 17-Apr-2014 02:12:29 GMT': true, // dashes
99 'Thu, 17-Apr-2014 02:12:29 UTC': true, // dashes and UTC
103 "strict date parse of Thu, 01 Jan 1970 00:00:010 GMT": {
105 return tough.parseDate('Thu, 01 Jan 1970 00:00:010 GMT', true) ? true : false;
107 "invalid": function(date) {
108 assert.equal(date,false);
116 var c = new Cookie();
121 "validates": function(c) {
122 assert.ok(c.validate());
124 "to string": function(c) {
125 assert.equal(c.toString(), 'a=b');
128 "a cookie with spaces in the value": {
130 var c = new Cookie();
132 c.value = 'beta gamma';
135 "doesn't validate": function(c) {
136 assert.ok(!c.validate());
138 "to string": function(c) {
139 assert.equal(c.toString(), 'a="beta gamma"');
142 "with an empty value and HttpOnly": {
144 var c = new Cookie();
149 "to string": function(c) {
150 assert.equal(c.toString(), 'a=; HttpOnly');
155 var c = new Cookie();
158 c.setExpires("Oct 18 2011 07:05:03 GMT");
161 "validates": function(c) {
162 assert.ok(c.validate());
164 "to string": function(c) {
165 assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT');
167 "to short string": function(c) {
168 assert.equal(c.cookieString(), 'a=b');
173 var c = new Cookie();
176 c.setExpires("Oct 18 2011 07:05:03 GMT");
180 "validates": function(c) {
181 assert.ok(c.validate()); // mabe this one *shouldn't*?
183 "to string": function(c) {
184 assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Max-Age=12345');
187 "with a bunch of things": function() {
188 var c = new Cookie();
191 c.setExpires("Oct 18 2011 07:05:03 GMT");
193 c.domain = 'example.com';
197 c.extensions = ['MyExtension'];
198 assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Max-Age=12345; Domain=example.com; Path=/foo; Secure; HttpOnly; MyExtension');
200 "a host-only cookie": {
202 var c = new Cookie();
206 c.domain = 'shouldnt-stringify.example.com';
207 c.path = '/should-stringify';
210 "validates": function(c) {
211 assert.ok(c.validate());
213 "to string": function(c) {
214 assert.equal(c.toString(), 'a=b; Path=/should-stringify');
217 "minutes are '10'": {
219 var c = new Cookie();
222 c.expires = new Date(1284113410000);
225 "validates": function(c) {
226 assert.ok(c.validate());
228 "to string": function(c) {
229 var str = c.toString();
230 assert.notEqual(str, 'a=b; Expires=Fri, 010 Sep 2010 010:010:010 GMT');
231 assert.equal(str, 'a=b; Expires=Fri, 10 Sep 2010 10:10:10 GMT');
237 "TTL with max-age": function() {
238 var c = new Cookie();
240 assert.equal(c.TTL(), 123000);
241 assert.equal(c.expiryTime(new Date(9000000)), 9123000);
243 "TTL with zero max-age": function() {
244 var c = new Cookie();
245 c.key = 'a'; c.value = 'b';
246 c.maxAge = 0; // should be treated as "earliest representable"
247 assert.equal(c.TTL(), 0);
248 assert.equal(c.expiryTime(new Date(9000000)), -Infinity);
249 assert.ok(!c.validate()); // not valid, really: non-zero-digit *DIGIT
251 "TTL with negative max-age": function() {
252 var c = new Cookie();
253 c.key = 'a'; c.value = 'b';
254 c.maxAge = -1; // should be treated as "earliest representable"
255 assert.equal(c.TTL(), 0);
256 assert.equal(c.expiryTime(new Date(9000000)), -Infinity);
257 assert.ok(!c.validate()); // not valid, really: non-zero-digit *DIGIT
259 "TTL with max-age and expires": function() {
260 var c = new Cookie();
262 c.expires = new Date(Date.now()+9000);
263 assert.equal(c.TTL(), 123000);
264 assert.ok(c.isPersistent());
266 "TTL with expires": function() {
267 var c = new Cookie();
268 var now = Date.now();
269 c.expires = new Date(now+9000);
270 assert.equal(c.TTL(now), 9000);
271 assert.equal(c.expiryTime(), c.expires.getTime());
273 "TTL with old expires": function() {
274 var c = new Cookie();
275 c.setExpires('17 Oct 2010 00:00:00 GMT');
276 assert.ok(c.TTL() < 0);
277 assert.ok(c.isPersistent());
280 topic: function() { return new Cookie() },
281 "is Infinite-future": function(c) { assert.equal(c.TTL(), Infinity) },
282 "is a 'session' cookie": function(c) { assert.ok(!c.isPersistent()) },
288 return Cookie.parse('a=bcd',true) || null;
290 "parsed": function(c) { assert.ok(c) },
291 "key": function(c) { assert.equal(c.key, 'a') },
292 "value": function(c) { assert.equal(c.value, 'bcd') },
293 "no path": function(c) { assert.equal(c.path, null) },
294 "no domain": function(c) { assert.equal(c.domain, null) },
295 "no extensions": function(c) { assert.ok(!c.extensions) },
299 return Cookie.parse('a=bcd; Expires=Tue, 18 Oct 2011 07:05:03 GMT',true) || null;
301 "parsed": function(c) { assert.ok(c) },
302 "key": function(c) { assert.equal(c.key, 'a') },
303 "value": function(c) { assert.equal(c.value, 'bcd') },
304 "has expires": function(c) {
305 assert.ok(c.expires !== Infinity, 'expiry is infinite when it shouldn\'t be');
306 assert.equal(c.expires.getTime(), 1318921503000);
309 "with expiry and path": {
311 return Cookie.parse('abc="xyzzy!"; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Path=/aBc',true) || null;
313 "parsed": function(c) { assert.ok(c) },
314 "key": function(c) { assert.equal(c.key, 'abc') },
315 "value": function(c) { assert.equal(c.value, 'xyzzy!') },
316 "has expires": function(c) {
317 assert.ok(c.expires !== Infinity, 'expiry is infinite when it shouldn\'t be');
318 assert.equal(c.expires.getTime(), 1318921503000);
320 "has path": function(c) { assert.equal(c.path, '/aBc'); },
321 "no httponly or secure": function(c) {
322 assert.ok(!c.httpOnly);
323 assert.ok(!c.secure);
328 return Cookie.parse('abc="xyzzy!"; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Path=/aBc; Domain=example.com; Secure; HTTPOnly; Max-Age=1234; Foo=Bar; Baz', true) || null;
330 "parsed": function(c) { assert.ok(c) },
331 "key": function(c) { assert.equal(c.key, 'abc') },
332 "value": function(c) { assert.equal(c.value, 'xyzzy!') },
333 "has expires": function(c) {
334 assert.ok(c.expires !== Infinity, 'expiry is infinite when it shouldn\'t be');
335 assert.equal(c.expires.getTime(), 1318921503000);
337 "has path": function(c) { assert.equal(c.path, '/aBc'); },
338 "has domain": function(c) { assert.equal(c.domain, 'example.com'); },
339 "has httponly": function(c) { assert.equal(c.httpOnly, true); },
340 "has secure": function(c) { assert.equal(c.secure, true); },
341 "has max-age": function(c) { assert.equal(c.maxAge, 1234); },
342 "has extensions": function(c) {
343 assert.ok(c.extensions);
344 assert.equal(c.extensions[0], 'Foo=Bar');
345 assert.equal(c.extensions[1], 'Baz');
349 "strict": function() { assert.ok(!Cookie.parse("a=b; Expires=xyzzy", true)) },
350 "non-strict": function() {
351 var c = Cookie.parse("a=b; Expires=xyzzy");
353 assert.equal(c.expires, Infinity);
357 "strict": function() { assert.ok(!Cookie.parse("a=b; Max-Age=0", true)) },
358 "non-strict": function() {
359 var c = Cookie.parse("a=b; Max-Age=0");
361 assert.equal(c.maxAge, 0);
364 "negative max-age": {
365 "strict": function() { assert.ok(!Cookie.parse("a=b; Max-Age=-1", true)) },
366 "non-strict": function() {
367 var c = Cookie.parse("a=b; Max-Age=-1");
369 assert.equal(c.maxAge, -1);
373 "strict": function() { assert.ok(!Cookie.parse("a=b; domain=", true)) },
374 "non-strict": function() {
375 var c = Cookie.parse("a=b; domain=");
377 assert.equal(c.domain, null);
381 "strict": function() { assert.ok(!Cookie.parse("a=b; domain=.", true)) },
382 "non-strict": function() {
383 var c = Cookie.parse("a=b; domain=.");
385 assert.equal(c.domain, null);
388 "uppercase domain": {
389 "strict lowercases": function() {
390 var c = Cookie.parse("a=b; domain=EXAMPLE.COM");
392 assert.equal(c.domain, 'example.com');
394 "non-strict lowercases": function() {
395 var c = Cookie.parse("a=b; domain=EXAMPLE.COM");
397 assert.equal(c.domain, 'example.com');
400 "trailing dot in domain": {
402 return Cookie.parse("a=b; Domain=example.com.", true) || null;
404 "has the domain": function(c) { assert.equal(c.domain,"example.com.") },
405 "but doesn't validate": function(c) { assert.equal(c.validate(),false) },
408 "strict": function() { assert.ok(!Cookie.parse("a=b; path=", true)) },
409 "non-strict": function() {
410 var c = Cookie.parse("a=b; path=");
412 assert.equal(c.path, null);
416 "strict": function() { assert.ok(!Cookie.parse("a=b; path=xyzzy", true)) },
417 "non-strict": function() {
418 var c = Cookie.parse("a=b; path=xyzzy");
420 assert.equal(c.path, null);
423 "trailing semi-colons after path": {
430 "strict": function (t) {
431 assert.ok(!Cookie.parse(t[0], true));
432 assert.ok(!Cookie.parse(t[1], true));
434 "non-strict": function (t) {
435 var c1 = Cookie.parse(t[0]);
436 var c2 = Cookie.parse(t[1]);
439 assert.equal(c1.path, '/');
442 "secure-with-value": {
443 "strict": function() { assert.ok(!Cookie.parse("a=b; Secure=xyzzy", true)) },
444 "non-strict": function() {
445 var c = Cookie.parse("a=b; Secure=xyzzy");
447 assert.equal(c.secure, true);
450 "httponly-with-value": {
451 "strict": function() { assert.ok(!Cookie.parse("a=b; HttpOnly=xyzzy", true)) },
452 "non-strict": function() {
453 var c = Cookie.parse("a=b; HttpOnly=xyzzy");
455 assert.equal(c.httpOnly, true);
460 return Cookie.parse("\x08", true) || null;
462 "doesn't parse": function(c) { assert.equal(c,null) },
464 "public suffix domain": {
466 return Cookie.parse("a=b; domain=kyoto.jp", true) || null;
468 "parses fine": function(c) {
470 assert.equal(c.domain, 'kyoto.jp');
472 "but fails validation": function(c) {
474 assert.ok(!c.validate());
477 "Ironically, Google 'GAPS' cookie has very little whitespace": {
479 return Cookie.parse("GAPS=1:A1aaaaAaAAa1aaAaAaaAAAaaa1a11a:aaaAaAaAa-aaaA1-;Path=/;Expires=Thu, 17-Apr-2014 02:12:29 GMT;Secure;HttpOnly");
481 "parsed": function(c) { assert.ok(c) },
482 "key": function(c) { assert.equal(c.key, 'GAPS') },
483 "value": function(c) { assert.equal(c.value, '1:A1aaaaAaAAa1aaAaAaaAAAaaa1a11a:aaaAaAaAa-aaaA1-') },
484 "path": function(c) {
485 assert.notEqual(c.path, '/;Expires'); // BUG
486 assert.equal(c.path, '/');
488 "expires": function(c) {
489 assert.notEqual(c.expires, Infinity);
490 assert.equal(c.expires.getTime(), 1397700749000);
492 "secure": function(c) { assert.ok(c.secure) },
493 "httponly": function(c) { assert.ok(c.httpOnly) },
495 "lots of equal signs": {
497 return Cookie.parse("queryPref=b=c&d=e; Path=/f=g; Expires=Thu, 17 Apr 2014 02:12:29 GMT; HttpOnly");
499 "parsed": function(c) { assert.ok(c) },
500 "key": function(c) { assert.equal(c.key, 'queryPref') },
501 "value": function(c) { assert.equal(c.value, 'b=c&d=e') },
502 "path": function(c) {
503 assert.equal(c.path, '/f=g');
505 "expires": function(c) {
506 assert.notEqual(c.expires, Infinity);
507 assert.equal(c.expires.getTime(), 1397700749000);
509 "httponly": function(c) { assert.ok(c.httpOnly) },
514 "domain normalization": {
515 "simple": function() {
516 var c = new Cookie();
517 c.domain = "EXAMPLE.com";
518 assert.equal(c.canonicalizedDomain(), "example.com");
520 "extra dots": function() {
521 var c = new Cookie();
522 c.domain = ".EXAMPLE.com";
523 assert.equal(c.cdomain(), "example.com");
525 "weird trailing dot": function() {
526 var c = new Cookie();
527 c.domain = "EXAMPLE.ca.";
528 assert.equal(c.canonicalizedDomain(), "example.ca.");
530 "weird internal dots": function() {
531 var c = new Cookie();
532 c.domain = "EXAMPLE...ca.";
533 assert.equal(c.canonicalizedDomain(), "example...ca.");
536 var c = new Cookie();
537 c.domain = "δοκιμή.δοκιμή"; // "test.test" in greek
538 assert.equal(c.canonicalizedDomain(), "xn--jxalpdlp.xn--jxalpdlp");
543 "Domain Match":matchVows(tough.domainMatch, [
545 ["example.com", "example.com", true],
546 ["eXaMpLe.cOm", "ExAmPlE.CoM", true],
547 ["no.ca", "yes.ca", false],
548 ["wwwexample.com", "example.com", false],
549 ["www.example.com", "example.com", true],
550 ["example.com", "www.example.com", false],
551 ["www.subdom.example.com", "example.com", true],
552 ["www.subdom.example.com", "subdom.example.com", true],
553 ["example.com", "example.com.", false], // RFC6265 S4.1.2.3
554 ["192.168.0.1", "168.0.1", false], // S5.1.3 "The string is a host name"
555 [null, "example.com", null],
556 ["example.com", null, null],
558 [undefined, undefined, null],
562 "default-path": defaultPathVows([
566 ["/dir/file","/dir"],
571 "Path-Match": matchVows(tough.pathMatch, [
572 // request, cookie, match
576 ["/dir/","/dir/", true],
577 ["/dir/file","/dir/",true],
578 ["/dir/file","/dir",true],
579 ["/directory","/dir",false],
586 var now = Date.now();
587 cookies.push(Cookie.parse("a=0; Domain=example.com"));
588 cookies.push(Cookie.parse("b=1; Domain=www.example.com"));
589 cookies.push(Cookie.parse("c=2; Domain=example.com; Path=/pathA"));
590 cookies.push(Cookie.parse("d=3; Domain=www.example.com; Path=/pathA"));
591 cookies.push(Cookie.parse("e=4; Domain=example.com; Path=/pathA/pathB"));
592 cookies.push(Cookie.parse("f=5; Domain=www.example.com; Path=/pathA/pathB"));
594 // force a stable creation time consistent with the order above since
595 // some may have been created at now + 1ms.
596 var i = cookies.length;
597 cookies.forEach(function(cookie) {
598 cookie.creation = new Date(now - 100*(i--));
602 cookies = cookies.sort(function(){return Math.random()-0.5});
604 cookies = cookies.sort(tough.cookieCompare);
607 "got": function(cookies) {
608 assert.lengthOf(cookies, 6);
609 var names = cookies.map(function(c) {return c.key});
610 assert.deepEqual(names, ['e','f','c','d','a','b']);
616 "Setting a basic cookie": {
618 var cj = new CookieJar();
619 var c = Cookie.parse("a=b; Domain=example.com; Path=/");
620 assert.strictEqual(c.hostOnly, null);
621 assert.instanceOf(c.creation, Date);
622 assert.strictEqual(c.lastAccessed, null);
623 c.creation = new Date(Date.now()-10000);
624 cj.setCookie(c, 'http://example.com/index.html', this.callback);
626 "works": function(c) { assert.instanceOf(c,Cookie) }, // C is for Cookie, good enough for me
627 "gets timestamped": function(c) {
628 assert.ok(c.creation);
629 assert.ok(Date.now() - c.creation.getTime() < 5000); // recently stamped
630 assert.ok(c.lastAccessed);
631 assert.equal(c.creation, c.lastAccessed);
632 assert.equal(c.TTL(), Infinity);
633 assert.ok(!c.isPersistent());
636 "Setting a no-path cookie": {
638 var cj = new CookieJar();
639 var c = Cookie.parse("a=b; Domain=example.com");
640 assert.strictEqual(c.hostOnly, null);
641 assert.instanceOf(c.creation, Date);
642 assert.strictEqual(c.lastAccessed, null);
643 c.creation = new Date(Date.now()-10000);
644 cj.setCookie(c, 'http://example.com/index.html', this.callback);
646 "domain": function(c) { assert.equal(c.domain, 'example.com') },
647 "path is /": function(c) { assert.equal(c.path, '/') },
648 "path was derived": function(c) { assert.strictEqual(c.pathIsDefault, true) },
650 "Setting a cookie already marked as host-only": {
652 var cj = new CookieJar();
653 var c = Cookie.parse("a=b; Domain=example.com");
654 assert.strictEqual(c.hostOnly, null);
655 assert.instanceOf(c.creation, Date);
656 assert.strictEqual(c.lastAccessed, null);
657 c.creation = new Date(Date.now()-10000);
659 cj.setCookie(c, 'http://example.com/index.html', this.callback);
661 "domain": function(c) { assert.equal(c.domain, 'example.com') },
662 "still hostOnly": function(c) { assert.strictEqual(c.hostOnly, true) },
664 "Setting a session cookie": {
666 var cj = new CookieJar();
667 var c = Cookie.parse("a=b");
668 assert.strictEqual(c.path, null);
669 cj.setCookie(c, 'http://www.example.com/dir/index.html', this.callback);
671 "works": function(c) { assert.instanceOf(c,Cookie) },
672 "gets the domain": function(c) { assert.equal(c.domain, 'www.example.com') },
673 "gets the default path": function(c) { assert.equal(c.path, '/dir') },
674 "is 'hostOnly'": function(c) { assert.ok(c.hostOnly) },
676 "Setting wrong domain cookie": {
678 var cj = new CookieJar();
679 var c = Cookie.parse("a=b; Domain=fooxample.com; Path=/");
680 cj.setCookie(c, 'http://example.com/index.html', this.callback);
682 "fails": function(err,c) {
683 assert.ok(err.message.match(/domain/i));
687 "Setting sub-domain cookie": {
689 var cj = new CookieJar();
690 var c = Cookie.parse("a=b; Domain=www.example.com; Path=/");
691 cj.setCookie(c, 'http://example.com/index.html', this.callback);
693 "fails": function(err,c) {
694 assert.ok(err.message.match(/domain/i));
698 "Setting super-domain cookie": {
700 var cj = new CookieJar();
701 var c = Cookie.parse("a=b; Domain=example.com; Path=/");
702 cj.setCookie(c, 'http://www.app.example.com/index.html', this.callback);
704 "success": function(err,c) {
706 assert.equal(c.domain, 'example.com');
709 "Setting HttpOnly cookie over non-HTTP API": {
711 var cj = new CookieJar();
712 var c = Cookie.parse("a=b; Domain=example.com; Path=/; HttpOnly");
713 cj.setCookie(c, 'http://example.com/index.html', {http:false}, this.callback);
715 "fails": function(err,c) {
716 assert.match(err.message, /HttpOnly/i);
721 "Cookie Jar store eight cookies": {
723 var cj = new CookieJar();
724 var ex = 'http://example.com/index.html';
726 tasks.push(function(next) {
727 cj.setCookie('a=1; Domain=example.com; Path=/',ex,at(0),next);
729 tasks.push(function(next) {
730 cj.setCookie('b=2; Domain=example.com; Path=/; HttpOnly',ex,at(1000),next);
732 tasks.push(function(next) {
733 cj.setCookie('c=3; Domain=example.com; Path=/; Secure',ex,at(2000),next);
735 tasks.push(function(next) { // path
736 cj.setCookie('d=4; Domain=example.com; Path=/foo',ex,at(3000),next);
738 tasks.push(function(next) { // host only
739 cj.setCookie('e=5',ex,at(4000),next);
741 tasks.push(function(next) { // other domain
742 cj.setCookie('f=6; Domain=nodejs.org; Path=/','http://nodejs.org',at(5000),next);
744 tasks.push(function(next) { // expired
745 cj.setCookie('g=7; Domain=example.com; Path=/; Expires=Tue, 18 Oct 2011 00:00:00 GMT',ex,at(6000),next);
747 tasks.push(function(next) { // expired via Max-Age
748 cj.setCookie('h=8; Domain=example.com; Path=/; Max-Age=1',ex,next);
750 var cb = this.callback;
751 async.parallel(tasks, function(err,results){
752 setTimeout(function() {
754 }, 2000); // so that 'h=8' expires
757 "setup ok": function(err,cj,results) {
760 "then retrieving for http://nodejs.org": {
761 topic: function(cj,results) {
762 cj.getCookies('http://nodejs.org',this.callback);
764 "get a nodejs cookie": function(cookies) {
765 assert.lengthOf(cookies, 1);
766 var cookie = cookies[0];
767 assert.equal(cookie.domain, 'nodejs.org');
770 "then retrieving for https://example.com": {
771 topic: function(cj,results) {
772 cj.getCookies('https://example.com',{secure:true},this.callback);
774 "get a secure example cookie with others": function(cookies) {
775 var names = cookies.map(function(c) {return c.key});
776 assert.deepEqual(names, ['a','b','c','e']);
779 "then retrieving for https://example.com (missing options)": {
780 topic: function(cj,results) {
781 cj.getCookies('https://example.com',this.callback);
783 "get a secure example cookie with others": function(cookies) {
784 var names = cookies.map(function(c) {return c.key});
785 assert.deepEqual(names, ['a','b','c','e']);
788 "then retrieving for http://example.com": {
789 topic: function(cj,results) {
790 cj.getCookies('http://example.com',this.callback);
792 "get a bunch of cookies": function(cookies) {
793 var names = cookies.map(function(c) {return c.key});
794 assert.deepEqual(names, ['a','b','e']);
797 "then retrieving for http://EXAMPlE.com": {
798 topic: function(cj,results) {
799 cj.getCookies('http://EXAMPlE.com',this.callback);
801 "get a bunch of cookies": function(cookies) {
802 var names = cookies.map(function(c) {return c.key});
803 assert.deepEqual(names, ['a','b','e']);
806 "then retrieving for http://example.com, non-HTTP": {
807 topic: function(cj,results) {
808 cj.getCookies('http://example.com',{http:false},this.callback);
810 "get a bunch of cookies": function(cookies) {
811 var names = cookies.map(function(c) {return c.key});
812 assert.deepEqual(names, ['a','e']);
815 "then retrieving for http://example.com/foo/bar": {
816 topic: function(cj,results) {
817 cj.getCookies('http://example.com/foo/bar',this.callback);
819 "get a bunch of cookies": function(cookies) {
820 var names = cookies.map(function(c) {return c.key});
821 assert.deepEqual(names, ['d','a','b','e']);
824 "then retrieving for http://example.com as a string": {
825 topic: function(cj,results) {
826 cj.getCookieString('http://example.com',this.callback);
828 "get a single string": function(cookieHeader) {
829 assert.equal(cookieHeader, "a=1; b=2; e=5");
832 "then retrieving for http://example.com as a set-cookie header": {
833 topic: function(cj,results) {
834 cj.getSetCookieStrings('http://example.com',this.callback);
836 "get a single string": function(cookieHeaders) {
837 assert.lengthOf(cookieHeaders, 3);
838 assert.equal(cookieHeaders[0], "a=1; Domain=example.com; Path=/");
839 assert.equal(cookieHeaders[1], "b=2; Domain=example.com; Path=/; HttpOnly");
840 assert.equal(cookieHeaders[2], "e=5; Path=/");
843 "then retrieving for http://www.example.com/": {
844 topic: function(cj,results) {
845 cj.getCookies('http://www.example.com/foo/bar',this.callback);
847 "get a bunch of cookies": function(cookies) {
848 var names = cookies.map(function(c) {return c.key});
849 assert.deepEqual(names, ['d','a','b']); // note lack of 'e'
855 var cb = this.callback;
856 var cj = new CookieJar();
857 var ex = 'http://www.example.com/';
858 var sc = cj.setCookie;
860 var now = Date.now();
861 tasks.push(sc.bind(cj,'aaaa=xxxx',ex,at(0)));
862 tasks.push(sc.bind(cj,'aaaa=1111; Domain=www.example.com',ex,at(1000)));
863 tasks.push(sc.bind(cj,'aaaa=2222; Domain=example.com',ex,at(2000)));
864 tasks.push(sc.bind(cj,'aaaa=3333; Domain=www.example.com; Path=/pathA',ex,at(3000)));
865 async.series(tasks,function(err,results) {
866 results = results.filter(function(e) {return e !== undefined});
867 cb(err,{cj:cj, cookies:results, now:now});
870 "all got set": function(err,t) {
871 assert.lengthOf(t.cookies,4);
873 "then getting 'em back": {
876 cj.getCookies('http://www.example.com/pathA',this.callback);
878 "there's just three": function (err,cookies) {
879 var vals = cookies.map(function(c) {return c.value});
880 // may break with sorting; sorting should put 3333 first due to longest path:
881 assert.deepEqual(vals, ['3333','1111','2222']);
885 "CookieJar setCookie errors": {
886 "public-suffix domain": {
888 var cj = new CookieJar();
889 cj.setCookie('i=9; Domain=kyoto.jp; Path=/','kyoto.jp',this.callback);
891 "errors": function(err,cookie) {
894 assert.match(err.message, /public suffix/i);
899 var cj = new CookieJar();
900 cj.setCookie('j=10; Domain=google.com; Path=/','google.ca',this.callback);
902 "errors": function(err,cookie) {
905 assert.match(err.message, /not in this host's domain/i);
908 "old cookie is HttpOnly": {
910 var cb = this.callback;
911 var next = function (err,c) {
914 var cj = new CookieJar();
915 cj.setCookie('k=11; Domain=example.ca; Path=/; HttpOnly','http://example.ca',{http:true},next);
917 "initial cookie is set": function(err,cj) {
920 "but when trying to overwrite": {
921 topic: function(cj) {
922 var cb = this.callback;
923 cj.setCookie('k=12; Domain=example.ca; Path=/','http://example.ca',{http:false},function(err,c) {cb(null,err)});
925 "it's an error": function(err) {
928 "then, checking the original": {
929 topic: function(ignored,cj) {
930 assert.ok(cj instanceof CookieJar);
931 cj.getCookies('http://example.ca',{http:true},this.callback);
933 "cookie has original value": function(err,cookies) {
934 assert.equal(err,null);
935 assert.lengthOf(cookies, 1);
936 assert.equal(cookies[0].value,11);
947 var c = Cookie.parse('alpha=beta; Domain=example.com; Path=/foo; Expires=Tue, 19 Jan 2038 03:14:07 GMT; HttpOnly');
948 return JSON.stringify(c);
950 "gives a string": function(str) {
951 assert.equal(typeof str, "string");
953 "date is in ISO format": function(str) {
954 assert.match(str, /"expires":"2038-01-19T03:14:07\.000Z"/, 'expires is in ISO format');
959 var json = '{"key":"alpha","value":"beta","domain":"example.com","path":"/foo","expires":"2038-01-19T03:14:07.000Z","httpOnly":true,"lastAccessed":2000000000123}';
960 return Cookie.fromJSON(json);
962 "works": function(c) {
965 "key": function(c) { assert.equal(c.key, "alpha") },
966 "value": function(c) { assert.equal(c.value, "beta") },
967 "domain": function(c) { assert.equal(c.domain, "example.com") },
968 "path": function(c) { assert.equal(c.path, "/foo") },
969 "httpOnly": function(c) { assert.strictEqual(c.httpOnly, true) },
970 "secure": function(c) { assert.strictEqual(c.secure, false) },
971 "hostOnly": function(c) { assert.strictEqual(c.hostOnly, null) },
972 "expires is a date object": function(c) {
973 assert.equal(c.expires.getTime(), 2147483647000);
975 "lastAccessed is a date object": function(c) {
976 assert.equal(c.lastAccessed.getTime(), 2000000000123);
978 "creation defaulted": function(c) {
979 assert.ok(c.creation.getTime());
982 "null deserialization": {
984 return Cookie.fromJSON(null);
986 "is null": function(cookie) {
987 assert.equal(cookie,null);
991 "expiry deserialization": {
993 topic: Cookie.fromJSON.bind(null, '{"expires":"Infinity"}'),
994 "is infinite": function(c) {
995 assert.strictEqual(c.expires, "Infinity");
996 assert.equal(c.expires, Infinity);
1000 "maxAge serialization": {
1002 return function(toSet) {
1003 var c = new Cookie();
1004 c.key = 'foo'; c.value = 'bar';
1006 return JSON.stringify(c);
1010 topic: function(f) { return f(0) },
1011 "looks good": function(str) {
1012 assert.match(str, /"maxAge":0/);
1016 topic: function(f) { return f(Infinity) },
1017 "looks good": function(str) {
1018 assert.match(str, /"maxAge":"Infinity"/);
1022 topic: function(f) { return f(-Infinity) },
1023 "looks good": function(str) {
1024 assert.match(str, /"maxAge":"-Infinity"/);
1028 topic: function(f) { return f(null) },
1029 "looks good": function(str) {
1030 assert.match(str, /"maxAge":null/);
1034 "maxAge deserialization": {
1036 topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":123}'),
1037 "is the number": function(c) {
1038 assert.strictEqual(c.maxAge, 123);
1042 topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":null}'),
1043 "is null": function(c) {
1044 assert.strictEqual(c.maxAge, null);
1048 topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":-123}'),
1049 "is -123": function(c) {
1050 assert.strictEqual(c.maxAge, -123);
1054 topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":"Infinity"}'),
1055 "is inf-as-string": function(c) {
1056 assert.strictEqual(c.maxAge, "Infinity");
1060 topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":"-Infinity"}'),
1061 "is inf-as-string": function(c) {
1062 assert.strictEqual(c.maxAge, "-Infinity");
1070 topic: tough.permuteDomain.bind(null,'example.com'),
1071 "got the domain": function(list) {
1072 assert.deepEqual(list, ['example.com']);
1076 topic: tough.permuteDomain.bind(null,'foo.bar.example.com'),
1077 "got three things": function(list) {
1078 assert.deepEqual(list, ['example.com','bar.example.com','foo.bar.example.com']);
1082 topic: tough.permuteDomain.bind(null,'foo.bar.example.localduhmain'),
1083 "got three things": function(list) {
1084 assert.equal(list, null);
1090 topic: tough.permutePath.bind(null,'/'),
1091 "just slash": function(list) {
1092 assert.deepEqual(list,['/']);
1096 topic: tough.permutePath.bind(null,'/foo'),
1097 "two things": function(list) {
1098 assert.deepEqual(list,['/foo','/']);
1100 "path matching": function(list) {
1101 list.forEach(function(e) {
1102 assert.ok(tough.pathMatch('/foo',e));
1107 topic: tough.permutePath.bind(null,'/foo/bar'),
1108 "four things": function(list) {
1109 assert.deepEqual(list,['/foo/bar','/foo','/']);
1111 "path matching": function(list) {
1112 list.forEach(function(e) {
1113 assert.ok(tough.pathMatch('/foo/bar',e));
1118 topic: tough.permutePath.bind(null,'/foo/bar/'),
1119 "three things": function(list) {
1120 assert.deepEqual(list,['/foo/bar','/foo','/']);
1122 "path matching": function(list) {
1123 list.forEach(function(e) {
1124 assert.ok(tough.pathMatch('/foo/bar/',e));
1133 var cj = new CookieJar();
1134 cj.setCookie('hello=world; path=/some/path/', 'http://domain/some/path/file', function(err,cookie) {
1135 this.callback(err,{cj:cj, cookie:cookie});
1138 "stored a cookie": function(t) {
1139 assert.ok(t.cookie);
1141 "cookie's path was modified to remove unnecessary slash": function(t) {
1142 assert.equal(t.cookie.path, '/some/path');
1144 "getting it back": {
1145 topic: function(t) {
1146 t.cj.getCookies('http://domain/some/path/file', function(err,cookies) {
1147 this.callback(err, {cj:t.cj, cookies:cookies||[]});
1150 "got one cookie": function(t) {
1151 assert.lengthOf(t.cookies, 1);
1153 "it's the right one": function(t) {
1154 var c = t.cookies[0];
1155 assert.equal(c.key, 'hello');
1156 assert.equal(c.value, 'world');
1164 var cb = this.callback;
1165 var cj = new CookieJar();
1166 cj.setCookie('near=expiry; Domain=example.com; Path=/; Max-Age=1','http://www.example.com',at(-1), function(err,cookie) {
1168 cb(err, {cj:cj, cookie:cookie});
1171 "set the cookie": function(t) {
1172 assert.ok(t.cookie, "didn't set?!");
1173 assert.equal(t.cookie.key, 'near');
1175 "then, retrieving": {
1176 topic: function(t) {
1177 var cb = this.callback;
1178 setTimeout(function() {
1179 t.cj.getCookies('http://www.example.com', {http:true, expire:false}, function(err,cookies) {
1180 t.cookies = cookies;
1185 "got the cookie": function(t) {
1186 assert.lengthOf(t.cookies, 1);
1187 assert.equal(t.cookies[0].key, 'near');
1193 "trailing semi-colon set into cj": {
1194 topic: function () {
1195 var cb = this.callback;
1196 var cj = new CookieJar();
1197 var ex = 'http://www.example.com';
1199 tasks.push(function(next) {
1200 cj.setCookie('broken_path=testme; path=/;',ex,at(-1),next);
1202 tasks.push(function(next) {
1203 cj.setCookie('b=2; Path=/;;;;',ex,at(-1),next);
1205 async.parallel(tasks, function (err, cookies) {
1212 "check number of cookies": function (t) {
1213 assert.lengthOf(t.cookies, 2, "didn't set");
1215 "check *broken_path* was set properly": function (t) {
1216 assert.equal(t.cookies[0].key, "broken_path");
1217 assert.equal(t.cookies[0].value, "testme");
1218 assert.equal(t.cookies[0].path, "/");
1220 "check *b* was set properly": function (t) {
1221 assert.equal(t.cookies[1].key, "b");
1222 assert.equal(t.cookies[1].value, "2");
1223 assert.equal(t.cookies[1].path, "/");
1225 "retrieve the cookie": {
1226 topic: function (t) {
1227 var cb = this.callback;
1228 t.cj.getCookies('http://www.example.com', {}, function (err, cookies) {
1229 t.cookies = cookies;
1233 "get the cookie": function(t) {
1234 assert.lengthOf(t.cookies, 2);
1235 assert.equal(t.cookies[0].key, 'broken_path');
1236 assert.equal(t.cookies[0].value, 'testme');
1237 assert.equal(t.cookies[1].key, "b");
1238 assert.equal(t.cookies[1].value, "2");
1239 assert.equal(t.cookies[1].path, "/");
1246 topic: function () {
1253 'check for key property': function (c) {
1255 assert.equal(c.key, 'test');
1257 'check for value property': function (c) {
1258 assert.equal(c.value, 'b');
1260 'check for maxAge': function (c) {
1261 assert.equal(c.maxAge, 60);
1263 'check for default values for unspecified properties': function (c) {
1264 assert.equal(c.expires, "Infinity");
1265 assert.equal(c.secure, false);
1266 assert.equal(c.httpOnly, false);
1271 "allPaths option": {
1273 var cj = new CookieJar();
1275 tasks.push(cj.setCookie.bind(cj, 'nopath_dom=qq; Path=/; Domain=example.com', 'http://example.com', {}));
1276 tasks.push(cj.setCookie.bind(cj, 'path_dom=qq; Path=/foo; Domain=example.com', 'http://example.com', {}));
1277 tasks.push(cj.setCookie.bind(cj, 'nopath_host=qq; Path=/', 'http://www.example.com', {}));
1278 tasks.push(cj.setCookie.bind(cj, 'path_host=qq; Path=/foo', 'http://www.example.com', {}));
1279 tasks.push(cj.setCookie.bind(cj, 'other=qq; Path=/', 'http://other.example.com/', {}));
1280 tasks.push(cj.setCookie.bind(cj, 'other2=qq; Path=/foo', 'http://other.example.com/foo', {}));
1281 var cb = this.callback;
1282 async.parallel(tasks, function(err,results) {
1283 cb(err, {cj:cj, cookies: results});
1286 "all set": function(t) {
1287 assert.equal(t.cookies.length, 6);
1288 assert.ok(t.cookies.every(function(c) { return !!c }));
1290 "getting without allPaths": {
1291 topic: function(t) {
1292 var cb = this.callback;
1294 cj.getCookies('http://www.example.com/', {}, function(err,cookies) {
1295 cb(err, {cj:cj, cookies:cookies});
1298 "found just two cookies": function(t) {
1299 assert.equal(t.cookies.length, 2);
1301 "all are path=/": function(t) {
1302 assert.ok(t.cookies.every(function(c) { return c.path === '/' }));
1304 "no 'other' cookies": function(t) {
1305 assert.ok(!t.cookies.some(function(c) { return (/^other/).test(c.name) }));
1308 "getting without allPaths for /foo": {
1309 topic: function(t) {
1310 var cb = this.callback;
1312 cj.getCookies('http://www.example.com/foo', {}, function(err,cookies) {
1313 cb(err, {cj:cj, cookies:cookies});
1316 "found four cookies": function(t) {
1317 assert.equal(t.cookies.length, 4);
1319 "no 'other' cookies": function(t) {
1320 assert.ok(!t.cookies.some(function(c) { return (/^other/).test(c.name) }));
1323 "getting with allPaths:true": {
1324 topic: function(t) {
1325 var cb = this.callback;
1327 cj.getCookies('http://www.example.com/', {allPaths:true}, function(err,cookies) {
1328 cb(err, {cj:cj, cookies:cookies});
1331 "found four cookies": function(t) {
1332 assert.equal(t.cookies.length, 4);
1334 "no 'other' cookies": function(t) {
1335 assert.ok(!t.cookies.some(function(c) { return (/^other/).test(c.name) }));