diff --git a/README.md b/README.md index 65a37e63..b357777e 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,26 @@ app.use(session({ })) ``` +##### hash + +Function to call to generate a hash of session object for detect changes. + +```js +app.use(session({ + hash: function(sess) { + // serialize + const { cookie, ...sessWithoutCookie } = sess; + const str = JSON.stringify(sessWithoutCookie); + // hash + return crypto + .createHash('sha1') + .update(str, 'utf8') + .digest('hex') + }, + secret: 'keyboard cat' +})) +``` + ##### name The name of the session ID cookie to set in the response (and read from in the diff --git a/index.js b/index.js index d41b2378..766ad2d1 100644 --- a/index.js +++ b/index.js @@ -80,6 +80,7 @@ var defer = typeof setImmediate === 'function' * @param {String|Array} [options.secret] Secret for signing session ID * @param {Object} [options.store=MemoryStore] Session store * @param {String} [options.unset] + * @param {Function} [options.hash] Hash method to detect changes in the session object * @return {Function} middleware * @public */ @@ -114,10 +115,17 @@ function session(options) { // get the cookie signing secret var secret = opts.secret + // get the hash method + var hash = opts.hash || hashSession + if (typeof generateId !== 'function') { throw new TypeError('genid option must be a function'); } + if (typeof hash !== 'function') { + throw new TypeError('hash option must be a function'); + } + if (resaveSession === undefined) { deprecate('undefined resave option; provide resave option'); resaveSession = true; @@ -601,7 +609,7 @@ function getcookie(req, name, secrets) { * @private */ -function hash(sess) { +function hashSession(sess) { // serialize var str = JSON.stringify(sess, function (key, val) { // ignore sess.cookie property diff --git a/test/session.js b/test/session.js index 7bf3e51f..0b80a9cf 100644 --- a/test/session.js +++ b/test/session.js @@ -843,6 +843,42 @@ describe('session()', function(){ }); }); + describe('hash option', function(){ + it('should reject non-function values', function(){ + assert.throws(session.bind(null, { hash: 'bogus!' }), /hash.*must/) + }); + + it('should provide default hash', function(done){ + request(createServer()) + .get('/') + .expect(shouldSetCookie('connect.sid')) + .expect(200, done) + }); + + it('should allow custom function', function(done){ + var counter = 0; + function hash() { + counter++; + var str = JSON.stringify(sess, function (key, val) { + if (this === sess && key === 'cookie') { + return + } + return val + }) + return str; + } + + var server = createServer({ hash: hash }, function (req, res) { + res.end('counter ' + counter) + }); + + request(server) + .get('/') + .expect(shouldSetCookie('connect.sid')) + .expect(200, 'counter 1', done) + }); + }); + describe('key option', function(){ it('should default to "connect.sid"', function(done){ request(createServer())