Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Eran Hammer committed Aug 22, 2014
1 parent aae57a6 commit df69788
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 118 deletions.
149 changes: 63 additions & 86 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,26 @@ var internals = {};


internals.defaults = {

// Global settings

strictHeader: true,
failAction: 'error', // 'error' bails on first error, other values ignored

// Per cookie settings

global: {
strictHeader: true,
failAction: 'error', // 'error' bails on first error, other values ignored
},
cookie: {

// Validation settings

strictHeader: undefined, // Defaults to global strictHeader
failAction: undefined, // Defaults to global failAction

// Cookie attributes

isSecure: false,
isHttpOnly: false,
path: null,
domain: null,
ttl: null, // MSecs, 0 means remove

// Value generation

encoding: 'none' // options: 'base64json', 'base64', 'form', 'iron', 'none'
}
};


exports.Definitions = internals.Definitions = function (options) {

this.settings = Hoek.applyToDefaults(internals.defaults.global, options || {});
this.cookieDefaults = Hoek.applyToDefaults(internals.defaults.cookie, this.settings);
this.cookies = {};

this.strictHeader = options && options.strictHeader !== undefined ? options.strictHeader : internals.defaults.strictHeader;
this.failAction = options && options.failAction !== undefined ? options.failAction : internals.defaults.failAction;
};


Expand All @@ -58,10 +42,13 @@ internals.Definitions.prototype.add = function (name, options) {
Hoek.assert(name && typeof name === 'string', 'Invalid name');
Hoek.assert(!this.cookies[name], 'State already defined:', name);

this.cookies[name] = Hoek.applyToDefaults(internals.defaults.cookie, options || {});
this.cookies[name] = Hoek.applyToDefaults(this.cookieDefaults, options || {});
};


internals.empty = new internals.Definitions();


// Header format

