diff --git a/History.md b/History.md index c44ab618..a1b4e0b9 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Add `resave` option to control saving unmodified sessions + 1.1.0 / 2014-05-12 ================== diff --git a/README.md b/README.md index 605200e0..a4a8810e 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ middleware _before_ `session()`. - `cookie` - session cookie settings. - (default: `{ path: '/', httpOnly: true, secure: false, maxAge: null }`) - `rolling` - forces a cookie reset on response. The reset affects the expiration date. (default: `false`) + - `resave` - forces session to be saved even when unmodified. (default: `true`) #### Cookie options diff --git a/index.js b/index.js index 4d057fa0..abfe723d 100644 --- a/index.js +++ b/index.js @@ -72,6 +72,11 @@ function session(options){ , storeReady = true , rollingSessions = options.rolling || false; + // TODO: switch default to false on next major + var resaveSession = options.resave === undefined + ? true + : false; + // notify user that this store is not // meant for a production environment if ('production' == env && store instanceof MemoryStore) { @@ -153,7 +158,7 @@ function session(options){ return } // compare hashes and ids - } else if (originalHash == hash(req.session) && originalId == req.session.id) { + } else if (!isModified(req.session)) { debug('unmodified session'); return } @@ -170,13 +175,18 @@ function session(options){ res.end = function(data, encoding){ res.end = end; if (!req.session) return res.end(data, encoding); - debug('saving'); req.session.resetMaxAge(); - req.session.save(function(err){ - if (err) console.error(err.stack); - debug('saved'); - res.end(data, encoding); - }); + + if (resaveSession || isModified(req.session)) { + debug('saving'); + return req.session.save(function(err){ + if (err) console.error(err.stack); + debug('saved'); + res.end(data, encoding); + }); + } + + res.end(data, encoding); }; // generate the session @@ -184,6 +194,11 @@ function session(options){ store.generate(req); } + // check if session has been modified + function isModified(sess) { + return originalHash != hash(sess) || originalId != sess.id; + } + // get the sessionID from the cookie req.sessionID = unsignedCookie; diff --git a/test/session.js b/test/session.js index 7e05b024..bfdda072 100644 --- a/test/session.js +++ b/test/session.js @@ -113,6 +113,96 @@ describe('session()', function(){ }) }) + describe('resave option', function(){ + it('should default to true', function(done){ + var count = 0; + var app = express(); + app.use(cookieParser()); + app.use(session({ secret: 'keyboard cat', cookie: { maxAge: min }})); + app.use(function(req, res, next){ + var save = req.session.save; + res.setHeader('x-count', count); + req.session.user = 'bob'; + req.session.save = function(fn){ + res.setHeader('x-count', ++count); + return save.call(this, fn); + }; + res.end(); + }); + + request(app) + .get('/') + .expect('x-count', '1') + .expect(200, function(err, res){ + if (err) return done(err); + request(app) + .get('/') + .set('Cookie', 'connect.sid=' + sid(res)) + .expect('x-count', '2') + .expect(200, done); + }); + }); + + it('should prevent save on unmodified session', function(done){ + var count = 0; + var app = express(); + app.use(cookieParser()); + app.use(session({ resave: false, secret: 'keyboard cat', cookie: { maxAge: min }})); + app.use(function(req, res, next){ + var save = req.session.save; + res.setHeader('x-count', count); + req.session.user = 'bob'; + req.session.save = function(fn){ + res.setHeader('x-count', ++count); + return save.call(this, fn); + }; + res.end(); + }); + + request(app) + .get('/') + .expect('x-count', '1') + .expect(200, function(err, res){ + if (err) return done(err); + request(app) + .get('/') + .set('Cookie', 'connect.sid=' + sid(res)) + .expect('x-count', '1') + .expect(200, done); + }); + }); + + it('should still save modified session', function(done){ + var count = 0; + var app = express(); + app.use(cookieParser()); + app.use(session({ resave: false, secret: 'keyboard cat', cookie: { maxAge: min }})); + app.use(function(req, res, next){ + var save = req.session.save; + res.setHeader('x-count', count); + req.session.count = count; + req.session.user = 'bob'; + req.session.save = function(fn){ + res.setHeader('x-count', ++count); + return save.call(this, fn); + }; + res.end(); + }); + + request(app) + .get('/') + .expect('x-count', '1') + .expect(200, function(err, res){ + if (err) return done(err); + request(app) + .get('/') + .set('Cookie', 'connect.sid=' + sid(res)) + .expect('x-count', '2') + .expect(200, done); + }); + }); + }); + it('should retain the sid', function(done){ var n = 0;