tough-cookie 0.9.11 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.jshintrc ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ // Settings
3
+ "passfail" : false, // Stop on first error.
4
+ "maxerr" : 100, // Maximum error before stopping.
5
+
6
+
7
+ // Predefined globals whom JSHint will ignore.
8
+ "browser" : false, // Standard browser globals e.g. `window`, `document`.
9
+ "node" : true,
10
+ "rhino" : false,
11
+ "couch" : false,
12
+ "wsh" : false, // Windows Scripting Host.
13
+
14
+ "jquery" : false,
15
+ "prototypejs" : false,
16
+ "mootools" : false,
17
+ "dojo" : false,
18
+
19
+ "predef" : [ // Custom globals.
20
+ "escape", "unescape" // aren't picked up by node flag above :/
21
+ ],
22
+
23
+
24
+ // Development.
25
+ "debug" : false, // Allow debugger statements e.g. browser breakpoints.
26
+ "devel" : false, // Allow developments statements e.g. `console.log();`.
27
+
28
+
29
+ // EcmaScript 5.
30
+ "es5" : true, // Allow EcmaScript 5 syntax.
31
+ "esnext" : true, // Allow ES "next" syntax, specifically 'const' (V8 has this)
32
+ "strict" : false, // Require `use strict` pragma in every file.
33
+ "globalstrict" : false, // Allow global "use strict" (also enables 'strict').
34
+
35
+
36
+ // The Good Parts.
37
+ "asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons).
38
+ "laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
39
+ "bitwise" : true, // Prohibit bitwise operators (&, |, ^, etc.).
40
+ "boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
41
+ "curly" : false, // Require {} for every new block or scope.
42
+ "eqeqeq" : false, // Require triple equals i.e. `===`.
43
+ "eqnull" : true, // Tolerate use of `== null`.
44
+ "evil" : false, // Tolerate use of `eval`.
45
+ "expr" : true, // Tolerate `ExpressionStatement` as Programs.
46
+ "forin" : false, // Tolerate `for in` loops without `hasOwnPrototype`.
47
+ "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
48
+ "latedef" : false, // Prohibit variable use before definition.
49
+ "loopfunc" : false, // Allow functions to be defined within loops.
50
+ "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`.
51
+ "regexp" : true, // Prohibit `.` and `[^...]` in regular expressions.
52
+ "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`.
53
+ "scripturl" : true, // Tolerate script-targeted URLs.
54
+ "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`.
55
+ "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`.
56
+ "undef" : true, // Require all non-global variables be declared before they are used.
57
+
58
+
59
+ // Personal styling prefrences.
60
+ "lastsemic" : true, // Prohibit missing semicolon for the last statement in a one-line block
61
+ "newcap" : false, // Require capitalization of all constructor functions e.g. `new F()`.
62
+ "noempty" : true, // Prohipit use of empty blocks.
63
+ "nonew" : true, // Prohibit use of constructors for side-effects.
64
+ "nomen" : false, // Prohibit use of initial or trailing underbars in names.
65
+ "onevar" : false, // Allow only one `var` statement per function.
66
+ "onecase" : false, // Allow switches with one case statement
67
+ "plusplus" : false, // Prohibit use of `++` & `--`.
68
+ "proto" : false, // Allow use of __proto__
69
+ "sub" : true, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
70
+ "trailing" : true, // Prohibit trailing whitespaces.
71
+ "white" : false // Check against strict whitespace and indentation rules.
72
+ }
package/README.md CHANGED
@@ -272,6 +272,7 @@ The `options` object can be omitted and can have the following properties:
272
272
  * _secure_ - boolean - autodetect from url - indicates if this is a "Secure" API. If the currentUrl starts with `https:` or `wss:` then this is defaulted to `true`, otherwise `false`.
273
273
  * _now_ - Date - default `new Date()` - what to use for the creation/access time of cookies
274
274
  * _expire_ - boolean - default `true` - perform expiry-time checking of cookies and asynchronously remove expired cookies from the store. Using `false` will return expired cookies and **not** remove them from the store (which is useful for replaying Set-Cookie headers, potentially).
275
+ * _allPaths_ - boolean - default `false` - if `true`, do not scope cookies by path. The default uses RFC-compliant path scoping. **Note**: may not be supported by the CookieStore `fetchCookies` function (the default MemoryCookieStore supports it).
275
276
 
276
277
  The `.lastAccessed` property of the returned cookies will have been updated.
277
278
 
@@ -309,6 +310,8 @@ If no cookies are found, the callback MUST be passed an empty array.
309
310
 
310
311
  The resulting list will be checked for applicability to the current request according to the RFC (domain-match, path-match, http-only-flag, secure-flag, expiry, etc.), so it's OK to use an optimistic search algorithm when implementing this method. However, the search algorithm used SHOULD try to find cookies that `domainMatch()` the domain and `pathMatch()` the path in order to limit the amount of checking that needs to be done.
