From bf37d2d9811df538f522e8fe5cbcff762dec11a1 Mon Sep 17 00:00:00 2001 From: Maxim Savin Date: Thu, 12 Feb 2015 00:38:56 +0600 Subject: [PATCH 01/12] Added posibility to generate session id asynchronously --- index.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index 43d70e16..26a0e718 100644 --- a/index.js +++ b/index.js @@ -16,7 +16,7 @@ var crc = require('crc').crc32; var debug = require('debug')('express-session'); var deprecate = require('depd')('express-session'); var parseUrl = require('parseurl'); -var uid = require('uid-safe').sync +var uid = require('uid-safe') , onHeaders = require('on-headers') , signature = require('cookie-signature') @@ -123,10 +123,14 @@ function session(options){ } // generates the new session - store.generate = function(req){ - req.sessionID = generateId(req); - req.session = new Session(req); - req.session.cookie = new Cookie(cookie); + store.generate = function(req, callback) { + generateId(req, function(err, sessionID) { + if (err) throw err; + req.sessionID = sessionID; + req.session = new Session(req); + req.session.cookie = new Cookie(cookie); + callback && callback(req); + }); }; var storeImplementsTouch = typeof store.touch === 'function'; @@ -298,11 +302,13 @@ function session(options){ }; // generate the session - function generate() { - store.generate(req); - originalId = req.sessionID; - originalHash = hash(req.session); - wrapmethods(req.session); + function generate(callback) { + store.generate(req, function(req) { + originalId = req.sessionID; + originalHash = hash(req.session); + wrapmethods(req.session); + callback && callback(); + }); } // wrap session methods From 44a08686ab24ad8d7f44522f4cfb3b6e1829dfa1 Mon Sep 17 00:00:00 2001 From: Maxim Savin Date: Thu, 12 Feb 2015 01:08:35 +0600 Subject: [PATCH 02/12] Middleware chain (next function) through a callback --- index.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 26a0e718..f61a751e 100644 --- a/index.js +++ b/index.js @@ -388,8 +388,9 @@ function session(options){ // generate a session if the browser doesn't send a sessionID if (!req.sessionID) { debug('no SID sent, generating session'); - generate(); - next(); + generate(function(){ + next(); + }); return; } @@ -405,11 +406,15 @@ function session(options){ return; } - generate(); + generate(function(){ + next(); + }); // no session } else if (!sess) { debug('no session found'); - generate(); + generate(function(){ + next(); + }); // populate req.session } else { debug('session found'); @@ -422,9 +427,9 @@ function session(options){ } wrapmethods(req.session); - } - next(); + next(); + } }); }; }; From 386bf552bb75471dbd0fb650fb2b6608f9b35caa Mon Sep 17 00:00:00 2001 From: Maxim Savin Date: Thu, 12 Feb 2015 01:20:05 +0600 Subject: [PATCH 03/12] Native uid-safe session id generator with a callback --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index f61a751e..0728b002 100644 --- a/index.js +++ b/index.js @@ -441,8 +441,8 @@ function session(options){ * @private */ -function generateSessionId(sess) { - return uid(24); +function generateSessionId(sess, callback) { + return uid(24, callback); } /** From beee73244b0bbc69a7ebec1e7c78dcc7d8b0eed1 Mon Sep 17 00:00:00 2001 From: Maxim Savin Date: Thu, 12 Feb 2015 02:25:41 +0600 Subject: [PATCH 04/12] Added generate session id function parse for backward compatibility --- index.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 0728b002..57fe6d7c 100644 --- a/index.js +++ b/index.js @@ -124,7 +124,15 @@ function session(options){ // generates the new session store.generate = function(req, callback) { - generateId(req, function(err, sessionID) { + //backward compatibility + var args = /function.*?\(([^\)]*)\)/.exec(generateId.toString())[1].split(','); + if (args.length < 2) { + var _generateId = generateId; + generateId = function(req, cb) { + cb(null, _generateId(req)); + }; + } + generateId.call(store, req, function(err, sessionID) { if (err) throw err; req.sessionID = sessionID; req.session = new Session(req); From cb04bc642f2f0104e83ed06137ccb653cfbe2dd3 Mon Sep 17 00:00:00 2001 From: Maxim Savin Date: Thu, 12 Feb 2015 02:44:07 +0600 Subject: [PATCH 05/12] Added regeneration async logic --- session/store.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/session/store.js b/session/store.js index 54294cbd..5b6dadf8 100644 --- a/session/store.js +++ b/session/store.js @@ -39,8 +39,9 @@ Store.prototype.__proto__ = EventEmitter.prototype; Store.prototype.regenerate = function(req, fn){ var self = this; this.destroy(req.sessionID, function(err){ - self.generate(req); - fn(err); + self.generate(req, function(req) { + fn(err); + }); }); }; From 171d8b6635d9ba27b0aab64355805bdf41344a45 Mon Sep 17 00:00:00 2001 From: Douglas Christopher Wilson Date: Fri, 29 Jan 2016 13:26:00 -0500 Subject: [PATCH 06/12] 1.13.1 --- HISTORY.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 727834eb..ffb63d71 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,5 @@ -unreleased -========== +1.13.1 / 2016-01-29 +=================== * deps: parseurl@~1.3.1 - perf: enable strict mode diff --git a/package.json b/package.json index 8b945f2e..b9e15281 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-session", - "version": "1.13.0", + "version": "1.13.1", "description": "Simple session middleware for Express", "author": "TJ Holowaychuk (http://tjholowaychuk.com)", "contributors": [ From 59b1f56097cac572ba587db89aa12e0794611923 Mon Sep 17 00:00:00 2001 From: Cian Clarke Date: Wed, 2 Mar 2016 08:54:13 -0500 Subject: [PATCH 07/12] docs: fix session store list for CommonMark spec fixes #248 closes #280 --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index f81381f2..f2a6df6b 100644 --- a/README.md +++ b/README.md @@ -412,90 +412,111 @@ The following modules implement a session store that is compatible with this module. Please make a PR to add additional modules :) [![★][cassandra-store-image] cassandra-store][cassandra-store-url] An Apache Cassandra-based session store. + [cassandra-store-url]: https://www.npmjs.com/package/cassandra-store [cassandra-store-image]: https://img.shields.io/github/stars/webcc/cassandra-store.svg?label=%E2%98%85 [![★][cluster-store-image] cluster-store][cluster-store-url] A wrapper for using in-process / embedded stores - such as SQLite (via knex), leveldb, files, or memory - with node cluster (desirable for Raspberry Pi 2 and other multi-core embedded devices). + [cluster-store-url]: https://www.npmjs.com/package/cluster-store [cluster-store-image]: https://img.shields.io/github/stars/coolaj86/cluster-store.svg?label=%E2%98%85 [![★][connect-couchbase-image] connect-couchbase][connect-couchbase-url] A [couchbase](http://www.couchbase.com/)-based session store. + [connect-couchbase-url]: https://www.npmjs.com/package/connect-couchbase [connect-couchbase-image]: https://img.shields.io/github/stars/christophermina/connect-couchbase.svg?label=%E2%98%85 [![★][connect-dynamodb-image] connect-dynamodb][connect-dynamodb-url] A DynamoDB-based session store. + [connect-dynamodb-url]: https://github.com/ca98am79/connect-dynamodb [connect-dynamodb-image]: https://img.shields.io/github/stars/ca98am79/connect-dynamodb.svg?label=%E2%98%85 [![★][connect-mssql-image] connect-mssql][connect-mssql-url] A SQL Server-based session store. + [connect-mssql-url]: https://www.npmjs.com/package/connect-mssql [connect-mssql-image]: https://img.shields.io/github/stars/patriksimek/connect-mssql.svg?label=%E2%98%85 [![★][connect-monetdb-image] connect-monetdb][connect-monetdb-url] A MonetDB-based session store. + [connect-monetdb-url]: https://www.npmjs.com/package/connect-monetdb [connect-monetdb-image]: https://img.shields.io/github/stars/MonetDB/npm-connect-monetdb.svg?label=%E2%98%85 [![★][connect-mongo-image] connect-mongo][connect-mongo-url] A MongoDB-based session store. + [connect-mongo-url]: https://www.npmjs.com/package/connect-mongo [connect-mongo-image]: https://img.shields.io/github/stars/kcbanner/connect-mongo.svg?label=%E2%98%85 [![★][connect-mongodb-session-image] connect-mongodb-session][connect-mongodb-session-url] Lightweight MongoDB-based session store built and maintained by MongoDB. + [connect-mongodb-session-url]: https://www.npmjs.com/package/connect-mongodb-session [connect-mongodb-session-image]: https://img.shields.io/github/stars/mongodb-js/connect-mongodb-session.svg?label=%E2%98%85 [![★][connect-pg-simple-image] connect-pg-simple][connect-pg-simple-url] A PostgreSQL-based session store. + [connect-pg-simple-url]: https://www.npmjs.com/package/connect-pg-simple [connect-pg-simple-image]: https://img.shields.io/github/stars/voxpelli/node-connect-pg-simple.svg?label=%E2%98%85 [![★][connect-redis-image] connect-redis][connect-redis-url] A Redis-based session store. + [connect-redis-url]: https://www.npmjs.com/package/connect-redis [connect-redis-image]: https://img.shields.io/github/stars/tj/connect-redis.svg?label=%E2%98%85 [![★][connect-memcached-image] connect-memcached][connect-memcached-url] A memcached-based session store. + [connect-memcached-url]: https://www.npmjs.com/package/connect-memcached [connect-memcached-image]: https://img.shields.io/github/stars/balor/connect-memcached.svg?label=%E2%98%85 [![★][connect-session-knex-image] connect-session-knex][connect-session-knex-url] A session store using [Knex.js](http://knexjs.org/), which is a SQL query builder for PostgreSQL, MySQL, MariaDB, SQLite3, and Oracle. + [connect-session-knex-url]: https://www.npmjs.com/package/connect-session-knex [connect-session-knex-image]: https://img.shields.io/github/stars/llambda/connect-session-knex.svg?label=%E2%98%85 [![★][connect-session-sequelize-image] connect-session-sequelize][connect-session-sequelize-url] A session store using [Sequelize.js](http://sequelizejs.com/), which is a Node.js / io.js ORM for PostgreSQL, MySQL, SQLite and MSSQL. + [connect-session-sequelize-url]: https://www.npmjs.com/package/connect-session-sequelize [connect-session-sequelize-image]: https://img.shields.io/github/stars/mweibel/connect-session-sequelize.svg?label=%E2%98%85 [![★][connect-sqlite3-image] connect-sqlite3][connect-sqlite3-url] A [SQLite3](https://github.com/mapbox/node-sqlite3) session store modeled after the TJ's `connect-redis` store. + [connect-sqlite3-url]: https://www.npmjs.com/package/connect-sqlite3 [connect-sqlite3-image]: https://img.shields.io/github/stars/rawberg/connect-sqlite3.svg?label=%E2%98%85 [![★][express-nedb-session-image] express-nedb-session][express-nedb-session-url] A NeDB-based session store. + [express-nedb-session-url]: https://www.npmjs.com/package/express-nedb-session [express-nedb-session-image]: https://img.shields.io/github/stars/louischatriot/express-nedb-session.svg?label=%E2%98%85 [![★][level-session-store-image] level-session-store][level-session-store-url] A LevelDB-based session store. + [level-session-store-url]: https://www.npmjs.com/package/level-session-store [level-session-store-image]: https://img.shields.io/github/stars/scriptollc/level-session-store.svg?label=%E2%98%85 [![★][mssql-session-store-image] mssql-session-store][mssql-session-store-url] A SQL Server-based session store. + [mssql-session-store-url]: https://www.npmjs.com/package/mssql-session-store [mssql-session-store-image]: https://img.shields.io/github/stars/jwathen/mssql-session-store.svg?label=%E2%98%85 [![★][nedb-session-store-image] nedb-session-store][nedb-session-store-url] An alternate NeDB-based (either in-memory or file-persisted) session store. + [nedb-session-store-url]: https://www.npmjs.com/package/nedb-session-store [nedb-session-store-image]: https://img.shields.io/github/stars/JamesMGreene/nedb-session-store.svg?label=%E2%98%85 [![★][sequelstore-connect-image] sequelstore-connect][sequelstore-connect-url] A session store using [Sequelize.js](http://sequelizejs.com/). + [sequelstore-connect-url]: https://www.npmjs.com/package/sequelstore-connect [sequelstore-connect-image]: https://img.shields.io/github/stars/MattMcFarland/sequelstore-connect.svg?label=%E2%98%85 [![★][session-file-store-image] session-file-store][session-file-store-url] A file system-based session store. + [session-file-store-url]: https://www.npmjs.com/package/session-file-store [session-file-store-image]: https://img.shields.io/github/stars/valery-barysok/session-file-store.svg?label=%E2%98%85 [![★][session-rethinkdb-image] session-rethinkdb][session-rethinkdb-url] A [RethinkDB](http://rethinkdb.com/)-based session store. + [session-rethinkdb-url]: https://www.npmjs.com/package/session-rethinkdb [session-rethinkdb-image]: https://img.shields.io/github/stars/llambda/session-rethinkdb.svg?label=%E2%98%85 From db2a442266cd27c9ce5c32287000f201b567e57f Mon Sep 17 00:00:00 2001 From: Mike Goodwin Date: Sun, 6 Mar 2016 23:00:31 +0000 Subject: [PATCH 08/12] docs: add connect-azuretables to list of session stores closes #285 --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index f2a6df6b..56e47732 100644 --- a/README.md +++ b/README.md @@ -423,6 +423,11 @@ and other multi-core embedded devices). [cluster-store-url]: https://www.npmjs.com/package/cluster-store [cluster-store-image]: https://img.shields.io/github/stars/coolaj86/cluster-store.svg?label=%E2%98%85 +[![★][connect-azuretables-image] connect-azuretables][connect-azuretables-url] An [Azure Table Storage](https://azure.microsoft.com/en-gb/services/storage/tables/)-based session store. + +[connect-azuretables-url]: https://www.npmjs.com/package/connect-azuretables +[connect-azuretables-image]: https://img.shields.io/github/stars/mike-goodwin/connect-azuretables.svg?label=%E2%98%85 + [![★][connect-couchbase-image] connect-couchbase][connect-couchbase-url] A [couchbase](http://www.couchbase.com/)-based session store. [connect-couchbase-url]: https://www.npmjs.com/package/connect-couchbase From 30a3f498203e0cd3b8815713f29e1f8f10f8c34a Mon Sep 17 00:00:00 2001 From: Gabriel Foust Date: Wed, 2 Mar 2016 14:54:08 -0600 Subject: [PATCH 09/12] Methods are no longer enumerable on req.session object closes #282 --- HISTORY.md | 5 +++++ session/session.js | 39 ++++++++++++++++++++++++++++----------- test/session.js | 18 ++++++++++++++++++ 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index ffb63d71..4145b4fd 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Methods are no longer enumerable on `req.session` object + 1.13.1 / 2016-01-29 =================== diff --git a/session/session.js b/session/session.js index c3c0f15a..2eacde60 100644 --- a/session/session.js +++ b/session/session.js @@ -44,9 +44,9 @@ function Session(req, data) { * @api public */ -Session.prototype.touch = function(){ +defineMethod(Session.prototype, 'touch', function touch() { return this.resetMaxAge(); -}; +}); /** * Reset `.maxAge` to `.originalMaxAge`. @@ -55,10 +55,10 @@ Session.prototype.touch = function(){ * @api public */ -Session.prototype.resetMaxAge = function(){ +defineMethod(Session.prototype, 'resetMaxAge', function resetMaxAge() { this.cookie.maxAge = this.cookie.originalMaxAge; return this; -}; +}); /** * Save the session data with optional callback `fn(err)`. @@ -68,10 +68,10 @@ Session.prototype.resetMaxAge = function(){ * @api public */ -Session.prototype.save = function(fn){ +defineMethod(Session.prototype, 'save', function save(fn) { this.req.sessionStore.set(this.id, this, fn || function(){}); return this; -}; +}); /** * Re-loads the session data _without_ altering @@ -85,7 +85,7 @@ Session.prototype.save = function(fn){ * @api public */ -Session.prototype.reload = function(fn){ +defineMethod(Session.prototype, 'reload', function reload(fn) { var req = this.req , store = this.req.sessionStore; store.get(this.id, function(err, sess){ @@ -95,7 +95,7 @@ Session.prototype.reload = function(fn){ fn(); }); return this; -}; +}); /** * Destroy `this` session. @@ -105,11 +105,11 @@ Session.prototype.reload = function(fn){ * @api public */ -Session.prototype.destroy = function(fn){ +defineMethod(Session.prototype, 'destroy', function destroy(fn) { delete this.req.session; this.req.sessionStore.destroy(this.id, fn); return this; -}; +}); /** * Regenerate this request's session. @@ -119,7 +119,24 @@ Session.prototype.destroy = function(fn){ * @api public */ -Session.prototype.regenerate = function(fn){ +defineMethod(Session.prototype, 'regenerate', function regenerate(fn) { this.req.sessionStore.regenerate(this.req, fn); return this; +}); + +/** + * Helper function for creating a method on a prototype. + * + * @param {Object} obj + * @param {String} name + * @param {Function} fn + * @private + */ +function defineMethod(obj, name, fn) { + Object.defineProperty(obj, name, { + configurable: true, + enumerable: false, + value: fn, + writable: true + }); }; diff --git a/test/session.js b/test/session.js index 682cb8d0..35f20de2 100644 --- a/test/session.js +++ b/test/session.js @@ -1420,6 +1420,24 @@ describe('session()', function(){ }); }) + it('should not have enumerable methods', function (done) { + var app = express() + .use(session({ secret: 'keyboard cat', cookie: { maxAge: min }})) + .use(function(req, res, next) { + req.session.foo = 'foo'; + req.session.bar = 'bar'; + var keys = []; + for (var key in req.session) { + keys.push(key); + } + res.end(keys.sort().join(',')); + }); + + request(app) + .get('/') + .expect(200, 'bar,cookie,foo', done); + }); + describe('.destroy()', function(){ it('should destroy the previous session', function(done){ var app = express() From 005078aaada837d2a57e7324986ead60bf326e84 Mon Sep 17 00:00:00 2001 From: Charles Hill Date: Tue, 2 Feb 2016 16:11:51 +0100 Subject: [PATCH 10/12] docs: add express-mysql-session to list of session stores closes #268 --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 56e47732..ca9da9d1 100644 --- a/README.md +++ b/README.md @@ -485,6 +485,12 @@ and other multi-core embedded devices). [connect-session-sequelize-url]: https://www.npmjs.com/package/connect-session-sequelize [connect-session-sequelize-image]: https://img.shields.io/github/stars/mweibel/connect-session-sequelize.svg?label=%E2%98%85 +[![★][express-mysql-session-image] express-mysql-session][express-mysql-session-url] A session store using native +[MySQL](https://www.mysql.com/) via the [node-mysql](https://github.com/felixge/node-mysql) module. + +[express-mysql-session-url]: https://www.npmjs.com/package/express-mysql-session +[express-mysql-session-image]: https://img.shields.io/github/stars/chill117/express-mysql-session.svg?label=%E2%98%85 + [![★][connect-sqlite3-image] connect-sqlite3][connect-sqlite3-url] A [SQLite3](https://github.com/mapbox/node-sqlite3) session store modeled after the TJ's `connect-redis` store. [connect-sqlite3-url]: https://www.npmjs.com/package/connect-sqlite3 From c596f5a602f25acb0e92973495fb80fdcc1b8ae3 Mon Sep 17 00:00:00 2001 From: Maxim Savin Date: Tue, 3 May 2016 10:07:55 +0600 Subject: [PATCH 11/12] cookieOptions fix --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index ef792d24..66de61a7 100644 --- a/index.js +++ b/index.js @@ -151,7 +151,7 @@ function session(options){ if (err) throw err; req.sessionID = sessionID; req.session = new Session(req); - req.session.cookie = new Cookie(cookie); + req.session.cookie = new Cookie(cookieOptions); if (cookieOptions.secure === 'auto') { req.session.cookie.secure = issecure(req, trustProxy); From 644115ec69abb215eec1ec24d72f69eb5534a82c Mon Sep 17 00:00:00 2001 From: derMart Date: Fri, 30 Sep 2016 15:41:21 +0200 Subject: [PATCH 12/12] tests: add custom async genid function test --- test/session.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/session.js b/test/session.js index 9eb3d38e..b7f426e0 100644 --- a/test/session.js +++ b/test/session.js @@ -786,6 +786,15 @@ describe('session()', function(){ .expect(200, done) }); + it('should allow custom async function', function(done){ + function genid(req, cb) { cb(null,'apple'); } + + request(createServer({ genid: genid })) + .get('/') + .expect(shouldSetCookieToValue('connect.sid', 's%3Aapple.D8Y%2BpkTAmeR0PobOhY4G97PRW%2Bj7bUnP%2F5m6%2FOn1MCU')) + .expect(200, done) + }); + it('should encode unsafe chars', function(done){ function genid() { return '%' } @@ -803,6 +812,18 @@ describe('session()', function(){ .expect(shouldSetCookieToValue('connect.sid', 's%3A%2Ffoo.paEKBtAHbV5s1IB8B2zPnzAgYmmnRPIqObW4VRYj%2FMQ')) .expect(200, done) }); + + it('should provide store to custom async function', function(done){ + var store = new session.MemoryStore() + + function genid(req, cb) { assert(this===store); cb(null,'apple'); } + + request(createServer({ genid: genid, store:store })) + .get('/') + .expect(shouldSetCookieToValue('connect.sid', 's%3Aapple.D8Y%2BpkTAmeR0PobOhY4G97PRW%2Bj7bUnP%2F5m6%2FOn1MCU')) + .expect(200, done) + }); + }); describe('key option', function(){