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 +72 -0
- package/README.md +3 -0
- package/lib/cookie.js +52 -39
- package/lib/memstore.js +38 -12
- package/package.json +2 -2
- package/test.js +157 -30
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
|
|
195
|
-
var h = date.getUTCHours(); h = h
|
|
196
|
-
var m = date.getUTCMinutes(); m = m
|
|
197
|
-
var s = date.getUTCSeconds(); s = 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
|
|
351
|
-
var av_key
|
|
352
|
-
|
|
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
|
-
|
|
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
|
|
487
|
+
if (deltaLen !== 0) return deltaLen;
|
|
477
488
|
// ascending for time: a CMP b
|
|
478
|
-
return (a.creation ? a.creation.getTime() : MAX_TIME)
|
|
479
|
-
|
|
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
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
25
|
+
if (!domain) return cb(null,[]);
|
|
26
26
|
|
|
27
|
-
var
|
|
28
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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":
|
|
6
|
-
"version": "0.9.
|
|
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')
|
|
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
|
-
})
|
|
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
|
-
)
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
})
|
|
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
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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
|
-
})
|
|
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);
|