sgapps-server
Version:
SGApps Network Server
232 lines (184 loc) • 6.45 kB
JavaScript
//@ts-nocheck
/*!
* cookies
* Copyright(c) 2014 Jed Schmidt, http://jed.is/
* Copyright(c) 2015-2016 Douglas Christopher Wilson
* MIT Licensed
*/
var deprecate = require('../depd')('cookies');
var Keygrip = require('../keygrip');
var http = require('http');
var cache = {};
/**
* 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]+$/;
/**
* RegExp to match Same-Site cookie attribute value.
*/
var sameSiteRegExp = /^(?:lax|strict)$/i;
function Cookies(request, response, options) {
if (!(this instanceof Cookies)) return new Cookies(request, response, options);
this.secure = undefined;
this.request = request;
this.response = response;
if (options) {
if (Array.isArray(options)) {
// array of key strings
deprecate('"keys" argument; provide using options {"keys": [...]}');
this.keys = new Keygrip(options);
} else if (options.constructor && options.constructor.name === 'Keygrip') {
// any keygrip constructor to allow different versions
deprecate('"keys" argument; provide using options {"keys": keygrip}');
this.keys = options;
} else {
this.keys = Array.isArray(options.keys) ? new Keygrip(options.keys) : options.keys;
this.secure = options.secure;
}
}
}
Cookies.prototype.get = function(name, opts) {
var sigName = name + ".sig", header, match, value, remote, data, index,
signed = opts && opts.signed !== undefined ? opts.signed : !!this.keys;
header = this.request.headers.cookie;
if (!header) return;
match = header.match(getPattern(name));
if (!match) return;
value = match[1];
if (!opts || !signed) return value;
remote = this.get(sigName);
if (!remote) return;
data = name + "=" + value;
if (!this.keys) throw new Error('.keys required for signed cookies');
index = this.keys.index(data, remote);
if (index < 0) {
this.set(sigName, null, {path: "/", signed: false }, true);
} else {
index && this.set(sigName, this.keys.sign(data), { signed: false }, true);
return value;
}
};
Cookies.prototype.set = function(name, value, opts, skipError) {
var res = this.response,
req = this.request,
headers = res.getHeader("Set-Cookie") || [],
secure = this.secure !== undefined ? !!this.secure : req.protocol === 'https' || (req.connection || {}).encrypted,
cookie = new Cookie(name, value, opts),
signed = opts && opts.signed !== undefined ? opts.signed : !!this.keys;
if (typeof headers == "string") headers = [headers];
if (!secure && opts && opts.secure) {
if (skipError) return this;
throw new Error('Cannot send secure cookie over unencrypted connection');
}
cookie.secure = secure;
if (opts && "secure" in opts) cookie.secure = opts.secure;
if (opts && "secureProxy" in opts) {
if (!skipError) {
deprecate('"secureProxy" option; use "secure" option, provide "secure" to constructor if needed');
}
cookie.secure = opts.secureProxy;
}
pushCookie(headers, cookie);
if (opts && signed) {
if (!this.keys) {
if (skipError) return this;
throw new Error('.keys required for signed cookies');
}
cookie.value = this.keys.sign(cookie.toString());
cookie.name += ".sig";
pushCookie(headers, cookie);
}
// disabled
// var setHeader = res.set ? http.OutgoingMessage.prototype.setHeader : res.setHeader
var setHeader = res.setHeader || http.OutgoingMessage.prototype.setHeader;
setHeader.call(res, 'Set-Cookie', headers);
return this;
};
function Cookie(name, value, attrs) {
if (!fieldContentRegExp.test(name)) {
throw new TypeError('argument name is invalid');
}
if (value && !fieldContentRegExp.test(value)) {
throw new TypeError('argument value is invalid');
}
if (!value) {
this.expires = new Date(0);
}
this.name = name;
this.value = value || "";
var itemName;
for (itemName in attrs) {
this[itemName] = attrs[itemName];
}
if (this.path && !fieldContentRegExp.test(this.path)) {
throw new TypeError('option path is invalid');
}
if (this.domain && !fieldContentRegExp.test(this.domain)) {
throw new TypeError('option domain is invalid');
}
if (this.sameSite && this.sameSite !== true && !sameSiteRegExp.test(this.sameSite)) {
throw new TypeError('option sameSite is invalid');
}
}
Cookie.prototype.path = "/";
Cookie.prototype.expires = undefined;
Cookie.prototype.domain = undefined;
Cookie.prototype.httpOnly = true;
Cookie.prototype.sameSite = false;
Cookie.prototype.secure = false;
Cookie.prototype.overwrite = false;
Cookie.prototype.toString = function() {
return this.name + "=" + this.value;
};
Cookie.prototype.toHeader = function() {
var header = this.toString();
if (this.maxAge) this.expires = new Date(Date.now() + this.maxAge);
if (this.path ) header += "; path=" + this.path;
if (this.expires ) header += "; expires=" + this.expires.toUTCString();
if (this.domain ) header += "; domain=" + this.domain;
if (this.sameSite ) header += "; samesite=" + (this.sameSite === true ? 'strict' : this.sameSite.toLowerCase());
if (this.secure ) header += "; secure";
if (this.httpOnly ) header += "; httponly";
return header;
};
// back-compat so maxage mirrors maxAge
Object.defineProperty(Cookie.prototype, 'maxage', {
configurable: true,
enumerable: true,
get: function () { return this.maxAge; },
set: function (val) { this.maxAge = val; return this.maxAge; }
});
deprecate.property(Cookie.prototype, 'maxage', '"maxage"; use "maxAge" instead');
function getPattern(name) {
if (cache[name]) return cache[name];
cache[name] = new RegExp(
"(?:^|;) *" +
name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") +
"=([^;]*)"
);
return cache[name];
}
function pushCookie(headers, cookie) {
if (cookie.overwrite) {
for (var i = headers.length - 1; i >= 0; i--) {
if (headers[i].indexOf(cookie.name + '=') === 0) {
headers.splice(i, 1);
}
}
}
headers.push(cookie.toHeader());
}
Cookies.connect = Cookies.express = function(keys) {
return function(req, res, next) {
req.cookies = res.cookies = new Cookies(req, res, {
keys: keys
});
next();
};
};
Cookies.Cookie = Cookie;
module.exports = Cookies;