Permalink
Browse files

(Fixes: #46) Use TTL sessions for removing expired sessions, set brow…

…ser-session-length expiring cookies (maxAge: null) to expire after 2 weeks of inactivity
  • Loading branch information...
1 parent 28298d0 commit 0ccfb74bfe82aacd82d33d3bc4cc0cb9315026c4 Avi Romanoff committed Dec 17, 2012
Showing with 37 additions and 48 deletions.
  1. +15 −2 Readme.md
  2. +22 −11 lib/connect-mongo.js
  3. +0 −35 test/connect-mongo.test.js
View
@@ -25,8 +25,6 @@ via npm:
- `url` Connection url of the form: `mongodb://user:pass@host:port/database/collection`.
If provided, information in the URL takes priority over the other options.
- `mongoose_connection` in the form: `someMongooseDb.connections[0]` to use an existing mongoose connection. (optional)
- - `clear_interval` Interval in seconds to clear expired sessions (optional, default: `-1`).
- Values <= 0 disable expired session clearing.
- `stringify` If true, connect-mongo will serialize sessions using `JSON.stringify` before
setting them, and deserialize them with `JSON.parse` when getting them.
(optional, default: true). This is useful if you are using types that
@@ -55,6 +53,21 @@ With connect:
var connect = require('connect');
var MongoStore = require('connect-mongo')(connect);
+## Removing expired sessions
+
+ connect-mongo uses MongoDB's TTL collection feature (2.2+) to
+ have mongod automatically remove expired sessions. (mongod runs this
+ check every minute.)
+
+ **Note:** By connect/express's default, session cookies are set to
+ expire when the user closes their browser (maxAge: null). In accordance
+ with standard industry practices, connect-mongo will set these sessions
+ to expire two weeks from their last 'set'. You can override this
+ behavior by manually setting the maxAge for your cookies -- just keep in
+ mind that any value less than 60 seconds is pointless, as mongod will
+ only delete expired documents in a TTL collection every minute.
+
+ For more information, consult connect's [session documentation](http://www.senchalabs.org/connect/session.html)
## Tests
View
@@ -20,8 +20,7 @@ var defaultOptions = {host: '127.0.0.1',
port: 27017,
stringify: true,
collection: 'sessions',
- auto_reconnect: false,
- clear_interval: -1};
+ auto_reconnect: false};
module.exports = function(connect) {
var Store = connect.session.Store;
@@ -122,13 +121,15 @@ module.exports = function(connect) {
throw new Error('Error getting collection: ' + self.db_collection_name);
} else {
self.collection = collection;
-
- var clear_interval = options.clear_interval || defaultOptions.clear_interval;
- if (clear_interval > 0) {
- self.clear_interval = setInterval(function() {
- self.collection.remove({expires: {$lte: new Date()}});
- }, clear_interval * 1000, self);
- }
+
+ // Make sure we have a TTL index on "expires", so mongod will automatically
+ // remove expired sessions. expireAfterSeconds is set to 0 because we want
+ // mongo to remove anything expired without any additional delay.
+ self.collection.ensureIndex({expires: 1}, {expireAfterSeconds: 0}, function(err, result) {
+ if (err) {
+ throw new Error('Error setting TTL index on collection : ' + self.db_collection_name);
+ }
+ });
callback && callback(self.collection);
}
@@ -204,8 +205,18 @@ module.exports = function(connect) {
try {
var s = {_id: sid, session: this._serialize_session(session)};
- if (session && session.cookie && session.cookie._expires) {
- s.expires = new Date(session.cookie._expires);
+ if (session && session.cookie) {
+ if (session.cookie._expires) {
+ s.expires = new Date(session.cookie._expires);
+ } else {
+ // If there's no expiration date specified, it is
+ // browser-session cookie, as per the connect docs.
+ // So we set the expiration to two-weeks from now,
+ // as is common practice in the industry (e.g Django).
+ var today = new Date(),
+ twoWeeks = 1000 * 60 * 60 * 24 * 14;
+ s.expires = new Date(today.getTime() + twoWeeks);
+ }
}
this._get_collection(function(collection) {
View
@@ -53,7 +53,6 @@ var open_db = function(options, callback) {
};
var cleanup_store = function(store) {
- clearInterval(store.clear_interval);
store.db.close();
};
@@ -234,23 +233,6 @@ exports.test_options_no_db = function(done) {
done();
};
-exports.test_clear_expired = function(done) {
- open_db({db: options.db, clear_interval: 0.1}, function(store, db, collection) {
- var sid = 'test_clear_expired-sid';
- store.set(sid, {foo:'bar', cookie: {_expires: '2011-04-26T03:10:12.890Z'}}, function(err, session) {
- setTimeout(function() {
- collection.find({_id: sid}).toArray(function(err, results) {
- assert.strictEqual(results.length, 0);
-
- cleanup(store, db, collection, function() {
- done();
- });
- });
- }, 150);
- });
- });
-};
-
exports.test_options_url_and_db = function(done){
var store = new MongoStore({
url: 'mongodb://test:test@127.0.0.1:27017/',
@@ -405,23 +387,6 @@ exports.test_clear_with_raw_db = function(done) {
});
};
-exports.test_clear_expired_with_raw_db = function(done) {
- open_db({mongoose_connection: options_with_mongoose_connection.mongoose_connection, clear_interval: 0.1}, function(store, db, collection) {
- var sid = 'test_clear_expired-sid';
- store.set(sid, {foo:'bar', cookie: {_expires: '2011-04-26T03:10:12.890Z'}}, function(err, session) {
- setTimeout(function() {
- collection.find({_id: sid}).toArray(function(err, results) {
- assert.strictEqual(results.length, 0);
-
- cleanup(store, db, collection, function() {
- done();
- });
- });
- }, 150);
- });
- });
-};
-
exports.test_options_bad_db_with_raw_db = function(done) {
assert.throws(
function() {

0 comments on commit 0ccfb74

Please sign in to comment.