// 1: name 2: quoted 3: value
Expand All @@ -84,10 +71,8 @@ internals.validateRx = {

exports.parse = function (cookies, definitions, next) {

var invalids = {};
var state = {};
var names = [];

var verify = cookies.replace(internals.parseRx, function ($0, $1, $2, $3) {

var name = $1;
Expand All @@ -111,34 +96,52 @@ exports.parse = function (cookies, definitions, next) {
// Validate cookie header syntax

if (verify !== '' &&
internals.shouldStop(cookies, null, null, definitions, invalids, next)) {
definitions.settings.failAction === 'error') {

return; // shouldStop calls next()
return next(Boom.badRequest('Bad cookie header'), null, {});
}

// Fail action

var invalids = {};

var shouldStop = function (error, name, value, definition) {

invalids[name] = {
value: value,
settings: definition,
reason: error.message
};

if (definition.failAction === 'error') {
next(Boom.badRequest('Bad cookie value: ' + Hoek.escapeHtml(name)), null, invalids);
return true;
}

return false;
};

// Parse cookies

var parsed = {};
Items.serial(names, function (name, nextName) {

var value = state[name];
var definition = definitions.cookies[name];
var definition = definitions.cookies[name] || definitions.cookieDefaults;

// Validate cookie

if (definition &&
definition.strictHeader !== undefined ? definition.strictHeader : definitions.strictHeader) {

if (definition.strictHeader) {
if (!name.match(internals.validateRx.nameRx.strict)) {
if (internals.shouldStop(cookies, name, definition, definitions, invalids, next)) {
if (shouldStop(Boom.badRequest('Invalid cookie name'), name, value, definition)) {
return; // shouldStop calls next()
}
}

var values = [].concat(state[name]);
for (var v = 0, vl = values.length; v < vl; ++v) {
if (!values[v].match(internals.validateRx.valueRx.strict)) {
if (internals.shouldStop(cookies, name, definition, definitions, invalids, next)) {
if (shouldStop(Boom.badRequest('Invalid cookie value'), name, value, definition)) {
return; // shouldStop calls next()
}
}
Expand All @@ -147,9 +150,7 @@ exports.parse = function (cookies, definitions, next) {

// Check cookie format

if (!definition ||
!definition.encoding) {

if (definition.encoding === 'none') {
parsed[name] = value;
return nextName();
}
Expand All @@ -160,7 +161,7 @@ exports.parse = function (cookies, definitions, next) {
internals.unsign(name, value, definition, function (err, unsigned) {

if (err) {
if (internals.shouldStop({ name: name, value: value, settings: definition, reason: err.message }, name, definition, definitions, invalids, next)) {
if (shouldStop(err, name, value, definition)) {
return; // shouldStop calls next()
}

Expand All @@ -170,7 +171,7 @@ exports.parse = function (cookies, definitions, next) {
internals.decode(unsigned, definition, function (err, result) {

if (err) {
if (internals.shouldStop({ name: name, value: value, settings: definition, reason: err.message }, name, definition, definitions, invalids, next)) {
if (shouldStop(err, name, value, definition)) {
return; // shouldStop calls next()
}

Expand All @@ -193,7 +194,7 @@ exports.parse = function (cookies, definitions, next) {
internals.unsign(name, arrayValue, definition, function (err, unsigned) {

if (err) {
if (internals.shouldStop({ name: name, value: value, settings: definition, reason: err.message }, name, definition, definitions, invalids, next)) {
if (shouldStop(err, name, value, definition)) {
return; // shouldStop calls next()
}

Expand All @@ -203,7 +204,7 @@ exports.parse = function (cookies, definitions, next) {
internals.decode(unsigned, definition, function (err, result) {

if (err) {
if (internals.shouldStop({ name: name, value: value, settings: definition, reason: err.message }, name, definition, definitions, invalids, next)) {
if (shouldStop(err, name, value, definition)) {
return; // shouldStop calls next()
}

Expand All @@ -230,24 +231,6 @@ exports.parse = function (cookies, definitions, next) {
};


internals.shouldStop = function (error, name, definition, definitions, invalids, next) {

if (name) {
invalids[name] = error;
}

// failAction: 'error', 'log', 'ignore'

var failAction = (definition && definition.failAction !== undefined ? definition.failAction : definitions.failAction);
if (failAction === 'error') {
next(Boom.badRequest('Bad cookie ' + (name ? 'value: ' + Hoek.escapeHtml(name) : 'header')), null, invalids);
return true;
}

return false;
};


internals.macPrefix = 'hapi.signed.cookie.1';


Expand Down Expand Up @@ -337,7 +320,7 @@ internals.decode = function (value, definition, next) {

exports.format = function (cookies, definitions, callback) {

definitions = definitions || { cookies: {}, strictHeader: internals.defaults.strictHeader };
definitions = definitions || internals.empty;

if (!cookies ||
(Array.isArray(cookies) && !cookies.length)) {
Expand All @@ -352,33 +335,29 @@ exports.format = function (cookies, definitions, callback) {
var header = [];
Items.serial(cookies, function (cookie, next) {

var settings = cookie.options || {};

// Apply definition to local configuration

if (definitions.cookies[cookie.name]) {
settings = Hoek.applyToDefaults(definitions.cookies[cookie.name], settings);
}
var base = definitions.cookies[cookie.name] || definitions.cookieDefaults;
var definition = cookie.options ? Hoek.applyToDefaults(base, cookie.options) : base;

// Validate name

var strictHeader = settings.strictHeader !== undefined ? settings.strictHeader : definitions.strictHeader;
var nameRx = (strictHeader ? internals.validateRx.nameRx.strict : internals.validateRx.nameRx.loose);
if (!cookie.name.match(nameRx)) {
var nameRx = (definition.strictHeader ? internals.validateRx.nameRx.strict : internals.validateRx.nameRx.loose);
if (!nameRx.test(cookie.name)) {
return callback(Boom.badImplementation('Invalid cookie name: ' + cookie.name));
}

// Prepare value (encode, sign)

exports.prepareValue(cookie.name, cookie.value, settings, function (err, value) {
exports.prepareValue(cookie.name, cookie.value, definition, function (err, value) {

if (err) {
return callback(err);
}

// Validate prepared value

var valueRx = (strictHeader ? internals.validateRx.valueRx.strict : internals.validateRx.valueRx.loose);
var valueRx = (definition.strictHeader ? internals.validateRx.valueRx.strict : internals.validateRx.valueRx.loose);
if (value &&
(typeof value !== 'string' || !value.match(valueRx))) {

Expand All @@ -389,40 +368,40 @@ exports.format = function (cookies, definitions, callback) {

var segment = cookie.name + '=' + (value || '');

if (settings.ttl !== null &&
settings.ttl !== undefined) { // Can be zero
if (definition.ttl !== null &&
definition.ttl !== undefined) { // Can be zero

var expires = new Date(settings.ttl ? Date.now() + settings.ttl : 0);
segment += '; Max-Age=' + Math.floor(settings.ttl / 1000) + '; Expires=' + expires.toUTCString();
var expires = new Date(definition.ttl ? Date.now() + definition.ttl : 0);
segment += '; Max-Age=' + Math.floor(definition.ttl / 1000) + '; Expires=' + expires.toUTCString();
}

if (settings.isSecure) {
if (definition.isSecure) {
segment += '; Secure';
}

if (settings.isHttpOnly) {
if (definition.isHttpOnly) {
segment += '; HttpOnly';
}

if (settings.domain) {
var domain = settings.domain.toLowerCase();
if (definition.domain) {
var domain = definition.domain.toLowerCase();
if (!domain.match(internals.validateRx.domainLabelLenRx)) {
return callback(Boom.badImplementation('Cookie domain too long: ' + settings.domain));
return callback(Boom.badImplementation('Cookie domain too long: ' + definition.domain));
}

if (!domain.match(internals.validateRx.domainRx)) {
return callback(Boom.badImplementation('Invalid cookie domain: ' + settings.domain));
return callback(Boom.badImplementation('Invalid cookie domain: ' + definition.domain));
}

segment += '; Domain=' + domain;
}

if (settings.path) {
if (!settings.path.match(internals.validateRx.pathRx)) {
return callback(Boom.badImplementation('Invalid cookie path: ' + settings.path));
if (definition.path) {
if (!definition.path.match(internals.validateRx.pathRx)) {
return callback(Boom.badImplementation('Invalid cookie path: ' + definition.path));
}

segment += '; Path=' + settings.path;
segment += '; Path=' + definition.path;
}

header.push(segment);
Expand Down Expand Up @@ -472,9 +451,7 @@ internals.encode = function (value, options, callback) {
return callback(null, value);
}

if (!options.encoding ||
options.encoding === 'none') {

if (options.encoding === 'none') {
return callback(null, value);
}

Expand Down
Loading

0 comments on commit df69788

Please sign in to comment.