session-file-store = function (session) { return require('./lib/session-file-store')(session); }
n/a
asyncReap = function (options, callback) { callback || (callback = function () { }); function execCallback(err) { if (err && options.reapSyncFallback) { helpers.reap(options, callback); } else { err ? callback(err) : callback(); } } if (isWindows) { childProcess.execFile('node', [path.join(__dirname, 'reap-worker.js'), options.path, options.ttl], execCallback); } else { childProcess.execFile(path.join(__dirname, 'reap-worker.js'), [options.path, options.ttl], execCallback); } }
...
},
scheduleReap: function (options) {
if (options.reapInterval !== -1) {
options.reapIntervalObject = setInterval(function () {
if (options.reapAsync) {
options.logFn('[session-file-store] Starting reap worker thread');
helpers.asyncReap(options);
} else {
options.logFn('[session-file-store] Deleting expired sessions');
helpers.reap(options);
}
}, options.reapInterval * 1000);
}
},
...
clear = function (options, callback) { fs.readdir(options.path, function (err, files) { if (err) return callback([err]); if (files.length <= 0) return callback(); var errors = []; files.forEach(function (file, i) { if (options.filePattern.exec(file)) { fs.remove(path.join(options.path, file), function (err) { if (err) { errors.push(err); } // TODO: wrong call condition (call after all completed attempts to remove instead of after completed attempt with last index) if (i >= files.length - 1) { errors.length > 0 ? callback(errors) : callback(); } }); } else { // TODO: wrong call condition (call after all completed attempts to remove instead of after completed attempt with last index) if (i >= files.length - 1) { errors.length > 0 ? callback(errors) : callback(); } } }); }); }
...
* Attempts to clear out all of the existing session files
*
* @param {Function} callback
*
* @api public
*/
FileStore.prototype.clear = function (callback) {
helpers.clear(this.options, callback);
};
/**
* Attempts to find all of the session files
*
* @param {Function} callback
*
...
decrypt = function (options, data, sessionId) { var decipher = crypto.createDecipher(helpers.encAlgorithm, options.keyFunction(options.secret, sessionId)); var dec = decipher.update(data, options.encryptEncoding, options.encoding); var decFinal = decipher.final(options.encoding); if ( typeof decFinal === 'string' ) { dec += decFinal; } else { dec = Buffer.concat([dec, decFinal], dec.length + decFinal.length); } return dec; }
...
operation.attempt(function () {
fs.readFile(sessionPath, helpers.isSecret(options.secret) && !options.encryptEncoding ? null : options.encoding,
function readCallback(err, data) {
if (!err) {
var json;
try {
json = options.decoder(helpers.isSecret(options.secret) ? helpers.decrypt(
options, data, sessionId) : data);
} catch (parseError) {
return fs.remove(sessionPath, function (removeError) {
if (removeError) {
return callback(removeError);
}
callback(parseError);
...
defaults = function (options) { options = options || {}; var NOOP_FN = function () { }; var KEY_FN = function (secret, sessionId) { return secret + sessionId; }; return { path: path.normalize(options.path || './sessions'), ttl: options.ttl || 3600, retries: options.retries || 5, factor: options.factor || 1, minTimeout: options.minTimeout || 50, maxTimeout: options.maxTimeout || 100, reapInterval: options.reapInterval || 3600, reapMaxConcurrent: options.reapMaxConcurrent || 10, reapAsync: options.reapAsync || false, reapSyncFallback: options.reapSyncFallback || false, logFn: options.logFn || console.log || NOOP_FN, fallbackSessionFn: options.fallbackSessionFn, encoding: options.encoding !== undefined ? options.encoding : 'utf8', encoder: options.encoder || JSON.stringify, decoder: options.decoder || JSON.parse, secret: options.secret, encryptEncoding: options.encryptEncoding !== undefined ? options.encryptEncoding : 'hex', fileExtension: options.fileExtension || '.json', filePattern: options.fileExtension ? helpers.getFilePatternFromFileExtension(options.fileExtension) : /\.json$/, keyFunction: options.keyFunction || KEY_FN }; }
...
#!/usr/bin/env node
var helpers = require('./session-file-helpers');
var options = helpers.defaults({
path: process.argv[2],
ttl: process.argv[3]
});
if (options.path) {
options.logFn('[session-file-store:worker] Deleting expired sessions');
helpers.reap(options);
...
destroy = function (sessionId, options, callback) { var sessionPath = helpers.sessionPath(options, sessionId); fs.remove(sessionPath, callback); }
...
keyFunction: options.keyFunction || KEY_FN
};
},
destroyIfExpired: function (sessionId, options, callback) {
helpers.expired(sessionId, options, function (err, expired) {
if (err == null && expired) {
helpers.destroy(sessionId, options, callback);
} else if (callback) {
err ? callback(err) : callback();
}
});
},
scheduleReap: function (options) {
...
destroyIfExpired = function (sessionId, options, callback) { helpers.expired(sessionId, options, function (err, expired) { if (err == null && expired) { helpers.destroy(sessionId, options, callback); } else if (callback) { err ? callback(err) : callback(); } }); }
n/a
encrypt = function (options, data, sessionId) { var cipher = crypto.createCipher(helpers.encAlgorithm, options.keyFunction(options.secret, sessionId)); var crypted = cipher.update(data, options.encoding, options.encryptEncoding ); var cryptedFinal = cipher.final(options.encryptEncoding); if ( typeof cryptedFinal === 'string' ) { crypted += cryptedFinal; } else { crypted = Buffer.concat([crypted, cryptedFinal], crypted.length + cryptedFinal.length); } return crypted; }
...
set: function (sessionId, session, options, callback) {
try {
helpers.setLastAccess(session);
var sessionPath = helpers.sessionPath(options, sessionId);
var json = options.encoder(session);
if (helpers.isSecret(options.secret)) {
json = helpers.encrypt(options, json, sessionId)
}
writeFileAtomic(sessionPath, json, function (err) {
if (callback) {
err ? callback(err) : callback(null, session);
}
});
} catch (err) {
...
escapeForRegExp = function (str) { return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); }
...
},
escapeForRegExp: function (str) {
return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
},
getFilePatternFromFileExtension: function (fileExtension) {
return new RegExp( helpers.escapeForRegExp(fileExtension) + '$' );
},
defaults: function (options) {
options = options || {};
var NOOP_FN = function () {
};
...
expired = function (sessionId, options, callback) { helpers.get(sessionId, options, function (err, session) { if (err) return callback(err); err ? callback(err) : callback(null, helpers.isExpired(session, options)); }); }
...
? helpers.getFilePatternFromFileExtension(options.fileExtension)
: /\.json$/,
keyFunction: options.keyFunction || KEY_FN
};
},
destroyIfExpired: function (sessionId, options, callback) {
helpers.expired(sessionId, options, function (err, expired) {
if (err == null && expired) {
helpers.destroy(sessionId, options, callback);
} else if (callback) {
err ? callback(err) : callback();
}
});
},
...
get = function (sessionId, options, callback) { var sessionPath = helpers.sessionPath(options, sessionId); var operation = retry.operation({ retries: options.retries, factor: options.factor, minTimeout: options.minTimeout, maxTimeout: options.maxTimeout }); operation.attempt(function () { fs.readFile(sessionPath, helpers.isSecret(options.secret) && !options.encryptEncoding ? null : options.encoding, function readCallback (err, data) { if (!err) { var json; try { json = options.decoder(helpers.isSecret(options.secret) ? helpers.decrypt(options, data, sessionId) : data); } catch (parseError) { return fs.remove(sessionPath, function (removeError) { if (removeError) { return callback(removeError); } callback(parseError); }); } if (!err) { return callback(null, helpers.isExpired(json, options) ? null : json); } } if (operation.retry(err)) { options.logFn('[session-file-store] will retry, error on last attempt: ' + err); } else if (options.fallbackSessionFn) { var session = options.fallbackSessionFn(sessionId); helpers.setLastAccess(session); callback(null, session); } else { callback(err); } }); }); }
...
* @param {String} sessionId
* @param {Object} options
* @param {Function} callback
*
* @api public
*/
expired: function (sessionId, options, callback) {
helpers.get(sessionId, options, function (err, session) {
if (err) return callback(err);
err ? callback(err) : callback(null, helpers.isExpired(session, options));
});
},
isExpired: function (session, options) {
...
getFilePatternFromFileExtension = function (fileExtension) { return new RegExp( helpers.escapeForRegExp(fileExtension) + '$' ); }
...
decoder: options.decoder || JSON.parse,
secret: options.secret,
encryptEncoding: options.encryptEncoding !== undefined
? options.encryptEncoding
: 'hex',
fileExtension: options.fileExtension || '.json',
filePattern: options.fileExtension
? helpers.getFilePatternFromFileExtension(options.fileExtension)
: /\.json$/,
keyFunction: options.keyFunction || KEY_FN
};
},
destroyIfExpired: function (sessionId, options, callback) {
helpers.expired(sessionId, options, function (err, expired) {
...
getLastAccess = function (session) { return session.__lastAccess; }
...
});
},
isExpired: function (session, options) {
if (!session) return true;
var ttl = session.cookie && session.cookie.originalMaxAge ? session.cookie.originalMaxAge : options.ttl * 1000;
return !ttl || helpers.getLastAccess(session) + ttl < new Date().getTime();
},
encrypt: function (options, data, sessionId) {
var cipher = crypto.createCipher(helpers.encAlgorithm, options.keyFunction(options.secret, sessionId));
var crypted = cipher.update(data, options.encoding, options.encryptEncoding );
var cryptedFinal = cipher.final(options.encryptEncoding);
if ( typeof cryptedFinal === 'string' ) {
...
isExpired = function (session, options) { if (!session) return true; var ttl = session.cookie && session.cookie.originalMaxAge ? session.cookie.originalMaxAge : options.ttl * 1000; return !ttl || helpers.getLastAccess(session) + ttl < new Date().getTime(); }
...
return callback(removeError);
}
callback(parseError);
});
}
if (!err) {
return callback(null, helpers.isExpired(json, options) ? null : json);
}
}
if (operation.retry(err)) {
options.logFn('[session-file-store] will retry, error on last attempt: ' + err);
} else if (options.fallbackSessionFn) {
var session = options.fallbackSessionFn(sessionId);
...
isSecret = function (secret) { return secret !== undefined && secret != null; }
...
factor: options.factor,
minTimeout: options.minTimeout,
maxTimeout: options.maxTimeout
});
operation.attempt(function () {
fs.readFile(sessionPath, helpers.isSecret(options.secret) && !options
.encryptEncoding ? null : options.encoding, function readCallback(err, data) {
if (!err) {
var json;
try {
json = options.decoder(helpers.isSecret(options.secret) ? helpers.decrypt(options, data, sessionId) : data);
} catch (parseError) {
return fs.remove(sessionPath, function (removeError) {
...
length = function (options, callback) { fs.readdir(options.path, function (err, files) { if (err) return callback(err); var result = 0; files.forEach(function (file) { if (options.filePattern.exec(file)) { ++result; } }); callback(null, result); }); }
...
* Attempts to fetch number of the session files
*
* @param {Function} callback
*
* @api public
*/
FileStore.prototype.length = function (callback) {
helpers.length(this.options, callback);
};
/**
* Attempts to clear out all of the existing session files
*
* @param {Function} callback
*
...
list = function (options, callback) { fs.readdir(options.path, function (err, files) { if (err) return callback(err); files = files.filter(function (file) { return options.filePattern.exec(file); }); callback(null, files); }); }
...
childProcess.execFile(path.join(__dirname, 'reap-worker.js'), [options.path, options.ttl], execCallback);
}
},
reap: function (options, callback) {
callback || (callback = function () {
});
helpers.list(options, function (err, files) {
if (err) return callback(err);
if (files.length === 0) return callback();
var bagpipe = new Bagpipe(options.reapMaxConcurrent);
var errors = [];
files.forEach(function (file, i) {
...
reap = function (options, callback) { callback || (callback = function () { }); helpers.list(options, function (err, files) { if (err) return callback(err); if (files.length === 0) return callback(); var bagpipe = new Bagpipe(options.reapMaxConcurrent); var errors = []; files.forEach(function (file, i) { bagpipe.push(helpers.destroyIfExpired, helpers.sessionId(options,file), options, function (err) { if (err) { errors.push(err); } if (i >= files.length - 1) { errors.length > 0 ? callback(errors) : callback(); } }); }); }); }
...
var options = helpers.defaults({
path: process.argv[2],
ttl: process.argv[3]
});
if (options.path) {
options.logFn('[session-file-store:worker] Deleting expired sessions');
helpers.reap(options);
} else {
options.logFn('[session-file-store:worker] Reap worker started with invalid path');
process.exit(1);
}
...
scheduleReap = function (options) { if (options.reapInterval !== -1) { options.reapIntervalObject = setInterval(function () { if (options.reapAsync) { options.logFn('[session-file-store] Starting reap worker thread'); helpers.asyncReap(options); } else { options.logFn('[session-file-store] Deleting expired sessions'); helpers.reap(options); } }, options.reapInterval * 1000); } }
...
var self = this;
options = options || {};
Store.call(self, options);
self.options = helpers.defaults(options);
fs.mkdirsSync(self.options.path);
helpers.scheduleReap(self.options);
options.reapIntervalObject = self.options.reapIntervalObject;
}
/**
* Inherit from Store
*/
FileStore.prototype.__proto__ = Store.prototype;
...
sessionId = function (options, file) { //return file.substring(0, file.lastIndexOf('.json')); if ( options.fileExtension.length === 0 ) return file; var id = file.replace(options.filePattern, ''); return id === file ? '' : id; }
...
if (files.length === 0) return callback();
var bagpipe = new Bagpipe(options.reapMaxConcurrent);
var errors = [];
files.forEach(function (file, i) {
bagpipe.push(helpers.destroyIfExpired,
helpers.sessionId(options,file),
options,
function (err) {
if (err) {
errors.push(err);
}
if (i >= files.length - 1) {
errors.length > 0 ? callback(errors) : callback();
...
sessionPath = function (options, sessionId) { //return path.join(basepath, sessionId + '.json'); return path.join(options.path, sessionId + options.fileExtension); }
...
* @param {String} sessionId
* @param {Object} options
* @param {Function} callback
*
* @api public
*/
get: function (sessionId, options, callback) {
var sessionPath = helpers.sessionPath(options, sessionId);
var operation = retry.operation({
retries: options.retries,
factor: options.factor,
minTimeout: options.minTimeout,
maxTimeout: options.maxTimeout
});
...
set = function (sessionId, session, options, callback) { try { helpers.setLastAccess(session); var sessionPath = helpers.sessionPath(options, sessionId); var json = options.encoder(session); if (helpers.isSecret(options.secret)) { json = helpers.encrypt(options, json, sessionId) } writeFileAtomic(sessionPath, json, function (err) { if (callback) { err ? callback(err) : callback(null, session); } }); } catch (err) { if (callback) callback(err); } }
...
* @param {String} sessionId
* @param {Object} session
* @param {Function} callback (optional)
*
* @api public
*/
FileStore.prototype.set = function (sessionId, session, callback) {
helpers.set(sessionId, session, this.options, callback);
};
/**
* Touch the given session object associated with the given `sessionId`
*
* @param {string} sessionId
* @param {object} session
...
setLastAccess = function (session) { session.__lastAccess = new Date().getTime(); }
...
}
}
if (operation.retry(err)) {
options.logFn('[session-file-store] will retry, error on last attempt: ' + err);
} else if (options.fallbackSessionFn) {
var session = options.fallbackSessionFn(sessionId);
helpers.setLastAccess(session);
callback(null, session);
} else {
callback(err);
}
});
});
},
...