Skip to content

Split MemoryStore in a separate module + avoid memory leaks #487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 15 commits into from
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ and writes cookies on `req`/`res`. Using `cookie-parser` may result in issues
if the `secret` is not the same between this module and `cookie-parser`.

**Warning** The default server-side session storage, `MemoryStore`, is _purposely_
not designed for a production environment. It will leak memory under most
conditions, does not scale past a single process, and is meant for debugging and
not designed for a production environment. It does not scale past a single process, and is meant for debugging and
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dougwilson I believe you wanted the README to be 80 character wrap?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap at 80 characters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

developing.

For a list of stores, see [compatible session stores](#compatible-session-stores).
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"crc": "3.4.4",
"debug": "2.6.8",
"depd": "~1.1.0",
"memorystore": "1.5.0",
"on-headers": "~1.0.1",
"parseurl": "~1.3.1",
"uid-safe": "~2.1.4",
Expand Down
165 changes: 2 additions & 163 deletions session/memory.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,171 +15,10 @@

var Store = require('./store')
var util = require('util')

/**
* Shim setImmediate for node.js < 0.10
* @private
*/

/* istanbul ignore next */
var defer = typeof setImmediate === 'function'
? setImmediate
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
var MemoryStore = require('memorystore')

/**
* Module exports.
*/

module.exports = MemoryStore

/**
* A session store in memory.
* @public
*/

function MemoryStore() {
Store.call(this)
this.sessions = Object.create(null)
}

/**
* Inherit from Store.
*/

util.inherits(MemoryStore, Store)

/**
* Get all active sessions.
*
* @param {function} callback
* @public
*/

MemoryStore.prototype.all = function all(callback) {
var sessionIds = Object.keys(this.sessions)
var sessions = Object.create(null)

for (var i = 0; i < sessionIds.length; i++) {
var sessionId = sessionIds[i]
var session = getSession.call(this, sessionId)

if (session) {
sessions[sessionId] = session;
}
}

callback && defer(callback, null, sessions)
}

/**
* Clear all sessions.
*
* @param {function} callback
* @public
*/

MemoryStore.prototype.clear = function clear(callback) {
this.sessions = Object.create(null)
callback && defer(callback)
}

/**
* Destroy the session associated with the given session ID.
*
* @param {string} sessionId
* @public
*/

MemoryStore.prototype.destroy = function destroy(sessionId, callback) {
delete this.sessions[sessionId]
callback && defer(callback)
}

/**
* Fetch session by the given session ID.
*
* @param {string} sessionId
* @param {function} callback
* @public
*/

MemoryStore.prototype.get = function get(sessionId, callback) {
defer(callback, null, getSession.call(this, sessionId))
}

/**
* Commit the given session associated with the given sessionId to the store.
*
* @param {string} sessionId
* @param {object} session
* @param {function} callback
* @public
*/

MemoryStore.prototype.set = function set(sessionId, session, callback) {
this.sessions[sessionId] = JSON.stringify(session)
callback && defer(callback)
}

/**
* Get number of active sessions.
*
* @param {function} callback
* @public
*/

MemoryStore.prototype.length = function length(callback) {
this.all(function (err, sessions) {
if (err) return callback(err)
callback(null, Object.keys(sessions).length)
})
}

/**
* Touch the given session object associated with the given session ID.
*
* @param {string} sessionId
* @param {object} session
* @param {function} callback
* @public
*/

MemoryStore.prototype.touch = function touch(sessionId, session, callback) {
var currentSession = getSession.call(this, sessionId)

if (currentSession) {
// update expiration
currentSession.cookie = session.cookie
this.sessions[sessionId] = JSON.stringify(currentSession)
}

callback && defer(callback)
}

/**
* Get session from the store.
* @private
*/

function getSession(sessionId) {
var sess = this.sessions[sessionId]

if (!sess) {
return
}

// parse
sess = JSON.parse(sess)

var expires = typeof sess.cookie.expires === 'string'
? new Date(sess.cookie.expires)
: sess.cookie.expires

// destroy expired session
if (expires && expires <= Date.now()) {
delete this.sessions[sessionId]
return
}

return sess
}
module.exports = new MemoryStore({Store: Store})
Loading