function Cookie(options) {
options = options || {};
Object.keys(options).forEach(function(prop) {
if (Cookie.prototype.hasOwnProperty(prop) &&
Cookie.prototype[prop] !== options[prop] &&
prop.substr(0,1) !== '_')
{
this[prop] = options[prop];
}
}, this);
this.creation = this.creation || new Date();
// used to break creation ties in cookieCompare():
Object.defineProperty(this, 'creationIndex', {
configurable: false,
enumerable: false, // important for assert.deepEqual checks
writable: true,
value: ++Cookie.cookiesCreated
});
}n/a
function CookieJar(store, options) {
if (typeof options === "boolean") {
options = {rejectPublicSuffixes: options};
} else if (options == null) {
options = {};
}
if (options.rejectPublicSuffixes != null) {
this.rejectPublicSuffixes = options.rejectPublicSuffixes;
}
if (options.looseMode != null) {
this.enableLooseMode = options.looseMode;
}
if (!store) {
store = new MemoryCookieStore();
}
this.store = store;
}n/a
function MemoryCookieStore() {
Store.call(this);
this.idx = {};
}n/a
function Store() {
}n/a
function canonicalDomain(str) {
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)) {
str = punycode.toASCII(str);
}
return str.toLowerCase();
}n/a
function cookieCompare(a, b) {
var cmp = 0;
// descending for length: b CMP a
var aPathLen = a.path ? a.path.length : 0;
var bPathLen = b.path ? b.path.length : 0;
cmp = bPathLen - aPathLen;
if (cmp !== 0) {
return cmp;
}
// ascending for time: a CMP b
var aTime = a.creation ? a.creation.getTime() : MAX_TIME;
var bTime = b.creation ? b.creation.getTime() : MAX_TIME;
cmp = aTime - bTime;
if (cmp !== 0) {
return cmp;
}
// break ties for the same millisecond (precision of JavaScript's clock)
cmp = a.creationIndex - b.creationIndex;
return cmp;
}n/a
function defaultPath(path) {
// "2. If the uri-path is empty or if the first character of the uri-path is not
// a %x2F ("/") character, output %x2F ("/") and skip the remaining steps.
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;
}
var rightSlash = path.lastIndexOf("/");
if (rightSlash === 0) {
return "/";
}
// "4. Output the characters of the uri-path from the first character up to,
// but not including, the right-most %x2F ("/")."
return path.slice(0, rightSlash);
}n/a
function domainMatch(str, domStr, canonicalize) {
if (str == null || domStr == null) {
return null;
}
if (canonicalize !== false) {
str = canonicalDomain(str);
domStr = canonicalDomain(domStr);
}
/*
* "The domain string and the string are identical. (Note that both the
* domain string and the string will have been canonicalized to lower case at
* this point)"
*/
if (str == domStr) {
return true;
}
/* "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;
}
/* "* 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)
}
// e.g "a.b.c".indexOf("b.c") === 2
// 5 === 3+2
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;
}
return true;
}n/a
function formatDate(date) {
var d = date.getUTCDate(); d = d >= 10 ? d : '0'+d;
var h = date.getUTCHours(); h = h >= 10 ? h : '0'+h;
var m = date.getUTCMinutes(); m = m >= 10 ? m : '0'+m;
var s = date.getUTCSeconds(); s = s >= 10 ? s : '0'+s;
return NUM_TO_DAY[date.getUTCDay()] + ', ' +
d+' '+ NUM_TO_MONTH[date.getUTCMonth()] +' '+ date.getUTCFullYear() +' '+
h+':'+m+':'+s+' GMT';
}n/a
function fromJSON(str) {
if (!str) {
return null;
}
var obj;
if (typeof str === 'string') {
obj = jsonParse(str);
if (obj instanceof Error) {
return null;
}
} else {
// assume it's an Object
obj = str;
}
var c = new Cookie();
for (var i=0; i<Cookie.serializableProperties.length; i++) {
var prop = Cookie.serializableProperties[i];
if (obj[prop] === undefined ||
obj[prop] === Cookie.prototype[prop])
{
continue; // leave as prototype default
}
if (prop === 'expires' ||
prop === 'creation' ||
prop === 'lastAccessed')
{
if (obj[prop] === null) {
c[prop] = null;
} else {
c[prop] = obj[prop] == "Infinity" ?
"Infinity" : new Date(obj[prop]);
}
} else {
c[prop] = obj[prop];
}
}
return c;
}n/a
function getPublicSuffix(domain) {
/*!
* Copyright (c) 2015, Salesforce.com, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of Salesforce.com nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
if (!domain) {
return null;
}
if (domain.match(/^\./)) {
return null;
}
var asciiDomain = punycode.toASCII(domain);
var converted = false;
if (asciiDomain !== domain) {
domain = asciiDomain;
converted = true;
}
if (index[domain]) {
return null;
}
domain = domain.toLowerCase();
var parts = domain.split('.').reverse();
var suffix = '';
var suffixLen = 0;
for (var i=0; i<parts.length; i++) {
var part = parts[i];
var starstr = '*'+suffix;
var partstr = part+suffix;
if (index[starstr]) { // star rule matches
suffixLen = i+1;
if (index[partstr] === false) { // exception rule matches (NB: false, not undefined)
suffixLen--;
}
} else if (index[partstr]) { // exact match, not exception
suffixLen = i+1;
}
suffix = '.'+partstr;
}
if (index['*'+suffix]) { // *.domain exists (e.g. *.kyoto.jp for domain='kyoto.jp');
return null;
}
suffixLen = suffixLen || 1;
if (parts.length > suffixLen) {
var publicSuffix = parts.slice(0,suffixLen+1).reverse().join('.');
return converted ? punycode.toUnicode(publicSuffix) : publicSuffix;
}
return null;
}...
*/
"use strict";
var pubsuffix = require('./pubsuffix');
// Gives the permutation of all possible domainMatch()es of a given domain. The
// array is in shortest-to-longest order. Handy for indexing.
function permuteDomain (domain) {
var pubSuf = pubsuffix.getPublicSuffix(domain);
if (!pubSuf) {
return null;
}
if (pubSuf == domain) {
return [domain];
}
...function parse(str, options) {
if (!options || typeof options !== 'object') {
options = {};
}
str = str.trim();
// We use a regex to parse the "name-value-pair" part of S5.2
var firstSemi = str.indexOf(';'); // S5.2 step 1
var pairRe = options.loose ? LOOSE_COOKIE_PAIR : COOKIE_PAIR;
var result = pairRe.exec(firstSemi === -1 ? str : str.substr(0,firstSemi));
// Rx satisfies the "the name string is empty" and "lacks a %x3D ("=")"
// constraints as well as trimming any whitespace.
if (!result) {
return;
}
var c = new Cookie();
if (result[1]) {
c.key = result[2].trim();
} else {
c.key = '';
}
c.value = result[3].trim();
if (CONTROL_CHARS.test(c.key) || CONTROL_CHARS.test(c.value)) {
return;
}
if (firstSemi === -1) {
return c;
}
// S5.2.3 "unparsed-attributes consist of the remainder of the set-cookie-string
// (including the %x3B (";") in question)." plus later on in the same section
// "discard the first ";" and trim".
var unparsed = str.slice(firstSemi + 1).trim();
// "If the unparsed-attributes string is empty, skip the rest of these
// steps."
if (unparsed.length === 0) {
return c;
}
/*
* S5.2 says that when looping over the items "[p]rocess the attribute-name
* and attribute-value according to the requirements in the following
* subsections" for every item. Plus, for many of the individual attributes
* in S5.3 it says to use the "attribute-value of the last attribute in the
* cookie-attribute-list". Therefore, in this implementation, we overwrite
* the previous value.
*/
var cookie_avs = unparsed.split(';');
while (cookie_avs.length) {
var av = cookie_avs.shift().trim();
if (av.length === 0) { // happens if ";;" appears
continue;
}
var av_sep = av.indexOf('=');
var av_key, av_value;
if (av_sep === -1) {
av_key = av;
av_value = null;
} else {
av_key = av.substr(0,av_sep);
av_value = av.substr(av_sep+1);
}
av_key = av_key.trim().toLowerCase();
if (av_value) {
av_value = av_value.trim();
}
switch(av_key) {
case 'expires': // S5.2.1
if (av_value) {
var exp = parseDate(av_value);
// "If the attribute-value failed to parse as a cookie date, ignore the
// cookie-av."
if (exp) {
// over and underflow not realistically a concern: V8's getTime() seems to
// store something larger than a 32-bit time_t (even with 32-bit node)
c.expires = exp;
}
}
break;
case 'max-age': // S5.2.2
if (av_value) {
// "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)) {
var delta = parseInt(av_value, 10);
// "If delta-seconds is less than or equal to zero (0), let expiry-time
// be the earliest representable date and time."
c.setMaxAge(delta);
}
}
break;
case 'domain': // S5.2.3
// "If the attribute-value is empty, the behavior is undefined. However,
// the user agent SHOULD ignore the cookie-av entirely."
if (av_value) {
// S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E
// (".") character."
var domain = av_value.trim().replace(/^\./, '');
if (domain) {
// "Convert the cookie-domain to lower case."
c.domain = domain.toLowerCase();
}
}
break;
case 'path': // S5.2.4
/*
* "If the attribute-value is empty or if the first character of the
* attribute-value is not %x2F ("/"):
* Let cookie-path be the default-path.
* Otherwise:
* Let cookie-path be the attribute-value."
*
* We'll represent the default-path as null since it depends on the
* context of the parsing.
*/
c.path = av_value && av_va ...n/a
function parseDate(str) {
if (!str) {
return;
}
/* RFC6265 S5.1.1:
* 2. Process each date-token sequentially in the order the date-tokens
* appear in the cookie-date
*/
var tokens = str.split(DATE_DELIM);
if (!tokens) {
return;
}
var hour = null;
var minutes = null;
var seconds = null;
var day = null;
var month = null;
var year = null;
for (var i=0; i<tokens.length; i++) {
var token = tokens[i].trim();
if (!token.length) {
continue;
}
var result;
/* 2.1. If the found-time flag is not set and the token matches the time
* production, set the found-time flag and set the hour- value,
* minute-value, and second-value to the numbers denoted by the digits in
* the date-token, respectively. Skip the remaining sub-steps and continue
* to the next date-token.
*/
if (seconds === null) {
result = TIME.exec(token);
if (result) {
hour = parseInt(result[1], 10);
minutes = parseInt(result[2], 10);
seconds = parseInt(result[3], 10);
/* RFC6265 S5.1.1.5:
* [fail if]
* * the hour-value is greater than 23,
* * the minute-value is greater than 59, or
* * the second-value is greater than 59.
*/
if(hour > 23 || minutes > 59 || seconds > 59) {
return;
}
continue;
}
}
/* 2.2. If the found-day-of-month flag is not set and the date-token matches
* the day-of-month production, set the found-day-of- month flag and set
* the day-of-month-value to the number denoted by the date-token. Skip
* the remaining sub-steps and continue to the next date-token.
*/
if (day === null) {
result = DAY_OF_MONTH.exec(token);
if (result) {
day = parseInt(result, 10);
/* RFC6265 S5.1.1.5:
* [fail if] the day-of-month-value is less than 1 or greater than 31
*/
if(day < 1 || day > 31) {
return;
}
continue;
}
}
/* 2.3. If the found-month flag is not set and the date-token matches the
* month production, set the found-month flag and set the month-value to
* the month denoted by the date-token. Skip the remaining sub-steps and
* continue to the next date-token.
*/
if (month === null) {
result = MONTH.exec(token);
if (result) {
month = MONTH_TO_NUM[result[1].toLowerCase()];
continue;
}
}
/* 2.4. If the found-year flag is not set and the date-token matches the year
* production, set the found-year flag and set the year-value to the number
* denoted by the date-token. Skip the remaining sub-steps and continue to
* the next date-token.
*/
if (year === null) {
result = YEAR.exec(token);
if (result) {
year = parseInt(result[0], 10);
/* From S5.1.1:
* 3. If the year-value is greater than or equal to 70 and less
* than or equal to 99, increment the year-value by 1900.
* 4. If the year-value is greater than or equal to 0 and less
* than or equal to 69, increment the year-value by 2000.
*/
if (70 <= year && year <= 99) {
year += 1900;
} else if (0 <= year && year <= 69) {
year += 2000;
}
if (year < 1601) {
return; // 5. ... the year-value is less than 1601
}
}
}
}
if (seconds === null || day === null || month === null || year === null) {
return; // 5. ... at least one of the found-day-of-month, found-month, found-
// year, or found-time flags is not set,
}
return new Date(Date.UTC(year, month, day, hour, minutes, seconds));
}n/a
function pathMatch(reqPath, cookiePath) {
// "o The cookie-path and the request-path are identical."
if (cookiePath === reqPath) {
return true;
}
var idx = reqPath.indexOf(cookiePath);
if (idx === 0) {
// "o The cookie-path is a prefix of the request-path, and the last
// character of the cookie-path is %x2F ("/")."
if (cookiePath.substr(-1) === "/") {
return true;
}
// " o The cookie-path is a prefix of the request-path, and the first
// character of the request-path that is not included in the cookie- path
// is a %x2F ("/") character."
if (reqPath.substr(cookiePath.length, 1) === "/") {
return true;
}
}
return false;
}n/a
function permuteDomain(domain) {
var pubSuf = pubsuffix.getPublicSuffix(domain);
if (!pubSuf) {
return null;
}
if (pubSuf == domain) {
return [domain];
}
var prefix = domain.slice(0, -(pubSuf.length + 1)); // ".example.com"
var parts = prefix.split('.').reverse();
var cur = pubSuf;
var permutations = [cur];
while (parts.length) {
cur = parts.shift() + '.' + cur;
permutations.push(cur);
}
return permutations;
}n/a
function permutePath(path) {
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;
}
path = path.substr(0,lindex);
permutations.push(path);
}
permutations.push('/');
return permutations;
}n/a
function Cookie(options) {
options = options || {};
Object.keys(options).forEach(function(prop) {
if (Cookie.prototype.hasOwnProperty(prop) &&
Cookie.prototype[prop] !== options[prop] &&
prop.substr(0,1) !== '_')
{
this[prop] = options[prop];
}
}, this);
this.creation = this.creation || new Date();
// used to break creation ties in cookieCompare():
Object.defineProperty(this, 'creationIndex', {
configurable: false,
enumerable: false, // important for assert.deepEqual checks
writable: true,
value: ++Cookie.cookiesCreated
});
}n/a
function fromJSON(str) {
if (!str) {
return null;
}
var obj;
if (typeof str === 'string') {
obj = jsonParse(str);
if (obj instanceof Error) {
return null;
}
} else {
// assume it's an Object
obj = str;
}
var c = new Cookie();
for (var i=0; i<Cookie.serializableProperties.length; i++) {
var prop = Cookie.serializableProperties[i];
if (obj[prop] === undefined ||
obj[prop] === Cookie.prototype[prop])
{
continue; // leave as prototype default
}
if (prop === 'expires' ||
prop === 'creation' ||
prop === 'lastAccessed')
{
if (obj[prop] === null) {
c[prop] = null;
} else {
c[prop] = obj[prop] == "Infinity" ?
"Infinity" : new Date(obj[prop]);
}
} else {
c[prop] = obj[prop];
}
}
return c;
}n/a
function parse(str, options) {
if (!options || typeof options !== 'object') {
options = {};
}
str = str.trim();
// We use a regex to parse the "name-value-pair" part of S5.2
var firstSemi = str.indexOf(';'); // S5.2 step 1
var pairRe = options.loose ? LOOSE_COOKIE_PAIR : COOKIE_PAIR;
var result = pairRe.exec(firstSemi === -1 ? str : str.substr(0,firstSemi));
// Rx satisfies the "the name string is empty" and "lacks a %x3D ("=")"
// constraints as well as trimming any whitespace.
if (!result) {
return;
}
var c = new Cookie();
if (result[1]) {
c.key = result[2].trim();
} else {
c.key = '';
}
c.value = result[3].trim();
if (CONTROL_CHARS.test(c.key) || CONTROL_CHARS.test(c.value)) {
return;
}
if (firstSemi === -1) {
return c;
}
// S5.2.3 "unparsed-attributes consist of the remainder of the set-cookie-string
// (including the %x3B (";") in question)." plus later on in the same section
// "discard the first ";" and trim".
var unparsed = str.slice(firstSemi + 1).trim();
// "If the unparsed-attributes string is empty, skip the rest of these
// steps."
if (unparsed.length === 0) {
return c;
}
/*
* S5.2 says that when looping over the items "[p]rocess the attribute-name
* and attribute-value according to the requirements in the following
* subsections" for every item. Plus, for many of the individual attributes
* in S5.3 it says to use the "attribute-value of the last attribute in the
* cookie-attribute-list". Therefore, in this implementation, we overwrite
* the previous value.
*/
var cookie_avs = unparsed.split(';');
while (cookie_avs.length) {
var av = cookie_avs.shift().trim();
if (av.length === 0) { // happens if ";;" appears
continue;
}
var av_sep = av.indexOf('=');
var av_key, av_value;
if (av_sep === -1) {
av_key = av;
av_value = null;
} else {
av_key = av.substr(0,av_sep);
av_value = av.substr(av_sep+1);
}
av_key = av_key.trim().toLowerCase();
if (av_value) {
av_value = av_value.trim();
}
switch(av_key) {
case 'expires': // S5.2.1
if (av_value) {
var exp = parseDate(av_value);
// "If the attribute-value failed to parse as a cookie date, ignore the
// cookie-av."
if (exp) {
// over and underflow not realistically a concern: V8's getTime() seems to
// store something larger than a 32-bit time_t (even with 32-bit node)
c.expires = exp;
}
}
break;
case 'max-age': // S5.2.2
if (av_value) {
// "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)) {
var delta = parseInt(av_value, 10);
// "If delta-seconds is less than or equal to zero (0), let expiry-time
// be the earliest representable date and time."
c.setMaxAge(delta);
}
}
break;
case 'domain': // S5.2.3
// "If the attribute-value is empty, the behavior is undefined. However,
// the user agent SHOULD ignore the cookie-av entirely."
if (av_value) {
// S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E
// (".") character."
var domain = av_value.trim().replace(/^\./, '');
if (domain) {
// "Convert the cookie-domain to lower case."
c.domain = domain.toLowerCase();
}
}
break;
case 'path': // S5.2.4
/*
* "If the attribute-value is empty or if the first character of the
* attribute-value is not %x2F ("/"):
* Let cookie-path be the default-path.
* Otherwise:
* Let cookie-path be the attribute-value."
*
* We'll represent the default-path as null since it depends on the
* context of the parsing.
*/
c.path = av_value && av_va ...n/a
function TTL(now) {
/* RFC6265 S4.1.2.2 If a cookie has both the Max-Age and the Expires
* attribute, the Max-Age attribute has precedence and controls the
* expiration date of the cookie.
* (Concurs with S5.3 step 3)
*/
if (this.maxAge != null) {
return this.maxAge<=0 ? 0 : this.maxAge*1000;
}
var expires = this.expires;
if (expires != Infinity) {
if (!(expires instanceof Date)) {
expires = parseDate(expires) || Infinity;
}
if (expires == Infinity) {
return Infinity;
}
return expires.getTime() - (now || Date.now());
}
return Infinity;
}n/a
function canonicalizedDomain() {
if (this.domain == null) {
return null;
}
return canonicalDomain(this.domain);
}n/a
function canonicalizedDomain() {
if (this.domain == null) {
return null;
}
return canonicalDomain(this.domain);
}n/a
clone = function () {
return fromJSON(this.toJSON());
}n/a
function cookieString() {
var val = this.value;
if (val == null) {
val = '';
}
if (this.key === '') {
return val;
}
return this.key+'='+val;
}n/a
function expiryDate(now) {
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);
}
}n/a
function expiryTime(now) {
if (this.maxAge != null) {
var relativeTo = now || this.creation || new Date();
var age = (this.maxAge <= 0) ? -Infinity : this.maxAge*1000;
return relativeTo.getTime() + age;
}
if (this.expires == Infinity) {
return Infinity;
}
return this.expires.getTime();
}n/a
function inspect() {
var now = Date.now();
return 'Cookie="'+this.toString() +
'; hostOnly='+(this.hostOnly != null ? this.hostOnly : '?') +
'; aAge='+(this.lastAccessed ? (now-this.lastAccessed.getTime())+'ms' : '?') +
'; cAge='+(this.creation ? (now-this.creation.getTime())+'ms' : '?') +
'"';
}...
MemoryCookieStore.prototype.idx = null;
// Since it's just a struct in RAM, this Store is synchronous
MemoryCookieStore.prototype.synchronous = true;
// force a default depth:
MemoryCookieStore.prototype.inspect = function() {
return "{ idx: "+util.inspect(this.idx, false, 2)+' }';
};
MemoryCookieStore.prototype.findCookie = function(domain, path, key, cb) {
if (!this.idx[domain]) {
return cb(null,undefined);
}
if (!this.idx[domain][path]) {
...function isPersistent() {
return (this.maxAge != null || this.expires != Infinity);
}n/a
function setExpires(exp) {
if (exp instanceof Date) {
this.expires = exp;
} else {
this.expires = parseDate(exp) || "Infinity";
}
}n/a
function setMaxAge(age) {
if (age === Infinity || age === -Infinity) {
this.maxAge = age.toString(); // so JSON.stringify() works
} else {
this.maxAge = age;
}
}n/a
toJSON = function () {
var obj = {};
var props = Cookie.serializableProperties;
for (var i=0; i<props.length; i++) {
var prop = props[i];
if (this[prop] === Cookie.prototype[prop]) {
continue; // leave as prototype default
}
if (prop === 'expires' ||
prop === 'creation' ||
prop === 'lastAccessed')
{
if (this[prop] === null) {
obj[prop] = null;
} else {
obj[prop] = this[prop] == "Infinity" ? // intentionally not ===
"Infinity" : this[prop].toISOString();
}
} else if (prop === 'maxAge') {
if (this[prop] !== null) {
// again, intentionally not ===
obj[prop] = (this[prop] == Infinity || this[prop] == -Infinity) ?
this[prop].toString() : this[prop];
}
} else {
if (this[prop] !== Cookie.prototype[prop]) {
obj[prop] = this[prop];
}
}
}
return obj;
}n/a
function toString() {
var str = this.cookieString();
if (this.expires != Infinity) {
if (this.expires instanceof Date) {
str += '; Expires='+formatDate(this.expires);
} else {
str += '; Expires='+this.expires;
}
}
if (this.maxAge != null && this.maxAge != Infinity) {
str += '; Max-Age='+this.maxAge;
}
if (this.domain && !this.hostOnly) {
str += '; Domain='+this.domain;
}
if (this.path) {
str += '; Path='+this.path;
}
if (this.secure) {
str += '; Secure';
}
if (this.httpOnly) {
str += '; HttpOnly';
}
if (this.extensions) {
this.extensions.forEach(function(ext) {
str += '; '+ext;
});
}
return str;
}n/a
function validate() {
if (!COOKIE_OCTETS.test(this.value)) {
return false;
}
if (this.expires != Infinity && !(this.expires instanceof Date) && !parseDate(this.expires)) {
return false;
}
if (this.maxAge != null && this.maxAge <= 0) {
return false; // "Max-Age=" non-zero-digit *DIGIT
}
if (this.path != null && !PATH_VALUE.test(this.path)) {
return false;
}
var cdomain = this.cdomain();
if (cdomain) {
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
return false;
}
}
return true;
}n/a
function CookieJar(store, options) {
if (typeof options === "boolean") {
options = {rejectPublicSuffixes: options};
} else if (options == null) {
options = {};
}
if (options.rejectPublicSuffixes != null) {
this.rejectPublicSuffixes = options.rejectPublicSuffixes;
}
if (options.looseMode != null) {
this.enableLooseMode = options.looseMode;
}
if (!store) {
store = new MemoryCookieStore();
}
this.store = store;
}n/a
deserialize = function (strOrObj, store, cb) {
if (arguments.length !== 3) {
// store is optional
cb = store;
store = null;
}
var serialized;
if (typeof strOrObj === 'string') {
serialized = jsonParse(strOrObj);
if (serialized instanceof Error) {
return cb(serialized);
}
} else {
serialized = strOrObj;
}
var jar = new CookieJar(store, serialized.rejectPublicSuffixes);
jar._importCookies(serialized, function(err) {
if (err) {
return cb(err);
}
cb(null, jar);
});
}n/a
deserializeSync = function (strOrObj, store) {
var serialized = typeof strOrObj === 'string' ?
JSON.parse(strOrObj) : strOrObj;
var jar = new CookieJar(store, serialized.rejectPublicSuffixes);
// catch this mistake early:
if (!jar.store.synchronous) {
throw new Error('CookieJar store is not synchronous; use async API instead.');
}
jar._importCookiesSync(serialized);
return jar;
}n/a
fromJSON = function (strOrObj, store) {
var serialized = typeof strOrObj === 'string' ?
JSON.parse(strOrObj) : strOrObj;
var jar = new CookieJar(store, serialized.rejectPublicSuffixes);
// catch this mistake early:
if (!jar.store.synchronous) {
throw new Error('CookieJar store is not synchronous; use async API instead.');
}
jar._importCookiesSync(serialized);
return jar;
}n/a
_importCookies = function (serialized, cb) {
var jar = this;
var cookies = serialized.cookies;
if (!cookies || !Array.isArray(cookies)) {
return cb(new Error('serialized jar has no cookies array'));
}
function putNext(err) {
if (err) {
return cb(err);
}
if (!cookies.length) {
return cb(err, jar);
}
var cookie;
try {
cookie = fromJSON(cookies.shift());
} catch (e) {
return cb(e);
}
if (cookie === null) {
return putNext(null); // skip this cookie
}
jar.store.putCookie(cookie, putNext);
}
putNext();
}n/a
_importCookiesSync = 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;
}n/a
clone = function (newStore, cb) {
if (arguments.length === 1) {
cb = newStore;
newStore = null;
}
this.serialize(function(err,serialized) {
if (err) {
return cb(err);
}
CookieJar.deserialize(newStore, serialized, cb);
});
}n/a
cloneSync = 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;
}n/a
getCookieString = function () {
var args = Array.prototype.slice.call(arguments,0);
var cb = args.pop();
var next = function(err,cookies) {
if (err) {
cb(err);
} else {
cb(null, cookies
.sort(cookieCompare)
.map(function(c){
return c.cookieString();
})
.join('; '));
}
};
args.push(next);
this.getCookies.apply(this,args);
}n/a
getCookieStringSync = 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;
}n/a
getCookies = function (url, options, cb) {
var context = getCookieContext(url);
if (options instanceof Function) {
cb = options;
options = {};
}
var host = canonicalDomain(context.hostname);
var path = context.pathname || '/';
var secure = options.secure;
if (secure == null && context.protocol &&
(context.protocol == 'https:' || context.protocol == 'wss:'))
{
secure = true;
}
var http = options.http;
if (http == null) {
http = true;
}
var now = options.now || Date.now();
var expireCheck = options.expire !== false;
var allPaths = !!options.allPaths;
var store = this.store;
function matchingCookie(c) {
// "Either:
// The cookie's host-only-flag is true and the canonicalized
// request-host is identical to the cookie's domain.
// Or:
// The cookie's host-only-flag is false and the canonicalized
// request-host domain-matches the cookie's domain."
if (c.hostOnly) {
if (c.domain != host) {
return false;
}
} else {
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)) {
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) {
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) {
return false;
}
// deferred from S5.3
// non-RFC: allow retention of expired cookies by choice
if (expireCheck && c.expiryTime() <= now) {
store.removeCookie(c.domain, c.path, c.key, function(){}); // result ignored
return false;
}
return true;
}
store.findCookies(host, allPaths ? null : path, function(err,cookies) {
if (err) {
return cb(err);
}
cookies = cookies.filter(matchingCookie);
// sorting of S5.4 part 2
if (options.sort !== false) {
cookies = cookies.sort(cookieCompare);
}
// S5.4 part 3
var now = new Date();
cookies.forEach(function(c) {
c.lastAccessed = now;
});
// TODO persist lastAccessed
cb(null,cookies);
});
}n/a
getCookiesSync = 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;
}n/a
getSetCookieStrings = function () {
var args = Array.prototype.slice.call(arguments,0);
var cb = args.pop();
var next = function(err,cookies) {
if (err) {
cb(err);
} else {
cb(null, cookies.map(function(c){
return c.toString();
}));
}
};
args.push(next);
this.getCookies.apply(this,args);
}n/a
getSetCookieStringsSync = 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;
}n/a
serialize = function (cb) {
var type = this.store.constructor.name;
if (type === 'Object') {
type = null;
}
// update README.md "Serialization Format" if you change this, please!
var serialized = {
// The version of tough-cookie that serialized this jar. Generally a good
// practice since future versions can make data import decisions based on
// known past behavior. When/if this matters, use `semver`.
version: 'tough-cookie@'+VERSION,
// add the store type, to make humans happy:
storeType: type,
// CookieJar configuration:
rejectPublicSuffixes: !!this.rejectPublicSuffixes,
// this gets filled from getAllCookies:
cookies: []
};
if (!(this.store.getAllCookies &&
typeof this.store.getAllCookies === 'function'))
{
return cb(new Error('store does not support getAllCookies and cannot be serialized'));
}
this.store.getAllCookies(function(err,cookies) {
if (err) {
return cb(err);
}
serialized.cookies = cookies.map(function(cookie) {
// convert to serialized 'raw' cookies
cookie = (cookie instanceof Cookie) ? cookie.toJSON() : cookie;
// Remove the index so new ones get assigned during deserialization
delete cookie.creationIndex;
return cookie;
});
return cb(null, serialized);
});
}n/a
serializeSync = 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;
}n/a
setCookie = function (cookie, url, options, cb) {
var err;
var context = getCookieContext(url);
if (options instanceof Function) {
cb = options;
options = {};
}
var host = canonicalDomain(context.hostname);
var loose = this.enableLooseMode;
if (options.loose != null) {
loose = options.loose;
}
// S5.3 step 1
if (!(cookie instanceof Cookie)) {
cookie = Cookie.parse(cookie, { loose: loose });
}
if (!cookie) {
err = new Error("Cookie failed to parse");
return cb(options.ignoreError ? null : err);
}
// S5.3 step 2
var now = options.now || new Date(); // will assign later to save effort in the face of errors
// S5.3 step 3: NOOP; persistent-flag and expiry-time is handled by getCookie()
// S5.3 step 4: NOOP; domain is null by default
// S5.3 step 5: public suffixes
if (this.rejectPublicSuffixes && cookie.domain) {
var suffix = pubsuffix.getPublicSuffix(cookie.cdomain());
if (suffix == null) { // e.g. "com"
err = new Error("Cookie has domain set to a public suffix");
return cb(options.ignoreError ? null : err);
}
}
// S5.3 step 6:
if (cookie.domain) {
if (!domainMatch(host, cookie.cdomain(), false)) {
err = new Error("Cookie not in this host's domain. Cookie:"+cookie.cdomain()+" Request:"+host);
return cb(options.ignoreError ? null : err);
}
if (cookie.hostOnly == null) { // don't reset if already set
cookie.hostOnly = false;
}
} else {
cookie.hostOnly = true;
cookie.domain = host;
}
//S5.2.4 If the attribute-value is empty or if the first character of the
//attribute-value is not %x2F ("/"):
//Let cookie-path be the default-path.
if (!cookie.path || cookie.path[0] !== '/') {
cookie.path = defaultPath(context.pathname);
cookie.pathIsDefault = true;
}
// S5.3 step 8: NOOP; secure attribute
// S5.3 step 9: NOOP; httpOnly attribute
// S5.3 step 10
if (options.http === false && cookie.httpOnly) {
err = new Error("Cookie is HttpOnly and this isn't an HTTP API");
return cb(options.ignoreError ? null : err);
}
var store = this.store;
if (!store.updateCookie) {
store.updateCookie = function(oldCookie, newCookie, cb) {
this.putCookie(newCookie, cb);
};
}
function withCookie(err, oldCookie) {
if (err) {
return cb(err);
}
var next = function(err) {
if (err) {
return cb(err);
} else {
cb(null, cookie);
}
};
if (oldCookie) {
// S5.3 step 11 - "If the cookie store contains a cookie with the same name,
// domain, and path as the newly created cookie:"
if (options.http === false && oldCookie.httpOnly) { // step 11.2
err = new Error("old Cookie is HttpOnly and this isn't an HTTP API");
return cb(options.ignoreError ? null : err);
}
cookie.creation = oldCookie.creation; // step 11.3
cookie.creationIndex = oldCookie.creationIndex; // preserve tie-breaker
cookie.lastAccessed = now;
// Step 11.4 (delete cookie) is implied by just setting the new one:
store.updateCookie(oldCookie, cookie, next); // step 12
} else {
cookie.creation = cookie.lastAccessed = now;
store.putCookie(cookie, next); // step 12
}
}
store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie);
}n/a
setCookieSync = 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;
}n/a
toJSON = function () {
return this.serializeSync();
}n/a
function MemoryCookieStore() {
Store.call(this);
this.idx = {};
}n/a
function Store() {
}n/a
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);
}n/a
findCookies = function (domain, path, cb) {
var results = [];
if (!domain) {
return cb(null,[]);
}
var pathMatcher;
if (!path) {
// null means "all paths"
pathMatcher = function matchAll(domainIndex) {
for (var curPath in domainIndex) {
var pathIndex = domainIndex[curPath];
for (var key in pathIndex) {
results.push(pathIndex[key]);
}
}
};
} else {
pathMatcher = function matchRFC(domainIndex) {
//NOTE: we should use path-match algorithm from S5.1.4 here
//(see : https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/canonical_cookie
.cc#L299)
Object.keys(domainIndex).forEach(function (cookiePath) {
if (pathMatch(path, cookiePath)) {
var pathIndex = domainIndex[cookiePath];
for (var key in pathIndex) {
results.push(pathIndex[key]);
}
}
});
};
}
var domains = permuteDomain(domain) || [domain];
var idx = this.idx;
domains.forEach(function(curDomain) {
var domainIndex = idx[curDomain];
if (!domainIndex) {
return;
}
pathMatcher(domainIndex);
});
cb(null,results);
}n/a
getAllCookies = function (cb) {
var cookies = [];
var idx = this.idx;
var domains = Object.keys(idx);
domains.forEach(function(domain) {
var paths = Object.keys(idx[domain]);
paths.forEach(function(path) {
var keys = Object.keys(idx[domain][path]);
keys.forEach(function(key) {
if (key !== null) {
cookies.push(idx[domain][path][key]);
}
});
});
});
// Sort by creationIndex so deserializing retains the creation order.
// When implementing your own store, this SHOULD retain the order too
cookies.sort(function(a,b) {
return (a.creationIndex||0) - (b.creationIndex||0);
});
cb(null, cookies);
}n/a
inspect = function () {
return "{ idx: "+util.inspect(this.idx, false, 2)+' }';
}...
MemoryCookieStore.prototype.idx = null;
// Since it's just a struct in RAM, this Store is synchronous
MemoryCookieStore.prototype.synchronous = true;
// force a default depth:
MemoryCookieStore.prototype.inspect = function() {
return "{ idx: "+util.inspect(this.idx, false, 2)+' }';
};
MemoryCookieStore.prototype.findCookie = function(domain, path, key, cb) {
if (!this.idx[domain]) {
return cb(null,undefined);
}
if (!this.idx[domain][path]) {
...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;
cb(null);
}...
cb(null);
};
MemoryCookieStore.prototype.updateCookie = function(oldCookie, newCookie, cb) {
// updateCookie() may avoid updating cookies that are identical. For example,
// lastAccessed may not be important to some stores and an equality
// comparison could exclude that field.
this.putCookie(newCookie,cb);
};
MemoryCookieStore.prototype.removeCookie = function(domain, path, key, cb) {
if (this.idx[domain] && this.idx[domain][path] && this.idx[domain][path][key]) {
delete this.idx[domain][path][key];
}
cb(null);
...removeCookie = function (domain, path, key, cb) {
if (this.idx[domain] && this.idx[domain][path] && this.idx[domain][path][key]) {
delete this.idx[domain][path][key];
}
cb(null);
}n/a
removeCookies = function (domain, path, cb) {
if (this.idx[domain]) {
if (path) {
delete this.idx[domain][path];
} else {
delete this.idx[domain];
}
}
return cb(null);
}n/a
updateCookie = function (oldCookie, newCookie, cb) {
// updateCookie() may avoid updating cookies that are identical. For example,
// lastAccessed may not be important to some stores and an equality
// comparison could exclude that field.
this.putCookie(newCookie,cb);
}n/a
function Store() {
}n/a
findCookie = function (domain, path, key, cb) {
throw new Error('findCookie is not implemented');
}n/a
findCookies = function (domain, path, cb) {
throw new Error('findCookies is not implemented');
}n/a
getAllCookies = function (cb) {
throw new Error('getAllCookies is not implemented (therefore jar cannot be serialized)');
}n/a
putCookie = function (cookie, cb) {
throw new Error('putCookie is not implemented');
}...
cb(null);
};
MemoryCookieStore.prototype.updateCookie = function(oldCookie, newCookie, cb) {
// updateCookie() may avoid updating cookies that are identical. For example,
// lastAccessed may not be important to some stores and an equality
// comparison could exclude that field.
this.putCookie(newCookie,cb);
};
MemoryCookieStore.prototype.removeCookie = function(domain, path, key, cb) {
if (this.idx[domain] && this.idx[domain][path] && this.idx[domain][path][key]) {
delete this.idx[domain][path][key];
}
cb(null);
...removeCookie = function (domain, path, key, cb) {
throw new Error('removeCookie is not implemented');
}n/a
removeCookies = function (domain, path, cb) {
throw new Error('removeCookies is not implemented');
}n/a
updateCookie = function (oldCookie, newCookie, cb) {
// recommended default implementation:
// return this.putCookie(newCookie, cb);
throw new Error('updateCookie is not implemented');
}n/a
function MemoryCookieStore() {
Store.call(this);
this.idx = {};
}n/a
function pathMatch(reqPath, cookiePath) {
// "o The cookie-path and the request-path are identical."
if (cookiePath === reqPath) {
return true;
}
var idx = reqPath.indexOf(cookiePath);
if (idx === 0) {
// "o The cookie-path is a prefix of the request-path, and the last
// character of the cookie-path is %x2F ("/")."
if (cookiePath.substr(-1) === "/") {
return true;
}
// " o The cookie-path is a prefix of the request-path, and the first
// character of the request-path that is not included in the cookie- path
// is a %x2F ("/") character."
if (reqPath.substr(cookiePath.length, 1) === "/") {
return true;
}
}
return false;
}n/a
function permuteDomain(domain) {
var pubSuf = pubsuffix.getPublicSuffix(domain);
if (!pubSuf) {
return null;
}
if (pubSuf == domain) {
return [domain];
}
var prefix = domain.slice(0, -(pubSuf.length + 1)); // ".example.com"
var parts = prefix.split('.').reverse();
var cur = pubSuf;
var permutations = [cur];
while (parts.length) {
cur = parts.shift() + '.' + cur;
permutations.push(cur);
}
return permutations;
}n/a
function getPublicSuffix(domain) {
/*!
* Copyright (c) 2015, Salesforce.com, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of Salesforce.com nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
if (!domain) {
return null;
}
if (domain.match(/^\./)) {
return null;
}
var asciiDomain = punycode.toASCII(domain);
var converted = false;
if (asciiDomain !== domain) {
domain = asciiDomain;
converted = true;
}
if (index[domain]) {
return null;
}
domain = domain.toLowerCase();
var parts = domain.split('.').reverse();
var suffix = '';
var suffixLen = 0;
for (var i=0; i<parts.length; i++) {
var part = parts[i];
var starstr = '*'+suffix;
var partstr = part+suffix;
if (index[starstr]) { // star rule matches
suffixLen = i+1;
if (index[partstr] === false) { // exception rule matches (NB: false, not undefined)
suffixLen--;
}
} else if (index[partstr]) { // exact match, not exception
suffixLen = i+1;
}
suffix = '.'+partstr;
}
if (index['*'+suffix]) { // *.domain exists (e.g. *.kyoto.jp for domain='kyoto.jp');
return null;
}
suffixLen = suffixLen || 1;
if (parts.length > suffixLen) {
var publicSuffix = parts.slice(0,suffixLen+1).reverse().join('.');
return converted ? punycode.toUnicode(publicSuffix) : publicSuffix;
}
return null;
}...
*/
"use strict";
var pubsuffix = require('./pubsuffix');
// Gives the permutation of all possible domainMatch()es of a given domain. The
// array is in shortest-to-longest order. Handy for indexing.
function permuteDomain (domain) {
var pubSuf = pubsuffix.getPublicSuffix(domain);
if (!pubSuf) {
return null;
}
if (pubSuf == domain) {
return [domain];
}
...function Store() {
}n/a