tough-cookie
Advanced tools
Comparing version 0.11.0 to 0.12.0
@@ -21,3 +21,3 @@ /* | ||
*/ | ||
'use strict'; | ||
var fs = require('fs'); | ||
@@ -28,3 +28,5 @@ var assert = require('assert'); | ||
fs.readFile('./public-suffix.txt', 'utf8', function(err,string) { | ||
if (err) throw err; | ||
if (err) { | ||
throw err; | ||
} | ||
var lines = string.split("\n"); | ||
@@ -43,3 +45,5 @@ process.nextTick(function() { | ||
line = line.replace(COMMENT,'').trim(); | ||
if (!line) continue; | ||
if (!line) { | ||
continue; | ||
} | ||
addToIndex(index,line); | ||
@@ -53,3 +57,3 @@ } | ||
encoding: 'utf8', | ||
mode: 0644, | ||
mode: parseInt('644',8) | ||
}); | ||
@@ -83,7 +87,8 @@ w.on('end', process.exit); | ||
line = prefix + punycode.toASCII(line); | ||
if (line.substr(0,1) == '!') | ||
if (line.substr(0,1) == '!') { | ||
index[line.substr(1)] = false; | ||
else | ||
} else { | ||
index[line] = true; | ||
} | ||
} | ||
@@ -113,4 +118,8 @@ | ||
*/ | ||
if (!domain) return null; | ||
if (domain.match(/^\./)) return null; | ||
if (!domain) { | ||
return null; | ||
} | ||
if (domain.match(/^\./)) { | ||
return null; | ||
} | ||
@@ -117,0 +126,0 @@ domain = domain.toLowerCase(); |
@@ -22,6 +22,7 @@ /* | ||
/*jshint regexp:false */ | ||
'use strict'; | ||
var net = require('net'); | ||
var urlParse = require('url').parse; | ||
var pubsuffix = require('./pubsuffix'); | ||
var Store = require('./store').Store; | ||
@@ -74,3 +75,4 @@ var punycode; | ||
var MONTH_TO_NUM = { | ||
jan:0, feb:1, mar:2, apr:3, may:4, jun:5, jul:6, aug:7, sep:8, oct:9, nov:10, dec:11 | ||
jan:0, feb:1, mar:2, apr:3, may:4, jun:5, | ||
jul:6, aug:7, sep:8, oct:9, nov:10, dec:11 | ||
}; | ||
@@ -84,8 +86,6 @@ var NUM_TO_MONTH = [ | ||
var YEAR = /^([1-9][0-9]{1,3})$/; // 2 to 4 digits (will check range when parsing) | ||
var YEAR = /^([1-9][0-9]{1,3})$/; // 2 to 4 digits | ||
var MAX_TIME = 2147483647000; // 31-bit max | ||
var MAX_DATE = new Date(CookieJar.MAX_TIME); // 31-bit max | ||
var MIN_TIME = 0; // 31-bit min | ||
var MIN_DATE = new Date(CookieJar.MIN_TIME); // 31-bit min | ||
@@ -95,3 +95,5 @@ | ||
function parseDate(str,strict) { | ||
if (!str) return; | ||
if (!str) { | ||
return; | ||
} | ||
var found_time, found_dom, found_month, found_year; | ||
@@ -104,3 +106,5 @@ | ||
var tokens = str.split(DATE_DELIM); | ||
if (!tokens) return; | ||
if (!tokens) { | ||
return; | ||
} | ||
@@ -112,3 +116,5 @@ var date = new Date(); | ||
var token = tokens[i].trim(); | ||
if (!token.length) continue; | ||
if (!token.length) { | ||
continue; | ||
} | ||
@@ -177,9 +183,11 @@ var result; | ||
*/ | ||
if (70 <= year && year <= 99) | ||
if (70 <= year && year <= 99) { | ||
year += 1900; | ||
else if (0 <= year && year <= 69) | ||
} else if (0 <= year && year <= 69) { | ||
year += 2000; | ||
} | ||
if (year < 1601) | ||
if (year < 1601) { | ||
return; // 5. ... the year-value is less than 1601 | ||
} | ||
@@ -213,8 +221,11 @@ found_year = true; | ||
function canonicalDomain(str) { | ||
if (str == null) return null; | ||
if (str == null) { | ||
return null; | ||
} | ||
str = str.trim().replace(/^\./,''); // S4.1.2.3 & S5.2.3: ignore leading . | ||
// convert to IDN if any non-ASCII characters | ||
if (punycode && /[^\u0001-\u007f]/.test(str)) | ||
if (punycode && /[^\u0001-\u007f]/.test(str)) { | ||
str = punycode.toASCII(str); | ||
} | ||
@@ -226,3 +237,5 @@ return str.toLowerCase(); | ||
function domainMatch(str, domStr, canonicalize) { | ||
if (str == null || domStr == null) return null; | ||
if (str == null || domStr == null) { | ||
return null; | ||
} | ||
if (canonicalize !== false) { | ||
@@ -238,3 +251,5 @@ str = canonicalDomain(str); | ||
*/ | ||
if (str == domStr) return true; | ||
if (str == domStr) { | ||
return true; | ||
} | ||
@@ -244,16 +259,24 @@ /* "All of the following [three] conditions hold:" (order adjusted from the RFC) */ | ||
/* "* The string is a host name (i.e., not an IP address)." */ | ||
if (net.isIP(str)) return false; | ||
if (net.isIP(str)) { | ||
return false; | ||
} | ||
/* "* The domain string is a suffix of the string" */ | ||
var idx = str.indexOf(domStr); | ||
if (idx <= 0) return false; // it's a non-match (-1) or prefix (0) | ||
if (idx <= 0) { | ||
return false; // it's a non-match (-1) or prefix (0) | ||
} | ||
// e.g "a.b.c".indexOf("b.c") === 2 | ||
// 5 === 3+2 | ||
if (str.length !== domStr.length + idx) // it's not a suffix | ||
if (str.length !== domStr.length + idx) { // it's not a suffix | ||
return false; | ||
} | ||
/* "* The last character of the string that is not included in the domain | ||
* string is a %x2E (".") character." */ | ||
if (str.substr(idx-1,1) !== '.') return false; | ||
if (str.substr(idx-1,1) !== '.') { | ||
return false; | ||
} | ||
return true; | ||
@@ -274,10 +297,16 @@ } | ||
// a %x2F ("/") character, output %x2F ("/") and skip the remaining steps. | ||
if (!path || path.substr(0,1) !== "/") return "/"; | ||
if (!path || path.substr(0,1) !== "/") { | ||
return "/"; | ||
} | ||
// "3. If the uri-path contains no more than one %x2F ("/") character, output | ||
// %x2F ("/") and skip the remaining step." | ||
if (path === "/") return path; | ||
if (path === "/") { | ||
return path; | ||
} | ||
var rightSlash = path.lastIndexOf("/"); | ||
if (rightSlash === 0) return "/"; | ||
if (rightSlash === 0) { | ||
return "/"; | ||
} | ||
@@ -295,4 +324,5 @@ // "4. Output the characters of the uri-path from the first character up to, | ||
// "o The cookie-path and the request-path are identical." | ||
if (cookiePath === reqPath) | ||
if (cookiePath === reqPath) { | ||
return true; | ||
} | ||
@@ -303,4 +333,5 @@ var idx = reqPath.indexOf(cookiePath); | ||
// character of the cookie-path is %x2F ("/")." | ||
if (cookiePath.substr(-1) === "/") | ||
if (cookiePath.substr(-1) === "/") { | ||
return true; | ||
} | ||
@@ -310,4 +341,5 @@ // " o The cookie-path is a prefix of the request-path, and the first | ||
// is a %x2F ("/") character." | ||
if (reqPath.substr(cookiePath.length,1) === "/") | ||
if (reqPath.substr(cookiePath.length,1) === "/") { | ||
return true; | ||
} | ||
} | ||
@@ -325,3 +357,5 @@ | ||
if (semiColonCheck) { | ||
if (strict) return; | ||
if (strict) { | ||
return; | ||
} | ||
str = str.slice(0, semiColonCheck.index); | ||
@@ -337,3 +371,5 @@ } | ||
// constraints as well as trimming any whitespace. | ||
if (!result) return; | ||
if (!result) { | ||
return; | ||
} | ||
@@ -344,3 +380,5 @@ var c = new Cookie(); | ||
if (firstSemi === -1) return c; | ||
if (firstSemi === -1) { | ||
return c; | ||
} | ||
@@ -354,3 +392,5 @@ // S5.2.3 "unparsed-attributes consist of the remainder of the set-cookie-string | ||
// steps." | ||
if (unparsed.length === 0) return c; | ||
if (unparsed.length === 0) { | ||
return c; | ||
} | ||
@@ -369,3 +409,5 @@ /* | ||
if (strict && !EXTENSION_AV.test(av)) return; | ||
if (strict && !EXTENSION_AV.test(av)) { | ||
return; | ||
} | ||
@@ -383,11 +425,13 @@ var av_sep = av.indexOf('='); | ||
av_key = av_key.trim().toLowerCase(); | ||
if (av_value) av_value = av_value.trim(); | ||
if (av_value) { | ||
av_value = av_value.trim(); | ||
} | ||
switch(av_key) { | ||
case 'expires': // S5.2.1 | ||
if (!av_value) { if(strict){return}else{break;} } | ||
if (!av_value) {if(strict){return;}else{break;} } | ||
var exp = parseDate(av_value,strict); | ||
// "If the attribute-value failed to parse as a cookie date, ignore the | ||
// cookie-av." | ||
if (exp == null) { if(strict){return}else{break;} } | ||
if (exp == null) { if(strict){return;}else{break;} } | ||
c.expires = exp; | ||
@@ -399,9 +443,11 @@ // over and underflow not realistically a concern: V8's getTime() seems to | ||
case 'max-age': // S5.2.2 | ||
if (!av_value) { if(strict){return}else{break;} } | ||
if (!av_value) { if(strict){return;}else{break;} } | ||
// "If the first character of the attribute-value is not a DIGIT or a "-" | ||
// character ...[or]... If the remainder of attribute-value contains a | ||
// non-DIGIT character, ignore the cookie-av." | ||
if (!/^-?[0-9]+$/.test(av_value)) { if(strict){return}else{break;} } | ||
if (!/^-?[0-9]+$/.test(av_value)) { if(strict){return;}else{break;} } | ||
var delta = parseInt(av_value,10); | ||
if (strict && delta <= 0) return; // S4.1.1 | ||
if (strict && delta <= 0) { | ||
return; // S4.1.1 | ||
} | ||
// "If delta-seconds is less than or equal to zero (0), let expiry-time | ||
@@ -415,7 +461,7 @@ // be the earliest representable date and time." | ||
// the user agent SHOULD ignore the cookie-av entirely." | ||
if (!av_value) { if(strict){return}else{break;} } | ||
if (!av_value) { if(strict){return;}else{break;} } | ||
// S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E | ||
// (".") character." | ||
var domain = av_value.trim().replace(/^\./,''); | ||
if (!domain) { if(strict){return}else{break;} } // see "is empty" above | ||
if (!domain) { if(strict){return;}else{break;} } // see "is empty" above | ||
// "Convert the cookie-domain to lower case." | ||
@@ -436,3 +482,5 @@ c.domain = domain.toLowerCase(); | ||
*/ | ||
if (!av_value || av_value.substr(0,1) != "/") { if(strict){return}else{break;} } | ||
if (!av_value || av_value.substr(0,1) != "/") { | ||
if(strict){return;}else{break;} | ||
} | ||
c.path = av_value; | ||
@@ -447,3 +495,3 @@ break; | ||
*/ | ||
if (av_value != null) { if(strict){return} } | ||
if (av_value != null) { if(strict){return;} } | ||
c.secure = true; | ||
@@ -453,3 +501,3 @@ break; | ||
case 'httponly': // S5.2.6 -- effectively the same as 'secure' | ||
if (av_value != null) { if(strict){return} } | ||
if (av_value != null) { if(strict){return;} } | ||
c.httpOnly = true; | ||
@@ -471,3 +519,5 @@ break; | ||
function fromJSON(str) { | ||
if (!str) return null; | ||
if (!str) { | ||
return null; | ||
} | ||
@@ -484,3 +534,5 @@ var obj; | ||
var prop = cookieProperties[i]; | ||
if (obj[prop] == null) continue; | ||
if (obj[prop] == null) { | ||
continue; | ||
} | ||
if (prop === 'expires' || | ||
@@ -515,3 +567,5 @@ prop === 'creation' || | ||
var deltaLen = (b.path ? b.path.length : 0) - (a.path ? a.path.length : 0); | ||
if (deltaLen !== 0) return deltaLen; | ||
if (deltaLen !== 0) { | ||
return deltaLen; | ||
} | ||
// ascending for time: a CMP b | ||
@@ -526,4 +580,8 @@ return (a.creation ? a.creation.getTime() : MAX_TIME) - | ||
var pubSuf = pubsuffix.getPublicSuffix(domain); | ||
if (!pubSuf) return null; | ||
if (pubSuf == domain) return [domain]; | ||
if (!pubSuf) { | ||
return null; | ||
} | ||
if (pubSuf == domain) { | ||
return [domain]; | ||
} | ||
@@ -544,10 +602,14 @@ var prefix = domain.slice(0,-(pubSuf.length+1)); // ".example.com" | ||
function permutePath(path) { | ||
var origPath = path; | ||
if (path === '/') return ['/']; | ||
if (path.lastIndexOf('/') === path.length-1) | ||
if (path === '/') { | ||
return ['/']; | ||
} | ||
if (path.lastIndexOf('/') === path.length-1) { | ||
path = path.substr(0,path.length-1); | ||
} | ||
var permutations = [path]; | ||
while (path.length > 1) { | ||
var lindex = path.lastIndexOf('/'); | ||
if (lindex === 0) break; | ||
if (lindex === 0) { | ||
break; | ||
} | ||
path = path.substr(0,lindex); | ||
@@ -562,3 +624,5 @@ permutations.push(path); | ||
function Cookie (opts) { | ||
if (typeof opts !== "object") return; | ||
if (typeof opts !== "object") { | ||
return; | ||
} | ||
Object.keys(opts).forEach(function (key) { | ||
@@ -593,3 +657,5 @@ if (Cookie.prototype.hasOwnProperty(key)) { | ||
var cookieProperties = Object.freeze(Object.keys(Cookie.prototype).map(function(p) { | ||
if (p instanceof Function) return; | ||
if (p instanceof Function) { | ||
return; | ||
} | ||
return p; | ||
@@ -609,18 +675,24 @@ })); | ||
Cookie.prototype.validate = function validate() { | ||
if (!COOKIE_OCTETS.test(this.value)) | ||
if (!COOKIE_OCTETS.test(this.value)) { | ||
return false; | ||
if (this.expires != Infinity && !(this.expires instanceof Date) && !parseDate(this.expires,true)) | ||
} | ||
if (this.expires != Infinity && !(this.expires instanceof Date) && !parseDate(this.expires,true)) { | ||
return false; | ||
if (this.maxAge != null && this.maxAge <= 0) | ||
} | ||
if (this.maxAge != null && this.maxAge <= 0) { | ||
return false; // "Max-Age=" non-zero-digit *DIGIT | ||
if (this.path != null && !PATH_VALUE.test(this.path)) | ||
} | ||
if (this.path != null && !PATH_VALUE.test(this.path)) { | ||
return false; | ||
} | ||
var cdomain = this.cdomain(); | ||
if (cdomain) { | ||
if (cdomain.match(/\.$/)) | ||
if (cdomain.match(/\.$/)) { | ||
return false; // S4.1.2.3 suggests that this is bad. domainMatch() tests confirm this | ||
} | ||
var suffix = pubsuffix.getPublicSuffix(cdomain); | ||
if (suffix == null) // it's a public suffix | ||
if (suffix == null) { // it's a public suffix | ||
return false; | ||
} | ||
} | ||
@@ -631,11 +703,15 @@ return true; | ||
Cookie.prototype.setExpires = function setExpires(exp) { | ||
if (exp instanceof Date) this.expires = exp; | ||
else this.expires = parseDate(exp) || "Infinity"; | ||
if (exp instanceof Date) { | ||
this.expires = exp; | ||
} else { | ||
this.expires = parseDate(exp) || "Infinity"; | ||
} | ||
}; | ||
Cookie.prototype.setMaxAge = function setMaxAge(age) { | ||
if (age === Infinity || age === -Infinity) | ||
if (age === Infinity || age === -Infinity) { | ||
this.maxAge = age.toString(); // so JSON.stringify() works | ||
else | ||
} else { | ||
this.maxAge = age; | ||
} | ||
}; | ||
@@ -646,3 +722,5 @@ | ||
var val = this.value; | ||
if (val == null) val = ''; | ||
if (val == null) { | ||
val = ''; | ||
} | ||
return this.key+'='+val; | ||
@@ -656,18 +734,26 @@ }; | ||
if (this.expires != Infinity) { | ||
if (this.expires instanceof Date) | ||
if (this.expires instanceof Date) { | ||
str += '; Expires='+formatDate(this.expires); | ||
else | ||
} else { | ||
str += '; Expires='+this.expires; | ||
} | ||
} | ||
if (this.maxAge != null && this.maxAge != Infinity) | ||
if (this.maxAge != null && this.maxAge != Infinity) { | ||
str += '; Max-Age='+this.maxAge; | ||
} | ||
if (this.domain && !this.hostOnly) | ||
if (this.domain && !this.hostOnly) { | ||
str += '; Domain='+this.domain; | ||
if (this.path) | ||
} | ||
if (this.path) { | ||
str += '; Path='+this.path; | ||
} | ||
if (this.secure) str += '; Secure'; | ||
if (this.httpOnly) str += '; HttpOnly'; | ||
if (this.secure) { | ||
str += '; Secure'; | ||
} | ||
if (this.httpOnly) { | ||
str += '; HttpOnly'; | ||
} | ||
if (this.extensions) { | ||
@@ -702,4 +788,5 @@ this.extensions.forEach(function(ext) { | ||
if (expires == Infinity) | ||
if (expires == Infinity) { | ||
return Infinity; | ||
} | ||
@@ -721,3 +808,5 @@ return expires.getTime() - (now || Date.now()); | ||
if (this.expires == Infinity) return Infinity; | ||
if (this.expires == Infinity) { | ||
return Infinity; | ||
} | ||
return this.expires.getTime(); | ||
@@ -730,5 +819,9 @@ }; | ||
var millisec = this.expiryTime(now); | ||
if (millisec == Infinity) return new Date(MAX_TIME); | ||
else if (millisec == -Infinity) return new Date(MIN_TIME); | ||
else return new Date(millisec); | ||
if (millisec == Infinity) { | ||
return new Date(MAX_TIME); | ||
} else if (millisec == -Infinity) { | ||
return new Date(MIN_TIME); | ||
} else { | ||
return new Date(millisec); | ||
} | ||
}; | ||
@@ -744,3 +837,5 @@ | ||
Cookie.prototype.canonicalizedDomain = function canonicalizedDomain() { | ||
if (this.domain == null) return null; | ||
if (this.domain == null) { | ||
return null; | ||
} | ||
return canonicalDomain(this.domain); | ||
@@ -752,3 +847,6 @@ }; | ||
function CookieJar(store, rejectPublicSuffixes) { | ||
if (rejectPublicSuffixes != null) this.rejectPublicSuffixes = rejectPublicSuffixes; | ||
if (rejectPublicSuffixes != null) { | ||
this.rejectPublicSuffixes = rejectPublicSuffixes; | ||
} | ||
if (!store) { | ||
@@ -762,4 +860,6 @@ memstore = memstore || require('./memstore'); | ||
CookieJar.prototype.rejectPublicSuffixes = true; | ||
var CAN_BE_SYNC = []; | ||
CookieJar.prototype.setCookie = function setCookie(cookie, url, options, cb) { | ||
CAN_BE_SYNC.push('setCookie'); | ||
CookieJar.prototype.setCookie = function(cookie, url, options, cb) { | ||
var err; | ||
@@ -775,4 +875,5 @@ var context = (url instanceof Object) ? url : urlParse(url); | ||
// S5.3 step 1 | ||
if (!(cookie instanceof Cookie)) | ||
if (!(cookie instanceof Cookie)) { | ||
cookie = Cookie.parse(cookie, options.strict === true); | ||
} | ||
if (!cookie) { | ||
@@ -806,4 +907,5 @@ err = new Error("Cookie failed to parse"); | ||
if (cookie.hostOnly == null) // don't reset if already set | ||
if (cookie.hostOnly == null) { // don't reset if already set | ||
cookie.hostOnly = false; | ||
} | ||
@@ -821,4 +923,5 @@ } else { | ||
} else { | ||
if (cookie.path.length > 1 && cookie.path.substr(-1) == '/') | ||
if (cookie.path.length > 1 && cookie.path.substr(-1) == '/') { | ||
cookie.path = cookie.path.slice(0,-1); | ||
} | ||
} | ||
@@ -838,3 +941,3 @@ | ||
if (!store.updateCookie) { | ||
store.updateCookie = function stubUpdateCookie(oldCookie, newCookie, cb) { | ||
store.updateCookie = function(oldCookie, newCookie, cb) { | ||
this.putCookie(newCookie, cb); | ||
@@ -844,8 +947,13 @@ }; | ||
store.findCookie(cookie.domain, cookie.path, cookie.key, function(err,oldCookie) { | ||
if (err) return cb(err); | ||
function withCookie(err, oldCookie) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
var next = function(err) { | ||
if (err) return cb(err); | ||
else cb(null, cookie); | ||
if (err) { | ||
return cb(err); | ||
} else { | ||
cb(null, cookie); | ||
} | ||
}; | ||
@@ -869,7 +977,10 @@ | ||
} | ||
}); | ||
} | ||
store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie); | ||
}; | ||
// RFC6365 S5.4 | ||
CookieJar.prototype.getCookies = function getCookies(url, options, cb) { | ||
CAN_BE_SYNC.push('getCookies'); | ||
CookieJar.prototype.getCookies = function(url, options, cb) { | ||
var context = (url instanceof Object) ? url : urlParse(url); | ||
@@ -892,3 +1003,5 @@ if (options instanceof Function) { | ||
var http = options.http; | ||
if (http == null) http = true; | ||
if (http == null) { | ||
http = true; | ||
} | ||
@@ -908,20 +1021,27 @@ var now = options.now || Date.now(); | ||
if (c.hostOnly) { | ||
if (c.domain != host) return false; | ||
if (c.domain != host) { | ||
return false; | ||
} | ||
} else { | ||
if (!domainMatch(host, c.domain, false)) return false; | ||
if (!domainMatch(host, c.domain, false)) { | ||
return false; | ||
} | ||
} | ||
// "The request-uri's path path-matches the cookie's path." | ||
if (!allPaths && !pathMatch(path, c.path)) | ||
if (!allPaths && !pathMatch(path, c.path)) { | ||
return false; | ||
} | ||
// "If the cookie's secure-only-flag is true, then the request-uri's | ||
// scheme must denote a "secure" protocol" | ||
if (c.secure && !secure) | ||
if (c.secure && !secure) { | ||
return false; | ||
} | ||
// "If the cookie's http-only-flag is true, then exclude the cookie if the | ||
// cookie-string is being generated for a "non-HTTP" API" | ||
if (c.httpOnly && !http) | ||
if (c.httpOnly && !http) { | ||
return false; | ||
} | ||
@@ -939,3 +1059,5 @@ // deferred from S5.3 | ||
store.findCookies(host, allPaths ? null : path, function(err,cookies) { | ||
if (err) return cb(err); | ||
if (err) { | ||
return cb(err); | ||
} | ||
@@ -945,8 +1067,11 @@ cookies = cookies.filter(matchingCookie); | ||
// sorting of S5.4 part 2 | ||
if (options.sort !== false) | ||
if (options.sort !== false) { | ||
cookies = cookies.sort(cookieCompare); | ||
} | ||
// S5.4 part 3 | ||
var now = new Date(); | ||
cookies.forEach(function(c) { c.lastAccessed = now }); | ||
cookies.forEach(function(c) { | ||
c.lastAccessed = now; | ||
}); | ||
// TODO persist lastAccessed | ||
@@ -958,2 +1083,3 @@ | ||
CAN_BE_SYNC.push('getCookieString'); | ||
CookieJar.prototype.getCookieString = function(/*..., cb*/) { | ||
@@ -963,4 +1089,9 @@ var args = Array.prototype.slice.call(arguments,0); | ||
var next = function(err,cookies) { | ||
if (err) cb(err); | ||
else cb(null, cookies.map(function(c){return c.cookieString()}).join('; ')); | ||
if (err) { | ||
cb(err); | ||
} else { | ||
cb(null, cookies.map(function(c){ | ||
return c.cookieString(); | ||
}).join('; ')); | ||
} | ||
}; | ||
@@ -971,2 +1102,3 @@ args.push(next); | ||
CAN_BE_SYNC.push('getSetCookieStrings'); | ||
CookieJar.prototype.getSetCookieStrings = function(/*..., cb*/) { | ||
@@ -976,4 +1108,9 @@ var args = Array.prototype.slice.call(arguments,0); | ||
var next = function(err,cookies) { | ||
if (err) cb(err); | ||
else cb(null, cookies.map(function(c){return c.toString()})); | ||
if (err) { | ||
cb(err); | ||
} else { | ||
cb(null, cookies.map(function(c){ | ||
return c.toString(); | ||
})); | ||
} | ||
}; | ||
@@ -984,7 +1121,33 @@ args.push(next); | ||
// Use a closure to provide a true imperative API for synchronous stores. | ||
function syncWrap(method) { | ||
return function() { | ||
if (!this.store.synchronous) { | ||
throw new Error('CookieJar store is not synchronous; use async API instead.'); | ||
} | ||
var args = Array.prototype.slice.call(arguments); | ||
var syncErr, syncResult; | ||
args.push(function syncCb(err, result) { | ||
syncErr = err; | ||
syncResult = result; | ||
}); | ||
this[method].apply(this, args); | ||
if (syncErr) { | ||
throw syncErr; | ||
} | ||
return syncResult; | ||
}; | ||
} | ||
// wrap all declared CAN_BE_SYNC methods in the sync wrapper | ||
CAN_BE_SYNC.forEach(function(method) { | ||
CookieJar.prototype[method+'Sync'] = syncWrap(method); | ||
}); | ||
module.exports = { | ||
CookieJar: CookieJar, | ||
Cookie: Cookie, | ||
Store: Store, | ||
parseDate: parseDate, | ||
@@ -991,0 +1154,0 @@ formatDate: formatDate, |
@@ -0,2 +1,4 @@ | ||
'use strict'; | ||
var tough = require('./cookie'); | ||
var Store = require('./store').Store; | ||
var permuteDomain = tough.permuteDomain; | ||
@@ -7,21 +9,30 @@ var permutePath = tough.permutePath; | ||
function MemoryCookieStore() { | ||
Store.call(this); | ||
this.idx = {}; | ||
} | ||
module.exports.MemoryCookieStore = MemoryCookieStore; | ||
util.inherits(MemoryCookieStore, Store); | ||
exports.MemoryCookieStore = MemoryCookieStore; | ||
MemoryCookieStore.prototype.idx = null; | ||
MemoryCookieStore.prototype.synchronous = true; | ||
// force a default depth: | ||
MemoryCookieStore.prototype.inspect = function inspect() { | ||
MemoryCookieStore.prototype.inspect = function() { | ||
return "{ idx: "+util.inspect(this.idx, false, 2)+' }'; | ||
}; | ||
MemoryCookieStore.prototype.findCookie = function findCookie(domain, path, key, cb) { | ||
if (!this.idx[domain]) return cb(null,undefined); | ||
if (!this.idx[domain][path]) return cb(null,undefined); | ||
MemoryCookieStore.prototype.findCookie = function(domain, path, key, cb) { | ||
if (!this.idx[domain]) { | ||
return cb(null,undefined); | ||
} | ||
if (!this.idx[domain][path]) { | ||
return cb(null,undefined); | ||
} | ||
return cb(null,this.idx[domain][path][key]||null); | ||
}; | ||
MemoryCookieStore.prototype.findCookies = function findCookies(domain, path, cb) { | ||
MemoryCookieStore.prototype.findCookies = function(domain, path, cb) { | ||
var results = []; | ||
if (!domain) return cb(null,[]); | ||
if (!domain) { | ||
return cb(null,[]); | ||
} | ||
@@ -43,3 +54,5 @@ var pathMatcher; | ||
var pathIndex = domainIndex['/']; | ||
if (!pathIndex) return; | ||
if (!pathIndex) { | ||
return; | ||
} | ||
for (var key in pathIndex) { | ||
@@ -55,3 +68,5 @@ results.push(pathIndex[key]); | ||
var pathIndex = domainIndex[curPath]; | ||
if (!pathIndex) return; | ||
if (!pathIndex) { | ||
return; | ||
} | ||
for (var key in pathIndex) { | ||
@@ -68,3 +83,5 @@ results.push(pathIndex[key]); | ||
var domainIndex = idx[curDomain]; | ||
if (!domainIndex) return; | ||
if (!domainIndex) { | ||
return; | ||
} | ||
pathMatcher(domainIndex); | ||
@@ -76,5 +93,9 @@ }); | ||
MemoryCookieStore.prototype.putCookie = function putCookie(cookie, cb) { | ||
if (!this.idx[cookie.domain]) this.idx[cookie.domain] = {}; | ||
if (!this.idx[cookie.domain][cookie.path]) this.idx[cookie.domain][cookie.path] = {}; | ||
MemoryCookieStore.prototype.putCookie = function(cookie, cb) { | ||
if (!this.idx[cookie.domain]) { | ||
this.idx[cookie.domain] = {}; | ||
} | ||
if (!this.idx[cookie.domain][cookie.path]) { | ||
this.idx[cookie.domain][cookie.path] = {}; | ||
} | ||
this.idx[cookie.domain][cookie.path][cookie.key] = cookie; | ||
@@ -81,0 +102,0 @@ cb(null); |
@@ -16,3 +16,3 @@ { | ||
], | ||
"version": "0.11.0", | ||
"version": "0.12.0", | ||
"homepage": "https://github.com/goinstant/node-cookie", | ||
@@ -19,0 +19,0 @@ "repository": { |
@@ -13,4 +13,4 @@ [RFC6265](http://tools.ietf.org/html/rfc6265) Cookies and CookieJar for Node.js | ||
``` javascript | ||
var cookies = require('tough-cookie'); // note: not 'cookie', 'cookies' or 'node-cookie' | ||
var Cookie = cookies.Cookie; | ||
var tough = require('tough-cookie'); // note: not 'cookie', 'cookies' or 'node-cookie' | ||
var Cookie = tough.Cookie; | ||
var cookie = Cookie.parse(header); | ||
@@ -20,3 +20,3 @@ cookie.value = 'somethingdifferent'; | ||
var cookiejar = new cookies.CookieJar(); | ||
var cookiejar = new tough.CookieJar(); | ||
cookiejar.setCookie(cookie, 'http://currentdomain.example.com/path', cb); | ||
@@ -41,4 +41,4 @@ // ... | ||
cookies | ||
======= | ||
tough | ||
===== | ||
@@ -265,2 +265,7 @@ Functions on the module you get from `require('tough-cookie')`. All can be used as pure functions and don't need to be "bound". | ||
.setCookieSync(cookieOrString, currentUrl, [{options}]) | ||
------------------------------------------------------- | ||
Synchronous version of `setCookie`; only works with synchronous stores (e.g. the default `MemoryCookieStore`). | ||
.storeCookie(cookie, [{options},] cb(err,cookie)) | ||
@@ -288,2 +293,7 @@ ------------------------------------------------- | ||
.getCookiesSync(currentUrl, [{options}]) | ||
---------------------------------------- | ||
Synchronous version of `getCookies`; only works with synchronous stores (e.g. the default `MemoryCookieStore`). | ||
.getCookieString(...) | ||
@@ -294,7 +304,22 @@ --------------------- | ||
.getCookieStringSync(...) | ||
------------------------- | ||
Synchronous version of `getCookieString`; only works with synchronous stores (e.g. the default `MemoryCookieStore`). | ||
.getSetCookieStrings(...) | ||
------------------------- | ||
Accepts the same options as `.getCookies()` but passes an array of strings suitable for Set-Cookie headers (rather than an array of `Cookie`s) to the callback. Simply maps the cookie array via `.toString()`. | ||
Returns an array of strings suitable for **Set-Cookie** headers. Accepts the same options as `.getCookies()`. Simply maps the cookie array via `.toString()`. | ||
.getSetCookieStringsSync(...) | ||
----------------------------- | ||
Synchronous version of `getSetCookieStrings`; only works with synchronous stores (e.g. the default `MemoryCookieStore`). | ||
Store | ||
===== | ||
Base class for CookieJar stores. | ||
# CookieStore API | ||
@@ -304,2 +329,4 @@ | ||
Stores should inherit from the base `Store` class, which is available as `require('tough-cookie').Store`. Stores are asynchronous by default, but if `store.synchronous` is set, then the `*Sync` methods on the CookieJar can be used. | ||
All `domain` parameters will have been normalized before calling. | ||
@@ -306,0 +333,0 @@ |
203
test.js
@@ -21,3 +21,3 @@ /* | ||
*/ | ||
'use strict'; | ||
var vows = require('vows'); | ||
@@ -35,3 +35,3 @@ var assert = require('assert'); | ||
var theVows = { }; | ||
var keys = Object.keys(table).forEach(function(date) { | ||
Object.keys(table).forEach(function(date) { | ||
var expect = table[date]; | ||
@@ -74,3 +74,3 @@ theVows[date] = function() { | ||
var atNow = Date.now(); | ||
function at(offset) { return {now: new Date(atNow+offset)} } | ||
function at(offset) { return {now: new Date(atNow+offset)}; } | ||
@@ -283,3 +283,3 @@ vows.describe('Cookie Jar') | ||
"default TTL": { | ||
topic: function() { return new Cookie() }, | ||
topic: function() { return new Cookie(); }, | ||
"is Infinite-future": function(c) { assert.equal(c.TTL(), Infinity) }, | ||
@@ -833,6 +833,9 @@ "is a 'session' cookie": function(c) { assert.ok(!c.isPersistent()) }, | ||
"setup ok": function(err,cj,results) { | ||
assert.ok(1); | ||
assert.ok(!err); | ||
assert.ok(cj); | ||
assert.ok(results); | ||
}, | ||
"then retrieving for http://nodejs.org": { | ||
topic: function(cj,results) { | ||
topic: function(cj,oldResults) { | ||
assert.ok(oldResults); | ||
cj.getCookies('http://nodejs.org',this.callback); | ||
@@ -847,3 +850,4 @@ }, | ||
"then retrieving for https://example.com": { | ||
topic: function(cj,results) { | ||
topic: function(cj,oldResults) { | ||
assert.ok(oldResults); | ||
cj.getCookies('https://example.com',{secure:true},this.callback); | ||
@@ -857,3 +861,4 @@ }, | ||
"then retrieving for https://example.com (missing options)": { | ||
topic: function(cj,results) { | ||
topic: function(cj,oldResults) { | ||
assert.ok(oldResults); | ||
cj.getCookies('https://example.com',this.callback); | ||
@@ -867,3 +872,4 @@ }, | ||
"then retrieving for http://example.com": { | ||
topic: function(cj,results) { | ||
topic: function(cj,oldResults) { | ||
assert.ok(oldResults); | ||
cj.getCookies('http://example.com',this.callback); | ||
@@ -877,3 +883,4 @@ }, | ||
"then retrieving for http://EXAMPlE.com": { | ||
topic: function(cj,results) { | ||
topic: function(cj,oldResults) { | ||
assert.ok(oldResults); | ||
cj.getCookies('http://EXAMPlE.com',this.callback); | ||
@@ -887,3 +894,4 @@ }, | ||
"then retrieving for http://example.com, non-HTTP": { | ||
topic: function(cj,results) { | ||
topic: function(cj,oldResults) { | ||
assert.ok(oldResults); | ||
cj.getCookies('http://example.com',{http:false},this.callback); | ||
@@ -897,3 +905,4 @@ }, | ||
"then retrieving for http://example.com/foo/bar": { | ||
topic: function(cj,results) { | ||
topic: function(cj,oldResults) { | ||
assert.ok(oldResults); | ||
cj.getCookies('http://example.com/foo/bar',this.callback); | ||
@@ -907,3 +916,4 @@ }, | ||
"then retrieving for http://example.com as a string": { | ||
topic: function(cj,results) { | ||
topic: function(cj,oldResults) { | ||
assert.ok(oldResults); | ||
cj.getCookieString('http://example.com',this.callback); | ||
@@ -916,3 +926,4 @@ }, | ||
"then retrieving for http://example.com as a set-cookie header": { | ||
topic: function(cj,results) { | ||
topic: function(cj,oldResults) { | ||
assert.ok(oldResults); | ||
cj.getSetCookieStrings('http://example.com',this.callback); | ||
@@ -928,3 +939,4 @@ }, | ||
"then retrieving for http://www.example.com/": { | ||
topic: function(cj,results) { | ||
topic: function(cj,oldResults) { | ||
assert.ok(oldResults); | ||
cj.getCookies('http://www.example.com/foo/bar',this.callback); | ||
@@ -997,2 +1009,3 @@ }, | ||
var next = function (err,c) { | ||
c = null; | ||
return cb(err,cj); | ||
@@ -1005,2 +1018,3 @@ }; | ||
assert.ok(!err); | ||
assert.ok(cj); | ||
}, | ||
@@ -1010,3 +1024,7 @@ "but when trying to overwrite": { | ||
var cb = this.callback; | ||
cj.setCookie('k=12; Domain=example.ca; Path=/','http://example.ca',{http:false},function(err,c) {cb(null,err)}); | ||
var next = function(err,c) { | ||
c = null; | ||
cb(null,err); | ||
}; | ||
cj.setCookie('k=12; Domain=example.ca; Path=/','http://example.ca',{http:false},next); | ||
}, | ||
@@ -1471,2 +1489,155 @@ "it's an error": function(err) { | ||
}) | ||
.addBatch({ | ||
"Synchronous CookieJar": { | ||
"setCookieSync": { | ||
topic: function() { | ||
var jar = new CookieJar(); | ||
var cookie = Cookie.parse("a=b; Domain=example.com; Path=/"); | ||
cookie = jar.setCookieSync(cookie, 'http://example.com/index.html'); | ||
return cookie; | ||
}, | ||
"returns a copy of the cookie": function(cookie) { | ||
assert.instanceOf(cookie, Cookie); | ||
} | ||
}, | ||
"setCookieSync strict parse error": { | ||
topic: function() { | ||
var jar = new CookieJar(); | ||
var opts = { strict: true }; | ||
try { | ||
jar.setCookieSync("farbe=weiß", 'http://example.com/index.html', opts); | ||
return false; | ||
} catch (e) { | ||
return e; | ||
} | ||
}, | ||
"throws the error": function(err) { | ||
assert.instanceOf(err, Error); | ||
assert.equal(err.message, "Cookie failed to parse"); | ||
} | ||
}, | ||
"getCookiesSync": { | ||
topic: function() { | ||
var jar = new CookieJar(); | ||
var url = 'http://example.com/index.html'; | ||
jar.setCookieSync("a=b; Domain=example.com; Path=/", url); | ||
jar.setCookieSync("c=d; Domain=example.com; Path=/", url); | ||
return jar.getCookiesSync(url); | ||
}, | ||
"returns the cookie array": function(err, cookies) { | ||
assert.ok(!err); | ||
assert.ok(Array.isArray(cookies)); | ||
assert.lengthOf(cookies, 2); | ||
cookies.forEach(function(cookie) { | ||
assert.instanceOf(cookie, Cookie); | ||
}); | ||
} | ||
}, | ||
"getCookieStringSync": { | ||
topic: function() { | ||
var jar = new CookieJar(); | ||
var url = 'http://example.com/index.html'; | ||
jar.setCookieSync("a=b; Domain=example.com; Path=/", url); | ||
jar.setCookieSync("c=d; Domain=example.com; Path=/", url); | ||
return jar.getCookieStringSync(url); | ||
}, | ||
"returns the cookie header string": function(err, str) { | ||
assert.ok(!err); | ||
assert.typeOf(str, 'string'); | ||
} | ||
}, | ||
"getSetCookieStringsSync": { | ||
topic: function() { | ||
var jar = new CookieJar(); | ||
var url = 'http://example.com/index.html'; | ||
jar.setCookieSync("a=b; Domain=example.com; Path=/", url); | ||
jar.setCookieSync("c=d; Domain=example.com; Path=/", url); | ||
return jar.getSetCookieStringsSync(url); | ||
}, | ||
"returns the cookie header string": function(err, headers) { | ||
assert.ok(!err); | ||
assert.ok(Array.isArray(headers)); | ||
assert.lengthOf(headers, 2); | ||
headers.forEach(function(header) { | ||
assert.typeOf(header, 'string'); | ||
}); | ||
} | ||
}, | ||
} | ||
}) | ||
.addBatch({ | ||
"Synchronous API on async CookieJar": { | ||
topic: function() { | ||
return new tough.Store(); | ||
}, | ||
"setCookieSync": { | ||
topic: function(store) { | ||
var jar = new CookieJar(store); | ||
try { | ||
jar.setCookieSync("a=b", 'http://example.com/index.html'); | ||
return false; | ||
} catch(e) { | ||
return e; | ||
} | ||
}, | ||
"fails": function(err) { | ||
assert.instanceOf(err, Error); | ||
assert.equal(err.message, | ||
'CookieJar store is not synchronous; use async API instead.'); | ||
} | ||
}, | ||
"getCookiesSync": { | ||
topic: function(store) { | ||
var jar = new CookieJar(store); | ||
try { | ||
jar.getCookiesSync('http://example.com/index.html'); | ||
return false; | ||
} catch(e) { | ||
return e; | ||
} | ||
}, | ||
"fails": function(err) { | ||
assert.instanceOf(err, Error); | ||
assert.equal(err.message, | ||
'CookieJar store is not synchronous; use async API instead.'); | ||
} | ||
}, | ||
"getCookieStringSync": { | ||
topic: function(store) { | ||
var jar = new CookieJar(store); | ||
try { | ||
jar.getCookieStringSync('http://example.com/index.html'); | ||
return false; | ||
} catch(e) { | ||
return e; | ||
} | ||
}, | ||
"fails": function(err) { | ||
assert.instanceOf(err, Error); | ||
assert.equal(err.message, | ||
'CookieJar store is not synchronous; use async API instead.'); | ||
} | ||
}, | ||
"getSetCookieStringsSync": { | ||
topic: function(store) { | ||
var jar = new CookieJar(store); | ||
try { | ||
jar.getSetCookieStringsSync('http://example.com/index.html'); | ||
return false; | ||
} catch(e) { | ||
return e; | ||
} | ||
}, | ||
"fails": function(err) { | ||
assert.instanceOf(err, Error); | ||
assert.equal(err.message, | ||
'CookieJar store is not synchronous; use async API instead.'); | ||
} | ||
}, | ||
} | ||
}) | ||
.export(module); |
Sorry, the diff of this file is not supported yet
276416
13
3269
413