311
312
 
313
+ As of version 0.9.12, the `allPaths` option to `cookiejar.getCookies()` above will cause the path here to be `null`. If the path is `null`, path-matching MUST NOT be performed (i.e. domain-matching only).
314
+
312
315
  store.putCookie(cookie, cb(err))
313
316
  --------------------------------
314
317
 
package/lib/cookie.js CHANGED
@@ -19,13 +19,14 @@
19
19
  * IN THE SOFTWARE.
20
20
  */
21
21
 
22
+ /*jshint regexp:false */
22
23
  var net = require('net');
23
24
  var urlParse = require('url').parse;
24
25
  var pubsuffix = require('./pubsuffix');
25
26
 
26
27
  var punycode;
27
28
  try {
28
- punycode = require('punycode')
29
+ punycode = require('punycode');
29
30
  } catch(e) {
30
31
  console.warn("cookie: can't load punycode; won't use punycode for domain normalization");
31
32
  }
@@ -188,13 +189,13 @@ function parseDate(str,strict) {
188
189
  }
189
190
 
190
191
  return date;
191
- };
192
+ }
192
193
 
193
194
  function formatDate(date) {
194
- var d = date.getUTCDate(); d = d > 10 ? d : '0'+d;
195
- var h = date.getUTCHours(); h = h > 10 ? h : '0'+h;
196
- var m = date.getUTCMinutes(); m = m > 10 ? m : '0'+m;
197
- var s = date.getUTCSeconds(); s = s > 10 ? s : '0'+s;
195
+ var d = date.getUTCDate(); d = d >= 10 ? d : '0'+d;
196
+ var h = date.getUTCHours(); h = h >= 10 ? h : '0'+h;
197
+ var m = date.getUTCMinutes(); m = m >= 10 ? m : '0'+m;
198
+ var s = date.getUTCSeconds(); s = s >= 10 ? s : '0'+s;
198
199
  return NUM_TO_DAY[date.getUTCDay()] + ', ' +
199
200
  d+' '+ NUM_TO_MONTH[date.getUTCMonth()] +' '+ date.getUTCFullYear() +' '+
200
201
  h+':'+m+':'+s+' GMT';
@@ -214,6 +215,7 @@ function canonicalDomain(str) {
214
215
 
215
216
  // S5.1.3 Domain Matching
216
217
  function domainMatch(str, domStr, canonicalize) {
218
+ if (str == null || domStr == null) return null;
217
219
  if (canonicalize !== false) {
218
220
  str = canonicalDomain(str);
219
221
  domStr = canonicalDomain(domStr);
@@ -266,7 +268,7 @@ function defaultPath(path) {
266
268
 
267
269
  var rightSlash = path.lastIndexOf("/");
268
270
  if (rightSlash === 0) return "/";
269
-
271
+
270
272
  // "4. Output the characters of the uri-path from the first character up to,
271
273
  // but not including, the right-most %x2F ("/")."
272
274
  return path.slice(0, rightSlash);
@@ -300,7 +302,7 @@ function pathMatch(reqPath,cookiePath) {
300
302
 
301
303
  function parse(str, strict) {
302
304
  str = str.trim();
303
-
305
+
304
306
  // S4.1.1 Trailing semi-colons are not part of the specification.
305
307
  // If we are not in strict mode we remove the trailing semi-colons.
306
308
  var semiColonCheck = TRAILING_SEMICOLON.exec(str);
@@ -333,7 +335,7 @@ function parse(str, strict) {
333
335
  // steps."
334
336
  if (unparsed.length === 0) return c;
335
337
 
336
- /*
338
+ /*
337
339
  * S5.2 says that when looping over the items "[p]rocess the attribute-name
338
340
  * and attribute-value according to the requirements in the following
339
341
  * subsections" for every item. Plus, for many of the individual attributes
@@ -347,30 +349,38 @@ function parse(str, strict) {
347
349
 
348
350
  if (strict && !EXTENSION_AV.test(av)) return;
349
351
 
350
- var av_parts = av.split('=',2);
351
- var av_key = av_parts[0].trim().toLowerCase();
352
- var av_value = av_parts[1];
352
+ var av_sep = av.indexOf('=');
353
+ var av_key, av_value;
354
+ if (av_sep === -1) {
355
+ av_key = av;
356
+ av_value = null;
357
+ } else {
358
+ av_key = av.substr(0,av_sep);
359
+ av_value = av.substr(av_sep+1);
360
+ }
361
+
362
+ av_key = av_key.trim().toLowerCase();
353
363
  if (av_value) av_value = av_value.trim();
354
364
 
355
365
  switch(av_key) {
356
366
  case 'expires': // S5.2.1
357
- if (!av_value) { if(strict){return}else{break} }
367
+ if (!av_value) { if(strict){return}else{break;} }
358
368
  var exp = parseDate(av_value,strict);
359
369
  // "If the attribute-value failed to parse as a cookie date, ignore the
360
370
  // cookie-av."
361
- if (exp == null) { if(strict){return}else{break} }
371
+ if (exp == null) { if(strict){return}else{break;} }
362
372
  c.expires = exp;
363
373
  // over and underflow not realistically a concern: V8's getTime() seems to
364
374
  // store something larger than a 32-bit time_t (even with 32-bit node)
365
375
  break;
366
376
 
367
377
  case 'max-age': // S5.2.2
368
- if (!av_value) { if(strict){return}else{break} }
378
+ if (!av_value) { if(strict){return}else{break;} }
369
379
  // "If the first character of the attribute-value is not a DIGIT or a "-"
370
380
  // character ...[or]... If the remainder of attribute-value contains a
371
381
  // non-DIGIT character, ignore the cookie-av."
372
- if (!/^-?[0-9]+$/.test(av_value)) { if(strict){return}else{break} }
373
- var delta = parseInt(av_value);
382
+ if (!/^-?[0-9]+$/.test(av_value)) { if(strict){return}else{break;} }
383
+ var delta = parseInt(av_value,10);
374
384
  if (strict && delta <= 0) return; // S4.1.1
375
385
  // "If delta-seconds is less than or equal to zero (0), let expiry-time
376
386
  // be the earliest representable date and time."
@@ -380,11 +390,11 @@ function parse(str, strict) {
380
390
  case 'domain': // S5.2.3
381
391
  // "If the attribute-value is empty, the behavior is undefined. However,
382
392
  // the user agent SHOULD ignore the cookie-av entirely."
383
- if (!av_value) { if(strict){return}else{break} }
393
+ if (!av_value) { if(strict){return}else{break;} }
384
394
  // S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E
385
395
  // (".") character."
386
396
  var domain = av_value.trim().replace(/^\./,'');
387
- if (!domain) { if(strict){return}else{break} } // see "is empty" above
397
+ if (!domain) { if(strict){return}else{break;} } // see "is empty" above
388
398
  // "Convert the cookie-domain to lower case."
389
399
  c.domain = domain.toLowerCase();
390
400
  break;
@@ -400,7 +410,7 @@ function parse(str, strict) {
400
410
  * We'll represent the default-path as null since it depends on the
401
411
  * context of the parsing.
402
412
  */
403
- if (!av_value || av_value.substr(0,1) != "/") { if(strict){return}else{break} }
413
+ if (!av_value || av_value.substr(0,1) != "/") { if(strict){return}else{break;} }
404
414
  c.path = av_value;
405
415
  break;
406
416
 
@@ -434,8 +444,9 @@ function parse(str, strict) {
434
444
  function fromJSON(str) {
435
445
  if (!str) return null;
436
446
 
447
+ var obj;
437
448
  try {
438
- var obj = JSON.parse(str);
449
+ obj = JSON.parse(str);
439
450
  } catch (e) {
440
451
  return null;
441
452
  }
@@ -464,7 +475,7 @@ function fromJSON(str) {
464
475
  /* Section 5.4 part 2:
465
476
  * "* Cookies with longer paths are listed before cookies with
466
477
  * shorter paths.
467
- *
478
+ *
468
479
  * * Among cookies that have equal-length path fields, cookies with
469
480
  * earlier creation-times are listed before cookies with later
470
481
  * creation-times."
@@ -473,10 +484,10 @@ function fromJSON(str) {
473
484
  function cookieCompare(a,b) {
474
485
  // descending for length: b CMP a
475
486
  var deltaLen = (b.path ? b.path.length : 0) - (a.path ? a.path.length : 0);
476
- if (deltaLen != 0) return deltaLen;
487
+ if (deltaLen !== 0) return deltaLen;
477
488
  // ascending for time: a CMP b
478
- return (a.creation ? a.creation.getTime() : MAX_TIME)
479
- - (b.creation ? b.creation.getTime() : MAX_TIME);
489
+ return (a.creation ? a.creation.getTime() : MAX_TIME) -
490
+ (b.creation ? b.creation.getTime() : MAX_TIME);
480
491
  }
481
492
 
482
493
  // Gives the permutation of all possible domainMatch()es of a given domain. The
@@ -554,11 +565,11 @@ var numCookieProperties = cookieProperties.length;
554
565
 
555
566
  Cookie.prototype.inspect = function inspect() {
556
567
  var now = Date.now();
557
- return 'Cookie="'+this.toString()
558
- + '; hostOnly='+(this.hostOnly != null ? this.hostOnly : '?')
559
- + '; aAge='+(this.lastAccessed ? (now-this.lastAccessed.getTime())+'ms' : '?')
560
- + '; cAge='+(this.creation ? (now-this.creation.getTime())+'ms' : '?')
561
- + '"';
568
+ return 'Cookie="'+this.toString() +
569
+ '; hostOnly='+(this.hostOnly != null ? this.hostOnly : '?') +
570
+ '; aAge='+(this.lastAccessed ? (now-this.lastAccessed.getTime())+'ms' : '?') +
571
+ '; cAge='+(this.creation ? (now-this.creation.getTime())+'ms' : '?') +
572
+ '"';
562
573
  };
563
574
 
564
575
  Cookie.prototype.validate = function validate() {
@@ -691,7 +702,7 @@ Cookie.prototype.isPersistent = function isPersistent() {
691
702
  };
692
703
 
693
704
  // Mostly S5.1.2 and S5.2.3:
694
- Cookie.prototype.cdomain =
705
+ Cookie.prototype.cdomain =
695
706
  Cookie.prototype.canonicalizedDomain = function canonicalizedDomain() {
696
707
  if (this.domain == null) return null;
697
708
  return canonicalDomain(this.domain);
@@ -711,6 +722,7 @@ CookieJar.prototype.store = null;
711
722
  CookieJar.prototype.rejectPublicSuffixes = true;
712
723
 
713
724
  CookieJar.prototype.setCookie = function setCookie(cookie, url, options, cb) {
725
+ var err;
714
726
  var context = (url instanceof Object) ? url : urlParse(url);
715
727
  if (options instanceof Function) {
716
728
  cb = options;
@@ -723,7 +735,7 @@ CookieJar.prototype.setCookie = function setCookie(cookie, url, options, cb) {
723
735
  if (!(cookie instanceof Cookie))
724
736
  cookie = Cookie.parse(cookie, options.strict === true);
725
737
  if (!cookie) {
726
- var err = new Error("Cookie failed to parse");
738
+ err = new Error("Cookie failed to parse");
727
739
  return cb(options.ignoreError ? null : err);
728
740
  }
729
741
 
@@ -738,7 +750,7 @@ CookieJar.prototype.setCookie = function setCookie(cookie, url, options, cb) {
738
750
  if (this.rejectPublicSuffixes && cookie.domain) {
739
751
  var suffix = pubsuffix.getPublicSuffix(cookie.cdomain());
740
752
  if (suffix == null) { // e.g. "com"
741
- var err = new Error("Cookie has domain set to a public suffix");
753
+ err = new Error("Cookie has domain set to a public suffix");
742
754
  return cb(options.ignoreError ? null : err);
743
755
  }
744
756
  }
@@ -746,7 +758,7 @@ CookieJar.prototype.setCookie = function setCookie(cookie, url, options, cb) {
746
758
  // S5.3 step 6:
747
759
  if (cookie.domain) {
748
760
  if (!domainMatch(host, cookie.cdomain(), false)) {
749
- var err = new Error("Cookie not in this host's domain. Cookie:"+cookie.cdomain()+" Request:"+host);
761
+ err = new Error("Cookie not in this host's domain. Cookie:"+cookie.cdomain()+" Request:"+host);
750
762
  return cb(options.ignoreError ? null : err);
751
763
  }
752
764
 
@@ -773,7 +785,7 @@ CookieJar.prototype.setCookie = function setCookie(cookie, url, options, cb) {
773
785
 
774
786
  // S5.3 step 10
775
787
  if (options.http === false && cookie.httpOnly) {
776
- var err = new Error("Cookie is HttpOnly and this isn't an HTTP API");
788
+ err = new Error("Cookie is HttpOnly and this isn't an HTTP API");
777
789
  return cb(options.ignoreError ? null : err);
778
790
  }
779
791
 
@@ -797,7 +809,7 @@ CookieJar.prototype.setCookie = function setCookie(cookie, url, options, cb) {
797
809
  // S5.3 step 11 - "If the cookie store contains a cookie with the same name,
798
810
  // domain, and path as the newly created cookie:"
799
811
  if (options.http === false && oldCookie.httpOnly) { // step 11.2
800
- var err = new Error("old Cookie is HttpOnly and this isn't an HTTP API");
812
+ err = new Error("old Cookie is HttpOnly and this isn't an HTTP API");
801
813
  return cb(options.ignoreError ? null : err);
802
814
  }
803
815
  cookie.creation = oldCookie.creation; // step 11.3
@@ -835,6 +847,7 @@ CookieJar.prototype.getCookies = function getCookies(url, options, cb) {
835
847
 
836
848
  var now = options.now || Date.now();
837
849
  var expireCheck = options.expire !== false;
850
+ var allPaths = !!options.allPaths;
838
851
  var store = this.store;
839
852
 
840
853
  function matchingCookie(c) {
@@ -851,7 +864,7 @@ CookieJar.prototype.getCookies = function getCookies(url, options, cb) {
851
864
  }
852
865
 
853
866
  // "The request-uri's path path-matches the cookie's path."
854
- if (!pathMatch(path, c.path))
867
+ if (!allPaths && !pathMatch(path, c.path))
855
868
  return false;
856
869
 
857
870
  // "If the cookie's secure-only-flag is true, then the request-uri's
@@ -861,7 +874,7 @@ CookieJar.prototype.getCookies = function getCookies(url, options, cb) {
861
874
 
862
875
  // "If the cookie's http-only-flag is true, then exclude the cookie if the
863
876
  // cookie-string is being generated for a "non-HTTP" API"
864
- if (http == false && c.httpOnly)
877
+ if (c.httpOnly && !http)
865
878
  return false;
866
879
 
867
880
  // deferred from S5.3
@@ -874,7 +887,7 @@ CookieJar.prototype.getCookies = function getCookies(url, options, cb) {
874
887
  return true;
875
888
  }
876
889
 
877
- store.findCookies(host, path, function(err,cookies) {
890
+ store.findCookies(host, allPaths ? null : path, function(err,cookies) {
878
891
  if (err) return cb(err);
879
892
 
880
893
  cookies = cookies.filter(matchingCookie);
package/lib/memstore.js CHANGED
@@ -22,24 +22,50 @@ MemoryCookieStore.prototype.findCookie = function findCookie(domain, path, key,
22
22
 
23
23
  MemoryCookieStore.prototype.findCookies = function findCookies(domain, path, cb) {
24
24
  var results = [];
25
- if (!domain || !path) return cb(null,[]);
25
+ if (!domain) return cb(null,[]);
26
26
 
27
- var domains = permuteDomain(domain)||[domain], dlen = domains.length;
28
- var paths = permutePath(path), plen = paths.length;
27
+ var pathMatcher;
28
+ if (!path) {
29
+ // null or '/' means "all paths"
30
+ pathMatcher = function matchAll(domainIndex) {
31
+ for (var curPath in domainIndex) {
32
+ var pathIndex = domainIndex[curPath];
33
+ for (var key in pathIndex) {
34
+ results.push(pathIndex[key]);
35
+ }
36
+ }
37
+ };
29
38
 
30
- for (var i=0; i<dlen; i++) {
31
- var curDomain = domains[i];
32
- var domainIndex = this.idx[curDomain];
33
- if (!domainIndex) continue;
34
- for (var j=0; j<plen; j++) {
35
- var curPath = paths[j];
36
- var pathIndex = domainIndex[curPath];
37
- if (!pathIndex) continue;
39
+ } else if (path === '/') {
40
+ pathMatcher = function matchSlash(domainIndex) {
41
+ var pathIndex = domainIndex['/'];
42
+ if (!pathIndex) return;
38
43
  for (var key in pathIndex) {
39
44
  results.push(pathIndex[key]);
40
45
  }
41
- }
46
+ };
47
+
48
+ } else {
49
+ var paths = permutePath(path) || [path];
50
+ pathMatcher = function matchRFC(domainIndex) {
51
+ paths.forEach(function(curPath) {
52
+ var pathIndex = domainIndex[curPath];
53
+ if (!pathIndex) return;
54
+ for (var key in pathIndex) {
55
+ results.push(pathIndex[key]);
56
+ }
57
+ });
58
+ };
42
59
  }
60
+
61
+ var domains = permuteDomain(domain) || [domain];
62
+ var idx = this.idx;
63
+ domains.forEach(function(curDomain) {
64
+ var domainIndex = idx[curDomain];
65
+ if (!domainIndex) return;
66
+ pathMatcher(domainIndex);
67
+ });
68
+
43
69
  cb(null,results);
44
70
  };
45
71
 
package/package.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "author": "Jeremy Stashewsky <jeremy@goinstant.com> (https://github.com/stash)",
3
3
  "name": "tough-cookie",
4
4
  "description": "RFC6265 Cookies and Cookie Jar for node.js",
5
- "keywords": "HTTP cookie cookies set-cookie cookiejar jar RFC6265 RFC2965",
6
- "version": "0.9.11",
5
+ "keywords":["HTTP","cookie","cookies","set-cookie","cookiejar","jar","RFC6265","RFC2965"],
6
+ "version": "0.9.15",
7
7
  "homepage": "https://github.com/goinstant/node-cookie",
8
8
  "repository": {
9
9
  "type": "git",
package/test.js CHANGED
@@ -35,11 +35,11 @@ function dateVows(table) {
35
35
  var expect = table[date];
36
36
  theVows[date] = function() {
37
37
  var got = tough.parseDate(date) ? 'valid' : 'invalid';
38
- assert.equal(got, expect ? 'valid' : 'invalid')
38
+ assert.equal(got, expect ? 'valid' : 'invalid');
39
39
  };
40
40
  });
41
- return { "date parsing": theVows }
42
- };
41
+ return { "date parsing": theVows };
42
+ }
43
43
 
44
44
  function matchVows(func,table) {
45
45
  var theVows = {};
@@ -71,12 +71,14 @@ function defaultPathVows(table) {
71
71
  var atNow = Date.now();
72
72
  function at(offset) { return {now: new Date(atNow+offset)} }
73
73
 
74
- vows.describe('Cookie Jar').addBatch({
74
+ vows.describe('Cookie Jar')
75
+ .addBatch({
75
76
  "all defined": function() {
76
77
  assert.ok(Cookie);
77
78
  assert.ok(CookieJar);
78
79
  },
79
- }).addBatch(
80
+ })
81
+ .addBatch(
80
82
  dateVows({
81
83
  "Wed, 09 Jun 2021 10:18:14 GMT": true,
82
84
  "Wed, 09 Jun 2021 22:18:14 GMT": true,
@@ -93,8 +95,11 @@ vows.describe('Cookie Jar').addBatch({
93
95
  '01 Jan 1601 00:00:00 GMT': true,
94
96
  '10 Feb 81 13:00:00 GMT': true, // implicit year
95
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
96
100
  })
97
- ).addBatch({
101
+ )
102
+ .addBatch({
98
103
  "strict date parse of Thu, 01 Jan 1970 00:00:010 GMT": {
99
104
  topic: function() {
100
105
  return tough.parseDate('Thu, 01 Jan 1970 00:00:010 GMT', true) ? true : false;
@@ -103,7 +108,8 @@ vows.describe('Cookie Jar').addBatch({
103
108
  assert.equal(date,false);
104
109
  },
105
110
  }
106
- }).addBatch({
111
+ })
112
+ .addBatch({
107
113
  "formatting": {
108
114
  "a simple cookie": {
109
115
  topic: function() {
@@ -208,8 +214,26 @@ vows.describe('Cookie Jar').addBatch({
208
214
  assert.equal(c.toString(), 'a=b; Path=/should-stringify');
209
215
  },
210
216
  },
217
+ "minutes are '10'": {
218
+ topic: function() {
219
+ var c = new Cookie();
220
+ c.key = 'a';
221
+ c.value = 'b';
222
+ c.expires = new Date(1284113410000);
223
+ return c;
224
+ },
225
+ "validates": function(c) {
226
+ assert.ok(c.validate());
227
+ },
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');
232
+ },
233
+ }
211
234
  }
212
- }).addBatch({
235
+ })
236
+ .addBatch({
213
237
  "TTL with max-age": function() {
214
238
  var c = new Cookie();
215
239
  c.maxAge = 123;
@@ -403,11 +427,11 @@ vows.describe('Cookie Jar').addBatch({
403
427
  "c=d;;;;"
404
428
  ];
405
429
  },
406
- "strict": function (t) {
407
- assert.ok(!Cookie.parse(t[0], true));
430
+ "strict": function (t) {
431
+ assert.ok(!Cookie.parse(t[0], true));
408
432
  assert.ok(!Cookie.parse(t[1], true));
409
433
  },
410
- "non-strict": function (t) {
434
+ "non-strict": function (t) {
411
435
  var c1 = Cookie.parse(t[0]);
412
436
  var c2 = Cookie.parse(t[1]);
413
437
  assert.ok(c1);
@@ -467,9 +491,26 @@ vows.describe('Cookie Jar').addBatch({
467
491
  },
468
492
  "secure": function(c) { assert.ok(c.secure) },
469
493
  "httponly": function(c) { assert.ok(c.httpOnly) },
494
+ },
495
+ "lots of equal signs": {
496
+ topic: function() {
497
+ return Cookie.parse("queryPref=b=c&d=e; Path=/f=g; Expires=Thu, 17 Apr 2014 02:12:29 GMT; HttpOnly");
498
+ },
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');
504
+ },
505
+ "expires": function(c) {
506
+ assert.notEqual(c.expires, Infinity);
507
+ assert.equal(c.expires.getTime(), 1397700749000);
508
+ },
509
+ "httponly": function(c) { assert.ok(c.httpOnly) },
470
510
  }
471
511
  }
472
- }).addBatch({
512
+ })
513
+ .addBatch({
473
514
  "domain normalization": {
474
515
  "simple": function() {
475
516
  var c = new Cookie();
@@ -497,7 +538,8 @@ vows.describe('Cookie Jar').addBatch({
497
538
  assert.equal(c.canonicalizedDomain(), "xn--jxalpdlp.xn--jxalpdlp");
498
539
  }
499
540
  }
500
- }).addBatch({
541
+ })
542
+ .addBatch({
501
543
  "Domain Match":matchVows(tough.domainMatch, [
502
544
  // str, dom, expect
503
545
  ["example.com", "example.com", true],
@@ -510,8 +552,13 @@ vows.describe('Cookie Jar').addBatch({
510
552
  ["www.subdom.example.com", "subdom.example.com", true],
511
553
  ["example.com", "example.com.", false], // RFC6265 S4.1.2.3
512
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],
557
+ [null, null, null],
558
+ [undefined, undefined, null],
513
559
  ])
514
- }).addBatch({
560
+ })
561
+ .addBatch({
515
562
  "default-path": defaultPathVows([
516
563
  [null,"/"],
517
564
  ["/","/"],
@@ -519,7 +566,8 @@ vows.describe('Cookie Jar').addBatch({
519
566
  ["/dir/file","/dir"],
520
567
  ["noslash","/"],
521
568
  ])
522
- }).addBatch({
569
+ })
570
+ .addBatch({
523
571
  "Path-Match": matchVows(tough.pathMatch, [
524
572
  // request, cookie, match
525
573
  ["/","/",true],
@@ -530,7 +578,8 @@ vows.describe('Cookie Jar').addBatch({
530
578
  ["/dir/file","/dir",true],
531
579
  ["/directory","/dir",false],
532
580
  ])
533
- }).addBatch({
581
+ })
582
+ .addBatch({
534
583
  "Cookie Sorting": {
535
584
  topic: function() {
536
585
  var cookies = [];
@@ -561,7 +610,8 @@ vows.describe('Cookie Jar').addBatch({
561
610
  assert.deepEqual(names, ['e','f','c','d','a','b']);
562
611
  },
563
612
  }
564
- }).addBatch({
613
+ })
614
+ .addBatch({
565
615
  "CookieJar": {
566
616
  "Setting a basic cookie": {
567
617
  topic: function() {
@@ -834,7 +884,7 @@ vows.describe('Cookie Jar').addBatch({
834
884
  },
835
885
  "CookieJar setCookie errors": {
836
886
  "public-suffix domain": {
837
- topic: function(cj) {
887
+ topic: function() {
838
888
  var cj = new CookieJar();
839
889
  cj.setCookie('i=9; Domain=kyoto.jp; Path=/','kyoto.jp',this.callback);
840
890
  },
@@ -845,7 +895,7 @@ vows.describe('Cookie Jar').addBatch({
845
895
  },
846
896
  },
847
897
  "wrong domain": {
848
- topic: function(cj) {
898
+ topic: function() {
849
899
  var cj = new CookieJar();
850
900
  cj.setCookie('j=10; Domain=google.com; Path=/','google.ca',this.callback);
851
901
  },
@@ -856,7 +906,7 @@ vows.describe('Cookie Jar').addBatch({
856
906
  },
857
907
  },
858
908
  "old cookie is HttpOnly": {
859
- topic: function(cj) {
909
+ topic: function() {
860
910
  var cb = this.callback;
861
911
  var next = function (err,c) {
862
912
  return cb(err,cj);
@@ -889,7 +939,8 @@ vows.describe('Cookie Jar').addBatch({
889
939
  },
890
940
  },
891
941
  },
892
- }).addBatch({
942
+ })
943
+ .addBatch({
893
944
  "JSON": {
894
945
  "serialization": {
895
946
  topic: function() {
@@ -900,7 +951,7 @@ vows.describe('Cookie Jar').addBatch({
900
951
  assert.equal(typeof str, "string");
901
952
  },
902
953
  "date is in ISO format": function(str) {
903
- assert.match(str, /"expires":"2038-01-19T03:14:07.000Z"/, 'expires is in ISO format');
954
+ assert.match(str, /"expires":"2038-01-19T03:14:07\.000Z"/, 'expires is in ISO format');
904
955
  },
905
956
  },
906
957
  "deserialization": {
@@ -1012,7 +1063,8 @@ vows.describe('Cookie Jar').addBatch({
1012
1063
  },
1013
1064
  },
1014
1065
  }
1015
- }).addBatch({
1066
+ })
1067
+ .addBatch({
1016
1068
  "permuteDomain": {
1017
1069
  "base case": {
1018
1070
  topic: tough.permuteDomain.bind(null,'example.com'),
@@ -1074,7 +1126,8 @@ vows.describe('Cookie Jar').addBatch({
1074
1126
  },
1075
1127
  },
1076
1128
  }
1077
- }).addBatch({
1129
+ })
1130
+ .addBatch({
1078
1131
  "Issue 1": {
1079
1132
  topic: function() {
1080
1133
  var cj = new CookieJar();
@@ -1104,7 +1157,8 @@ vows.describe('Cookie Jar').addBatch({
1104
1157
  },
1105
1158
  }
1106
1159
  }
1107
- }).addBatch({
1160
+ })
1161
+ .addBatch({
1108
1162
  "expiry option": {
1109
1163
  topic: function() {
1110
1164
  var cb = this.callback;
@@ -1134,13 +1188,14 @@ vows.describe('Cookie Jar').addBatch({
1134
1188
  },
1135
1189
  }
1136
1190
  }
1137
- }).addBatch({
1191
+ })
1192
+ .addBatch({
1138
1193
  "trailing semi-colon set into cj": {
1139
1194
  topic: function () {
1140
1195
  var cb = this.callback;
1141
1196
  var cj = new CookieJar();
1142
1197
  var ex = 'http://www.example.com';
1143
- tasks = []
1198
+ var tasks = [];
1144
1199
  tasks.push(function(next) {
1145
1200
  cj.setCookie('broken_path=testme; path=/;',ex,at(-1),next);
1146
1201
  });
@@ -1182,10 +1237,11 @@ vows.describe('Cookie Jar').addBatch({
1182
1237
  assert.equal(t.cookies[1].key, "b");
1183
1238
  assert.equal(t.cookies[1].value, "2");
1184
1239
  assert.equal(t.cookies[1].path, "/");
1185
- },
1240
+ },
1186
1241
  },
1187
1242
  }
1188
- }).addBatch({
1243
+ })
1244
+ .addBatch({
1189
1245
  "Constructor":{
1190
1246
  topic: function () {
1191
1247
  return new Cookie({
@@ -1210,4 +1266,75 @@ vows.describe('Cookie Jar').addBatch({
1210
1266
  assert.equal(c.httpOnly, false);
1211
1267
  }
1212
1268
  }
1213
- }).export(module);
1269
+ })
1270
+ .addBatch({
1271
+ "allPaths option": {
1272
+ topic: function() {
1273
+ var cj = new CookieJar();
1274
+ var tasks = [];
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});
1284
+ });
1285
+ },
1286
+ "all set": function(t) {
1287
+ assert.equal(t.cookies.length, 6);
1288
+ assert.ok(t.cookies.every(function(c) { return !!c }));
1289
+ },
1290
+ "getting without allPaths": {
1291
+ topic: function(t) {
1292
+ var cb = this.callback;
1293
+ var cj = t.cj;
1294
+ cj.getCookies('http://www.example.com/', {}, function(err,cookies) {
1295
+ cb(err, {cj:cj, cookies:cookies});
1296
+ });
1297
+ },
1298
+ "found just two cookies": function(t) {
1299
+ assert.equal(t.cookies.length, 2);
1300
+ },
1301
+ "all are path=/": function(t) {
1302
+ assert.ok(t.cookies.every(function(c) { return c.path === '/' }));
1303
+ },
1304
+ "no 'other' cookies": function(t) {
1305
+ assert.ok(!t.cookies.some(function(c) { return (/^other/).test(c.name) }));
1306
+ },
1307
+ },
1308
+ "getting without allPaths for /foo": {
1309
+ topic: function(t) {
1310
+ var cb = this.callback;
1311
+ var cj = t.cj;
1312
+ cj.getCookies('http://www.example.com/foo', {}, function(err,cookies) {
1313
+ cb(err, {cj:cj, cookies:cookies});
1314
+ });
1315
+ },
1316
+ "found four cookies": function(t) {
1317
+ assert.equal(t.cookies.length, 4);
1318
+ },
1319
+ "no 'other' cookies": function(t) {
1320
+ assert.ok(!t.cookies.some(function(c) { return (/^other/).test(c.name) }));
1321
+ },
1322
+ },
1323
+ "getting with allPaths:true": {
1324
+ topic: function(t) {
1325
+ var cb = this.callback;
1326
+ var cj = t.cj;
1327
+ cj.getCookies('http://www.example.com/', {allPaths:true}, function(err,cookies) {
1328
+ cb(err, {cj:cj, cookies:cookies});
1329
+ });
1330
+ },
1331
+ "found four cookies": function(t) {
1332
+ assert.equal(t.cookies.length, 4);
1333
+ },
1334
+ "no 'other' cookies": function(t) {
1335
+ assert.ok(!t.cookies.some(function(c) { return (/^other/).test(c.name) }));
1336
+ },
1337
+ },
1338
+ }
1339
+ })
1340
+ .export(module);