tough-cookie
Advanced tools
Comparing version 0.9.5 to 0.9.6
@@ -64,3 +64,4 @@ /* | ||
*/ | ||
var TIME = /^(0?[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/; | ||
var TIME = /(0?[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])/; | ||
var STRICT_TIME = /^(0?[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/; | ||
@@ -87,3 +88,3 @@ var MONTH = /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)$/i; | ||
// RFC6265 S5.1.1 date parser: | ||
function parseDate(str) { | ||
function parseDate(str,strict) { | ||
if (!str) return; | ||
@@ -115,3 +116,3 @@ var found_time, found_dom, found_month, found_year; | ||
if (!found_time) { | ||
result = TIME.exec(token); | ||
result = (strict ? STRICT_TIME : TIME).exec(token); | ||
if (result) { | ||
@@ -349,3 +350,3 @@ found_time = true; | ||
if (!av_value) { if(strict){return}else{break} } | ||
var exp = parseDate(av_value); | ||
var exp = parseDate(av_value,strict); | ||
// "If the attribute-value failed to parse as a cookie date, ignore the | ||
@@ -369,3 +370,3 @@ // cookie-av." | ||
// be the earliest representable date and time." | ||
c.maxAge = (delta <= 0) ? -Infinity : delta; | ||
c.setMaxAge(delta); | ||
break; | ||
@@ -444,3 +445,3 @@ | ||
{ | ||
c[prop] = new Date(obj[prop]); | ||
c[prop] = obj[prop] == "Infinity" ? "Infinity" : new Date(obj[prop]); | ||
} else { | ||
@@ -451,2 +452,3 @@ c[prop] = obj[prop]; | ||
// ensure a default date for sorting: | ||
@@ -522,3 +524,3 @@ c.creation = c.creation || new Date(); | ||
// the order in which the RFC has them: | ||
Cookie.prototype.expires = Infinity; | ||
Cookie.prototype.expires = "Infinity"; // coerces to literal Infinity | ||
Cookie.prototype.maxAge = null; // takes precedence over expires for TTL | ||
@@ -555,3 +557,3 @@ Cookie.prototype.domain = null; | ||
return false; | ||
if (this.expires !== Infinity && !(this.expires instanceof Date) && !parseDate(this.expires)) | ||
if (this.expires != Infinity && !(this.expires instanceof Date) && !parseDate(this.expires,true)) | ||
return false; | ||
@@ -576,11 +578,20 @@ if (this.maxAge != null && this.maxAge <= 0) | ||
if (exp instanceof Date) this.expires = exp; | ||
else this.expires = parseDate(exp) || Infinity; | ||
else this.expires = parseDate(exp) || "Infinity"; | ||
}; | ||
Cookie.prototype.setMaxAge = function setMaxAge(age) { | ||
if (age === Infinity || age === -Infinity) | ||
this.maxAge = age.toString(); // so JSON.stringify() works | ||
else | ||
this.maxAge = age; | ||
}; | ||
// gives Cookie header format | ||
Cookie.prototype.cookieString = function cookieString() { | ||
if (COOKIE_OCTETS.test(this.value)) | ||
return this.key+'='+this.value; | ||
var val = this.value; | ||
if (val == null) val = ''; | ||
if (!val.length || COOKIE_OCTETS.test(val)) | ||
return this.key+'='+val; | ||
else | ||
return this.key+'="'+this.value+'"'; | ||
return this.key+'="'+val+'"'; | ||
}; | ||
@@ -592,3 +603,3 @@ | ||
if (this.expires !== Infinity) { | ||
if (this.expires != Infinity) { | ||
if (this.expires instanceof Date) | ||
@@ -600,5 +611,4 @@ str += '; Expires='+formatDate(this.expires); | ||
if (this.maxAge != null && this.maxAge !== Infinity) { | ||
if (this.maxAge != null && this.maxAge != Infinity) | ||
str += '; Max-Age='+this.maxAge; | ||
} | ||
@@ -624,2 +634,3 @@ if (this.domain && !this.hostOnly) | ||
// S5.3 says to give the "latest representable date" for which we use Infinity | ||
// For "expired" we use 0 | ||
Cookie.prototype.TTL = function TTL(now) { | ||
@@ -632,14 +643,15 @@ /* RFC6265 S4.1.2.2 If a cookie has both the Max-Age and the Expires | ||
if (this.maxAge != null) { | ||
return this.maxAge * 1000; | ||
return this.maxAge<=0 ? 0 : this.maxAge*1000; | ||
} | ||
if (this.expires !== Infinity) { | ||
if (!(this.expires instanceof Date)) { | ||
this.expires = parseDate(this.expires) || Infinity; | ||
var expires = this.expires; | ||
if (expires != Infinity) { | ||
if (!(expires instanceof Date)) { | ||
expires = parseDate(expires) || Infinity; | ||
} | ||
if (this.expires === Infinity) | ||
if (expires == Infinity) | ||
return Infinity; | ||
return this.expires.getTime() - (now || Date.now()); | ||
return expires.getTime() - (now || Date.now()); | ||
} | ||
@@ -659,3 +671,3 @@ | ||
if (this.expires === Infinity) return Infinity; | ||
if (this.expires == Infinity) return Infinity; | ||
return this.expires.getTime(); | ||
@@ -675,3 +687,3 @@ }; | ||
Cookie.prototype.isPersistent = function isPersistent() { | ||
return (this.maxAge != null || this.expires !== Infinity); | ||
return (this.maxAge != null || this.expires != Infinity); | ||
}; | ||
@@ -808,3 +820,2 @@ | ||
var expireCheck = options.expire !== false; | ||
console.log(expireCheck); | ||
var store = this.store; | ||
@@ -811,0 +822,0 @@ |
@@ -6,3 +6,3 @@ { | ||
"keywords": "HTTP cookie cookies set-cookie cookiejar jar RFC6265 RFC2965", | ||
"version": "0.9.5", | ||
"version": "0.9.6", | ||
"homepage": "https://github.com/goinstant/node-cookie", | ||
@@ -9,0 +9,0 @@ "repository": { |
@@ -39,6 +39,6 @@ [RFC6265](http://tools.ietf.org/html/rfc6265) Cookies and CookieJar for Node.js | ||
parseDate(string) | ||
parseDate(string[,strict]) | ||
----------------- | ||
Parse a cookie date string into a `Date`. Parses according to RFC6265 Section 5.1.1, not `Date.parse()`. | ||
Parse a cookie date string into a `Date`. Parses according to RFC6265 Section 5.1.1, not `Date.parse()`. If strict is set to true then leading/trailing non-seperator characters around the time part will cause the parsing to fail (e.g. "Thu, 01 Jan 1970 00:00:010 GMT" has an extra trailing zero but Chrome, an assumedly RFC-compliant browser, treats this as valid). | ||
@@ -143,4 +143,4 @@ formatDate(date) | ||
* _value_ - string - the value of the cookie (default "") | ||
* _expires_ - `Date` - if set, the `Expires=` attribute of the cookie (defaults to Infinity) | ||
* _maxAge_ - seconds - if set, the `Max-Age=` attribute _in seconds_ of the cookie. | ||
* _expires_ - `Date` - if set, the `Expires=` attribute of the cookie (defaults to the string `"Infinity"`). See `setExpires()` | ||
* _maxAge_ - seconds - if set, the `Max-Age=` attribute _in seconds_ of the cookie. May also be set to strings `"Infinity"` and `"-Infinity"` for non-expiry and immediate-expiry, respectively. See `setMaxAge()` | ||
* _domain_ - string - the `Domain=` attribute of the cookie | ||
@@ -172,4 +172,9 @@ * _path_ - string - the `Path=` of the cookie | ||
sets the expiry based on a date-string passed through `parseDate()` | ||
sets the expiry based on a date-string passed through `parseDate()`. If parseDate returns `null` (i.e. can't parse this date string), `.expires` is set to `"Infinity"` (a string) is set. | ||
.setMaxAge(number) | ||
------------------- | ||
sets the maxAge in seconds. Coerces `-Infinity` to `"-Infinity"` and `Infinity` to `"Infinity"` so it JSON serializes correctly. | ||
.expiryTime([now=Date.now()]) | ||
@@ -181,3 +186,3 @@ ----------------------------- | ||
Computes the absolute unix-epoch milliseconds that this cookie expires (or a `Date` object in the case of `expiryDate()`). Note that in both cases `now` should be milliseconds. | ||
expiryTime() Computes the absolute unix-epoch milliseconds that this cookie expires. expiryDate() works similarly, except it returns a `Date` object. Note that in both cases the `now` parameter should be milliseconds. | ||
@@ -190,7 +195,9 @@ Max-Age takes precedence over Expires (as per the RFC). The `.created` attribute -- or, by default, the `now` paramter -- is used to offset the `.maxAge` attribute. | ||
.TTL(now) | ||
.TTL([now=Date.now()]) | ||
--------- | ||
compute the TTL relative to `now` (milliseconds). `Date.now()` is used by default. The same precedence rules as for `expiryTime` apply. | ||
compute the TTL relative to `now` (milliseconds). The same precedence rules as for `expiryTime`/`expiryDate` apply. | ||
The "number" `Infinity` is returned for cookies without an explicit expiry and `0` is returned if the cookie is expired. Otherwise a time-to-live in milliseconds is returned. | ||
.canonicalizedDoman() | ||
@@ -197,0 +204,0 @@ --------------------- |
268
test.js
@@ -95,52 +95,89 @@ /* | ||
'10 Feb 81 13:00:00 GMT': true, // implicit year | ||
'Thu, 01 Jan 1970 00:00:010 GMT': true, // strange time, non-strict OK | ||
}) | ||
).addBatch({ | ||
"formatting a simple cookie": { | ||
"strict date parse of Thu, 01 Jan 1970 00:00:010 GMT": { | ||
topic: function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; | ||
c.value = 'b'; | ||
return c; | ||
return tough.parseDate('Thu, 01 Jan 1970 00:00:010 GMT', true) ? true : false; | ||
}, | ||
"validates": function(c) { | ||
assert.ok(c.validate()); | ||
"invalid": function(date) { | ||
assert.equal(date,false); | ||
}, | ||
"to string": function(c) { | ||
assert.equal(c.toString(), 'a=b'); | ||
} | ||
}).addBatch({ | ||
"formatting": { | ||
"a simple cookie": { | ||
topic: function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; | ||
c.value = 'b'; | ||
return c; | ||
}, | ||
"validates": function(c) { | ||
assert.ok(c.validate()); | ||
}, | ||
"to string": function(c) { | ||
assert.equal(c.toString(), 'a=b'); | ||
}, | ||
}, | ||
}, | ||
"formatting a cookie with spaces in the value": { | ||
topic: function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; | ||
c.value = 'beta gamma'; | ||
return c; | ||
"a cookie with spaces in the value": { | ||
topic: function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; | ||
c.value = 'beta gamma'; | ||
return c; | ||
}, | ||
"doesn't validate": function(c) { | ||
assert.ok(!c.validate()); | ||
}, | ||
"to string": function(c) { | ||
assert.equal(c.toString(), 'a="beta gamma"'); | ||
}, | ||
}, | ||
"doesn't validate": function(c) { | ||
assert.ok(!c.validate()); | ||
"with an empty value and HttpOnly": { | ||
topic: function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; | ||
c.httpOnly = true; | ||
return c; | ||
}, | ||
"to string": function(c) { | ||
assert.equal(c.toString(), 'a=; HttpOnly'); | ||
} | ||
}, | ||
"to string": function(c) { | ||
assert.equal(c.toString(), 'a="beta gamma"'); | ||
"with an expiry": { | ||
topic: function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; | ||
c.value = 'b'; | ||
c.setExpires("Oct 18 2011 07:05:03 GMT"); | ||
return c; | ||
}, | ||
"validates": function(c) { | ||
assert.ok(c.validate()); | ||
}, | ||
"to string": function(c) { | ||
assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT'); | ||
}, | ||
"to short string": function(c) { | ||
assert.equal(c.cookieString(), 'a=b'); | ||
}, | ||
}, | ||
}, | ||
"formatting with an expiry": { | ||
topic: function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; | ||
c.value = 'b'; | ||
c.setExpires("Oct 18 2011 07:05:03 GMT"); | ||
return c; | ||
"with a max-age": { | ||
topic: function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; | ||
c.value = 'b'; | ||
c.setExpires("Oct 18 2011 07:05:03 GMT"); | ||
c.maxAge = 12345; | ||
return c; | ||
}, | ||
"validates": function(c) { | ||
assert.ok(c.validate()); // mabe this one *shouldn't*? | ||
}, | ||
"to string": function(c) { | ||
assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Max-Age=12345'); | ||
}, | ||
}, | ||
"validates": function(c) { | ||
assert.ok(c.validate()); | ||
}, | ||
"to string": function(c) { | ||
assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT'); | ||
}, | ||
"to short string": function(c) { | ||
assert.equal(c.cookieString(), 'a=b'); | ||
}, | ||
}, | ||
"formatting with a max-age": { | ||
topic: function() { | ||
"with a bunch of things": function() { | ||
var c = new Cookie(); | ||
@@ -151,41 +188,27 @@ c.key = 'a'; | ||
c.maxAge = 12345; | ||
return c; | ||
c.domain = 'example.com'; | ||
c.path = '/foo'; | ||
c.secure = true; | ||
c.httpOnly = true; | ||
c.extensions = ['MyExtension']; | ||
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'); | ||
}, | ||
"validates": function(c) { | ||
assert.ok(c.validate()); // mabe this one *shouldn't*? | ||
"a host-only cookie": { | ||
topic: function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; | ||
c.value = 'b'; | ||
c.hostOnly = true; | ||
c.domain = 'shouldnt-stringify.example.com'; | ||
c.path = '/should-stringify'; | ||
return c; | ||
}, | ||
"validates": function(c) { | ||
assert.ok(c.validate()); | ||
}, | ||
"to string": function(c) { | ||
assert.equal(c.toString(), 'a=b; Path=/should-stringify'); | ||
}, | ||
}, | ||
"to string": function(c) { | ||
assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Max-Age=12345'); | ||
}, | ||
}, | ||
"formatting with a bunch of things": function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; | ||
c.value = 'b'; | ||
c.setExpires("Oct 18 2011 07:05:03 GMT"); | ||
c.maxAge = 12345; | ||
c.domain = 'example.com'; | ||
c.path = '/foo'; | ||
c.secure = true; | ||
c.httpOnly = true; | ||
c.extensions = ['MyExtension']; | ||
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'); | ||
}, | ||
"formatting a host-only cookie": { | ||
topic: function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; | ||
c.value = 'b'; | ||
c.hostOnly = true; | ||
c.domain = 'shouldnt-stringify.example.com'; | ||
c.path = '/should-stringify'; | ||
return c; | ||
}, | ||
"validates": function(c) { | ||
assert.ok(c.validate()); | ||
}, | ||
"to string": function(c) { | ||
assert.equal(c.toString(), 'a=b; Path=/should-stringify'); | ||
}, | ||
}, | ||
} | ||
}).addBatch({ | ||
@@ -201,7 +224,15 @@ "TTL with max-age": function() { | ||
c.key = 'a'; c.value = 'b'; | ||
c.maxAge = 0; // technically against the spec to be zero: "Max-Age=" non-zero-digit *DIGIT | ||
c.maxAge = 0; // should be treated as "earliest representable" | ||
assert.equal(c.TTL(), 0); | ||
assert.equal(c.expiryTime(new Date(9000000)), -Infinity); | ||
assert.ok(!c.validate()); | ||
assert.ok(!c.validate()); // not valid, really: non-zero-digit *DIGIT | ||
}, | ||
"TTL with negative max-age": function() { | ||
var c = new Cookie(); | ||
c.key = 'a'; c.value = 'b'; | ||
c.maxAge = -1; // should be treated as "earliest representable" | ||
assert.equal(c.TTL(), 0); | ||
assert.equal(c.expiryTime(new Date(9000000)), -Infinity); | ||
assert.ok(!c.validate()); // not valid, really: non-zero-digit *DIGIT | ||
}, | ||
"TTL with max-age and expires": function() { | ||
@@ -309,3 +340,3 @@ var c = new Cookie(); | ||
assert.ok(c); | ||
assert.equal(c.maxAge, -Infinity); | ||
assert.equal(c.maxAge, 0); | ||
}, | ||
@@ -318,3 +349,3 @@ }, | ||
assert.ok(c); | ||
assert.equal(c.maxAge, -Infinity); | ||
assert.equal(c.maxAge, -1); | ||
}, | ||
@@ -871,2 +902,77 @@ }, | ||
}, | ||
}, | ||
"expiry deserialization": { | ||
"Infinity": { | ||
topic: Cookie.fromJSON.bind(null, '{"expires":"Infinity"}'), | ||
"is infinite": function(c) { | ||
assert.strictEqual(c.expires, "Infinity"); | ||
assert.equal(c.expires, Infinity); | ||
}, | ||
}, | ||
}, | ||
"maxAge serialization": { | ||
topic: function() { | ||
return function(toSet) { | ||
var c = new Cookie(); | ||
c.key = 'foo'; c.value = 'bar'; | ||
c.setMaxAge(toSet); | ||
return JSON.stringify(c); | ||
}; | ||
}, | ||
"zero": { | ||
topic: function(f) { return f(0) }, | ||
"looks good": function(str) { | ||
assert.match(str, /"maxAge":0/); | ||
}, | ||
}, | ||
"Infinity": { | ||
topic: function(f) { return f(Infinity) }, | ||
"looks good": function(str) { | ||
assert.match(str, /"maxAge":"Infinity"/); | ||
}, | ||
}, | ||
"-Infinity": { | ||
topic: function(f) { return f(-Infinity) }, | ||
"looks good": function(str) { | ||
assert.match(str, /"maxAge":"-Infinity"/); | ||
}, | ||
}, | ||
"null": { | ||
topic: function(f) { return f(null) }, | ||
"looks good": function(str) { | ||
assert.match(str, /"maxAge":null/); | ||
}, | ||
}, | ||
}, | ||
"maxAge deserialization": { | ||
"number": { | ||
topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":123}'), | ||
"is the number": function(c) { | ||
assert.strictEqual(c.maxAge, 123); | ||
}, | ||
}, | ||
"null": { | ||
topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":null}'), | ||
"is null": function(c) { | ||
assert.strictEqual(c.maxAge, null); | ||
}, | ||
}, | ||
"less than zero": { | ||
topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":-123}'), | ||
"is -123": function(c) { | ||
assert.strictEqual(c.maxAge, -123); | ||
}, | ||
}, | ||
"Infinity": { | ||
topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":"Infinity"}'), | ||
"is inf-as-string": function(c) { | ||
assert.strictEqual(c.maxAge, "Infinity"); | ||
}, | ||
}, | ||
"-Infinity": { | ||
topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":"-Infinity"}'), | ||
"is inf-as-string": function(c) { | ||
assert.strictEqual(c.maxAge, "-Infinity"); | ||
}, | ||
}, | ||
} | ||
@@ -873,0 +979,0 @@ }).addBatch({ |
249111
2458
358