You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
3.3 KiB
JavaScript
157 lines
3.3 KiB
JavaScript
/*!
|
|
* cookie
|
|
* Copyright(c) 2012-2014 Roman Shtylman
|
|
* Copyright(c) 2015 Douglas Christopher Wilson
|
|
* MIT Licensed
|
|
*/
|
|
|
|
/**
|
|
* Module exports.
|
|
* @public
|
|
*/
|
|
|
|
exports.parse = parse;
|
|
exports.serialize = serialize;
|
|
|
|
/**
|
|
* Module variables.
|
|
* @private
|
|
*/
|
|
|
|
var decode = decodeURIComponent;
|
|
var encode = encodeURIComponent;
|
|
|
|
/**
|
|
* RegExp to match field-content in RFC 7230 sec 3.2
|
|
*
|
|
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
|
* field-vchar = VCHAR / obs-text
|
|
* obs-text = %x80-FF
|
|
*/
|
|
|
|
var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
|
|
|
|
/**
|
|
* Parse a cookie header.
|
|
*
|
|
* Parse the given cookie header string into an object
|
|
* The object has the various cookies as keys(names) => values
|
|
*
|
|
* @param {string} str
|
|
* @param {object} [options]
|
|
* @return {object}
|
|
* @public
|
|
*/
|
|
|
|
function parse(str, options) {
|
|
if (typeof str !== 'string') {
|
|
throw new TypeError('argument str must be a string');
|
|
}
|
|
|
|
var obj = {}
|
|
var opt = options || {};
|
|
var pairs = str.split(/; */);
|
|
var dec = opt.decode || decode;
|
|
|
|
pairs.forEach(function(pair) {
|
|
var eq_idx = pair.indexOf('=')
|
|
|
|
// skip things that don't look like key=value
|
|
if (eq_idx < 0) {
|
|
return;
|
|
}
|
|
|
|
var key = pair.substr(0, eq_idx).trim()
|
|
var val = pair.substr(++eq_idx, pair.length).trim();
|
|
|
|
// quoted values
|
|
if ('"' == val[0]) {
|
|
val = val.slice(1, -1);
|
|
}
|
|
|
|
// only assign once
|
|
if (undefined == obj[key]) {
|
|
obj[key] = tryDecode(val, dec);
|
|
}
|
|
});
|
|
|
|
return obj;
|
|
}
|
|
|
|
/**
|
|
* Serialize data into a cookie header.
|
|
*
|
|
* Serialize the a name value pair into a cookie string suitable for
|
|
* http headers. An optional options object specified cookie parameters.
|
|
*
|
|
* serialize('foo', 'bar', { httpOnly: true })
|
|
* => "foo=bar; httpOnly"
|
|
*
|
|
* @param {string} name
|
|
* @param {string} val
|
|
* @param {object} [options]
|
|
* @return {string}
|
|
* @public
|
|
*/
|
|
|
|
function serialize(name, val, options) {
|
|
var opt = options || {};
|
|
var enc = opt.encode || encode;
|
|
|
|
if (!fieldContentRegExp.test(name)) {
|
|
throw new TypeError('argument name is invalid');
|
|
}
|
|
|
|
var value = enc(val);
|
|
|
|
if (value && !fieldContentRegExp.test(value)) {
|
|
throw new TypeError('argument val is invalid');
|
|
}
|
|
|
|
var pairs = [name + '=' + value];
|
|
|
|
if (null != opt.maxAge) {
|
|
var maxAge = opt.maxAge - 0;
|
|
if (isNaN(maxAge)) throw new Error('maxAge should be a Number');
|
|
pairs.push('Max-Age=' + maxAge);
|
|
}
|
|
|
|
if (opt.domain) {
|
|
if (!fieldContentRegExp.test(opt.domain)) {
|
|
throw new TypeError('option domain is invalid');
|
|
}
|
|
|
|
pairs.push('Domain=' + opt.domain);
|
|
}
|
|
|
|
if (opt.path) {
|
|
if (!fieldContentRegExp.test(opt.path)) {
|
|
throw new TypeError('option path is invalid');
|
|
}
|
|
|
|
pairs.push('Path=' + opt.path);
|
|
}
|
|
|
|
if (opt.expires) pairs.push('Expires=' + opt.expires.toUTCString());
|
|
if (opt.httpOnly) pairs.push('HttpOnly');
|
|
if (opt.secure) pairs.push('Secure');
|
|
|
|
return pairs.join('; ');
|
|
}
|
|
|
|
/**
|
|
* Try decoding a string using a decoding function.
|
|
*
|
|
* @param {string} str
|
|
* @param {function} decode
|
|
* @private
|
|
*/
|
|
|
|
function tryDecode(str, decode) {
|
|
try {
|
|
return decode(str);
|
|
} catch (e) {
|
|
return str;
|
|
}
|
|
}
|