276 lines
9.8 KiB
JavaScript
276 lines
9.8 KiB
JavaScript
/* jshint node: true */
|
|
(function () {
|
|
"use strict";
|
|
|
|
function CookieAccessInfo(domain, path, secure, script) {
|
|
if (this instanceof CookieAccessInfo) {
|
|
this.domain = domain || undefined;
|
|
this.path = path || "/";
|
|
this.secure = !!secure;
|
|
this.script = !!script;
|
|
return this;
|
|
}
|
|
return new CookieAccessInfo(domain, path, secure, script);
|
|
}
|
|
CookieAccessInfo.All = Object.freeze(Object.create(null));
|
|
exports.CookieAccessInfo = CookieAccessInfo;
|
|
|
|
function Cookie(cookiestr, request_domain, request_path) {
|
|
if (cookiestr instanceof Cookie) {
|
|
return cookiestr;
|
|
}
|
|
if (this instanceof Cookie) {
|
|
this.name = null;
|
|
this.value = null;
|
|
this.expiration_date = Infinity;
|
|
this.path = String(request_path || "/");
|
|
this.explicit_path = false;
|
|
this.domain = request_domain || null;
|
|
this.explicit_domain = false;
|
|
this.secure = false; //how to define default?
|
|
this.noscript = false; //httponly
|
|
if (cookiestr) {
|
|
this.parse(cookiestr, request_domain, request_path);
|
|
}
|
|
return this;
|
|
}
|
|
return new Cookie(cookiestr, request_domain, request_path);
|
|
}
|
|
exports.Cookie = Cookie;
|
|
|
|
Cookie.prototype.toString = function toString() {
|
|
var str = [this.name + "=" + this.value];
|
|
if (this.expiration_date !== Infinity) {
|
|
str.push("expires=" + (new Date(this.expiration_date)).toGMTString());
|
|
}
|
|
if (this.domain) {
|
|
str.push("domain=" + this.domain);
|
|
}
|
|
if (this.path) {
|
|
str.push("path=" + this.path);
|
|
}
|
|
if (this.secure) {
|
|
str.push("secure");
|
|
}
|
|
if (this.noscript) {
|
|
str.push("httponly");
|
|
}
|
|
return str.join("; ");
|
|
};
|
|
|
|
Cookie.prototype.toValueString = function toValueString() {
|
|
return this.name + "=" + this.value;
|
|
};
|
|
|
|
var cookie_str_splitter = /[:](?=\s*[a-zA-Z0-9_\-]+\s*[=])/g;
|
|
Cookie.prototype.parse = function parse(str, request_domain, request_path) {
|
|
if (this instanceof Cookie) {
|
|
var parts = str.split(";").filter(function (value) {
|
|
return !!value;
|
|
});
|
|
var i;
|
|
|
|
var pair = parts[0].match(/([^=]+)=([\s\S]*)/);
|
|
if (!pair) {
|
|
console.warn("Invalid cookie header encountered. Header: '"+str+"'");
|
|
return;
|
|
}
|
|
|
|
var key = pair[1];
|
|
var value = pair[2];
|
|
if ( typeof key !== 'string' || key.length === 0 || typeof value !== 'string' ) {
|
|
console.warn("Unable to extract values from cookie header. Cookie: '"+str+"'");
|
|
return;
|
|
}
|
|
|
|
this.name = key;
|
|
this.value = value;
|
|
|
|
for (i = 1; i < parts.length; i += 1) {
|
|
pair = parts[i].match(/([^=]+)(?:=([\s\S]*))?/);
|
|
key = pair[1].trim().toLowerCase();
|
|
value = pair[2];
|
|
switch (key) {
|
|
case "httponly":
|
|
this.noscript = true;
|
|
break;
|
|
case "expires":
|
|
this.expiration_date = value ?
|
|
Number(Date.parse(value)) :
|
|
Infinity;
|
|
break;
|
|
case "path":
|
|
this.path = value ?
|
|
value.trim() :
|
|
"";
|
|
this.explicit_path = true;
|
|
break;
|
|
case "domain":
|
|
this.domain = value ?
|
|
value.trim() :
|
|
"";
|
|
this.explicit_domain = !!this.domain;
|
|
break;
|
|
case "secure":
|
|
this.secure = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!this.explicit_path) {
|
|
this.path = request_path || "/";
|
|
}
|
|
if (!this.explicit_domain) {
|
|
this.domain = request_domain;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
return new Cookie().parse(str, request_domain, request_path);
|
|
};
|
|
|
|
Cookie.prototype.matches = function matches(access_info) {
|
|
if (access_info === CookieAccessInfo.All) {
|
|
return true;
|
|
}
|
|
if (this.noscript && access_info.script ||
|
|
this.secure && !access_info.secure ||
|
|
!this.collidesWith(access_info)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
Cookie.prototype.collidesWith = function collidesWith(access_info) {
|
|
if ((this.path && !access_info.path) || (this.domain && !access_info.domain)) {
|
|
return false;
|
|
}
|
|
if (this.path && access_info.path.indexOf(this.path) !== 0) {
|
|
return false;
|
|
}
|
|
if (this.explicit_path && access_info.path.indexOf( this.path ) !== 0) {
|
|
return false;
|
|
}
|
|
var access_domain = access_info.domain && access_info.domain.replace(/^[\.]/,'');
|
|
var cookie_domain = this.domain && this.domain.replace(/^[\.]/,'');
|
|
if (cookie_domain === access_domain) {
|
|
return true;
|
|
}
|
|
if (cookie_domain) {
|
|
if (!this.explicit_domain) {
|
|
return false; // we already checked if the domains were exactly the same
|
|
}
|
|
var wildcard = access_domain.indexOf(cookie_domain);
|
|
if (wildcard === -1 || wildcard !== access_domain.length - cookie_domain.length) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
function CookieJar() {
|
|
var cookies, cookies_list, collidable_cookie;
|
|
if (this instanceof CookieJar) {
|
|
cookies = Object.create(null); //name: [Cookie]
|
|
|
|
this.setCookie = function setCookie(cookie, request_domain, request_path) {
|
|
var remove, i;
|
|
cookie = new Cookie(cookie, request_domain, request_path);
|
|
//Delete the cookie if the set is past the current time
|
|
remove = cookie.expiration_date <= Date.now();
|
|
if (cookies[cookie.name] !== undefined) {
|
|
cookies_list = cookies[cookie.name];
|
|
for (i = 0; i < cookies_list.length; i += 1) {
|
|
collidable_cookie = cookies_list[i];
|
|
if (collidable_cookie.collidesWith(cookie)) {
|
|
if (remove) {
|
|
cookies_list.splice(i, 1);
|
|
if (cookies_list.length === 0) {
|
|
delete cookies[cookie.name];
|
|
}
|
|
return false;
|
|
}
|
|
cookies_list[i] = cookie;
|
|
return cookie;
|
|
}
|
|
}
|
|
if (remove) {
|
|
return false;
|
|
}
|
|
cookies_list.push(cookie);
|
|
return cookie;
|
|
}
|
|
if (remove) {
|
|
return false;
|
|
}
|
|
cookies[cookie.name] = [cookie];
|
|
return cookies[cookie.name];
|
|
};
|
|
//returns a cookie
|
|
this.getCookie = function getCookie(cookie_name, access_info) {
|
|
var cookie, i;
|
|
cookies_list = cookies[cookie_name];
|
|
if (!cookies_list) {
|
|
return;
|
|
}
|
|
for (i = 0; i < cookies_list.length; i += 1) {
|
|
cookie = cookies_list[i];
|
|
if (cookie.expiration_date <= Date.now()) {
|
|
if (cookies_list.length === 0) {
|
|
delete cookies[cookie.name];
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (cookie.matches(access_info)) {
|
|
return cookie;
|
|
}
|
|
}
|
|
};
|
|
//returns a list of cookies
|
|
this.getCookies = function getCookies(access_info) {
|
|
var matches = [], cookie_name, cookie;
|
|
for (cookie_name in cookies) {
|
|
cookie = this.getCookie(cookie_name, access_info);
|
|
if (cookie) {
|
|
matches.push(cookie);
|
|
}
|
|
}
|
|
matches.toString = function toString() {
|
|
return matches.join(":");
|
|
};
|
|
matches.toValueString = function toValueString() {
|
|
return matches.map(function (c) {
|
|
return c.toValueString();
|
|
}).join('; ');
|
|
};
|
|
return matches;
|
|
};
|
|
|
|
return this;
|
|
}
|
|
return new CookieJar();
|
|
}
|
|
exports.CookieJar = CookieJar;
|
|
|
|
//returns list of cookies that were set correctly. Cookies that are expired and removed are not returned.
|
|
CookieJar.prototype.setCookies = function setCookies(cookies, request_domain, request_path) {
|
|
cookies = Array.isArray(cookies) ?
|
|
cookies :
|
|
cookies.split(cookie_str_splitter);
|
|
var successful = [],
|
|
i,
|
|
cookie;
|
|
cookies = cookies.map(function(item){
|
|
return new Cookie(item, request_domain, request_path);
|
|
});
|
|
for (i = 0; i < cookies.length; i += 1) {
|
|
cookie = cookies[i];
|
|
if (this.setCookie(cookie, request_domain, request_path)) {
|
|
successful.push(cookie);
|
|
}
|
|
}
|
|
return successful;
|
|
};
|
|
}());
|