Skip to content

Commit c206548

Browse files
committed
Added a passReqToStore option, used instead of function arguments length to determine if the request should be passed to the store or not.
To that end, also exposed the session options in the request, and moved the load method so that it is overridden in the request, instead of defined at the store, i.e. all stores get it "for free". Tweaked the "should set maxAge" cookie test to store the time at the start, making it non flaky. Added more tests.
1 parent 5b91bec commit c206548

File tree

6 files changed

+195
-38
lines changed

6 files changed

+195
-38
lines changed

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,12 @@ all the elements will be considered when verifying the signature in requests.
278278

279279
The session store instance, defaults to a new `MemoryStore` instance.
280280

281+
##### passReqToStore
282+
283+
Pass the request object to required and optional store methods.
284+
285+
By default, the request object is not passed to store methods.
286+
281287
##### unset
282288

283289
Control the result of unsetting `req.session` (through `delete`, setting to `null`,
@@ -348,6 +354,11 @@ req.session.reload(function(err) {
348354
})
349355
```
350356

357+
#### Session.load(sid, callback)
358+
359+
Loads the the session data for the given `sid` from the store and re-populates the
360+
`req.session` object. Once complete, the `callback` will be invoked.
361+
351362
#### Session.save(callback)
352363

353364
Save the session back to the store, replacing the contents on the store with the
@@ -420,6 +431,14 @@ To get the ID of the loaded session, access the request property
420431
`req.sessionID`. This is simply a read-only value set when a session
421432
is loaded/created.
422433

434+
### req.sessionOptions
435+
436+
To get the options of the session, access the request property
437+
`req.sessionOptions`.
438+
439+
**NOTE** For performance reasons, this is the original global options object.
440+
Changes to it would take effect for all requests in an undefined manner.
441+
423442
## Session Store Implementation
424443

425444
Every session store _must_ be an `EventEmitter` and implement specific
@@ -449,6 +468,10 @@ This required method is used to destroy/delete a session from the store given
449468
a session ID (`sid`). The `callback` should be called as `callback(error)` once
450469
the session is destroyed.
451470

471+
The request is passed into the `req` argument only if the session's options
472+
have `passReqToStore` set to `true`. A store wishing to operate in either mode
473+
should check whether the second argument is a function or an object.
474+
452475
### store.clear(callback)
453476

454477
**Optional**
@@ -474,6 +497,10 @@ The `session` argument should be a session if found, otherwise `null` or
474497
`undefined` if the session was not found (and there was no error). A special
475498
case is made when `error.code === 'ENOENT'` to act like `callback(null, null)`.
476499

500+
The request is passed into the `req` argument only if the session's options
501+
have `passReqToStore` set to `true`. A store wishing to operate in either mode
502+
should check whether the second argument is a function or an object.
503+
477504
### store.set(sid, session, [req], callback)
478505

479506
**Required**
@@ -482,6 +509,10 @@ This required method is used to upsert a session into the store given a
482509
session ID (`sid`) and session (`session`) object. The callback should be
483510
called as `callback(error)` once the session has been set in the store.
484511

512+
The request is passed into the `req` argument only if the session's options
513+
have `passReqToStore` set to `true`. A store wishing to operate in either mode
514+
should check whether the third argument is a function or an object.
515+
485516
### store.touch(sid, session, [req], callback)
486517

487518
**Recommended**
@@ -494,6 +525,10 @@ This is primarily used when the store will automatically delete idle sessions
494525
and this method is used to signal to the store the given session is active,
495526
potentially resetting the idle timer.
496527

528+
The request is passed into the `req` argument only if the session's options
529+
have `passReqToStore` set to `true`. A store wishing to operate in either mode
530+
should check whether the third argument is a function or an object.
531+
497532
## Compatible Session Stores
498533

499534
The following modules implement a session store that is compatible with this

index.js

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ var defer = typeof setImmediate === 'function'
7979
* @param {Boolean} [options.saveUninitialized] Save uninitialized sessions to the store
8080
* @param {String|Array} [options.secret] Secret for signing session ID
8181
* @param {Object} [options.store=MemoryStore] Session store
82+
* @param {Boolean} [options.passReqToStore] Pass the request to store methods
8283
* @param {String} [options.unset]
8384
* @return {Function} middleware
8485
* @public
@@ -99,6 +100,8 @@ function session(options) {
99100
// get the session store
100101
var store = opts.store || new MemoryStore()
101102

103+
var passReqToStore = opts.passReqToStore;
104+
102105
// get the trust proxy setting
103106
var trustProxy = opts.proxy
104107

@@ -166,9 +169,6 @@ function session(options) {
166169
};
167170

168171
var storeImplementsTouch = typeof store.touch === 'function';
169-
var storeImplementsRequestAtGet = store.get.length >= 3;
170-
var storeImplementsRequestAtDestroy = store.destroy.length >= 3;
171-
var storeImplementsRequestAtTouch = storeImplementsTouch && store.touch.length >= 4;
172172

173173
// register event listeners for the store to track readiness
174174
var storeReady = true
@@ -204,6 +204,27 @@ function session(options) {
204204
return;
205205
}
206206

207+
/**
208+
* Load a `Session` instance via the given `sid`
209+
* and invoke the callback `fn(err, sess)`.
210+
*
211+
* @param {String} sid
212+
* @param {Function} fn
213+
* @api public
214+
*/
215+
store.load = function(sid, fn){
216+
var getCallback = function(err, sess){
217+
if (err) return fn(err);
218+
if (!sess) return fn();
219+
fn(null, store.createSession(req, sess))
220+
};
221+
if (passReqToStore) {
222+
store.get(sid, req, getCallback);
223+
} else {
224+
store.get(sid, getCallback);
225+
}
226+
};
227+
207228
// backwards compatibility for signed cookies
208229
// req.secret is passed from the cookie parser middleware
209230
var secrets = secret || [req.secret];
@@ -215,6 +236,8 @@ function session(options) {
215236

216237
// expose store
217238
req.sessionStore = store;
239+
// expose session options
240+
req.sessionOptions = opts;
218241

219242
// get the session ID from the cookie
220243
var cookieId = req.sessionID = getcookie(req, name, secrets);
@@ -315,7 +338,7 @@ function session(options) {
315338
writeend();
316339
}
317340

318-
if (storeImplementsRequestAtDestroy) {
341+
if (passReqToStore) {
319342
store.destroy(req.sessionID, req, ondestroy);
320343
} else {
321344
store.destroy(req.sessionID, ondestroy);
@@ -358,7 +381,7 @@ function session(options) {
358381
writeend();
359382
}
360383

361-
if (storeImplementsRequestAtTouch) {
384+
if (passReqToStore) {
362385
store.touch(req.sessionID, req.session, req, ontouch);
363386
} else {
364387
store.touch(req.sessionID, req.session, ontouch);
@@ -509,7 +532,7 @@ function session(options) {
509532

510533
next()
511534
}
512-
if (storeImplementsRequestAtGet) {
535+
if (passReqToStore) {
513536
store.get(req.sessionID, req, getHandler);
514537
} else {
515538
store.get(req.sessionID, getHandler);

session/session.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ defineMethod(Session.prototype, 'resetMaxAge', function resetMaxAge() {
6969
*/
7070

7171
defineMethod(Session.prototype, 'save', function save(fn) {
72-
if (this.req.sessionStore.set.length >= 4) {
72+
if (this.req.sessionOptions.passReqToStore) {
7373
this.req.sessionStore.set(this.id, this, this.req, fn || function(){});
7474
} else {
7575
this.req.sessionStore.set(this.id, this, fn || function(){});
@@ -93,12 +93,17 @@ defineMethod(Session.prototype, 'reload', function reload(fn) {
9393
var req = this.req
9494
var store = this.req.sessionStore
9595

96-
store.get(this.id, function(err, sess){
96+
var getCallback = function(err, sess){
9797
if (err) return fn(err);
9898
if (!sess) return fn(new Error('failed to load session'));
9999
store.createSession(req, sess);
100100
fn();
101-
});
101+
};
102+
if (this.req.sessionOptions.passReqToStore) {
103+
store.get(this.id, req, getCallback);
104+
} else {
105+
store.get(this.id, getCallback);
106+
}
102107
return this;
103108
});
104109

@@ -112,7 +117,11 @@ defineMethod(Session.prototype, 'reload', function reload(fn) {
112117

113118
defineMethod(Session.prototype, 'destroy', function destroy(fn) {
114119
delete this.req.session;
115-
this.req.sessionStore.destroy(this.id, fn);
120+
if (this.req.sessionOptions.passReqToStore) {
121+
this.req.sessionStore.destroy(this.id, this.req, fn);
122+
} else {
123+
this.req.sessionStore.destroy(this.id, fn);
124+
}
116125
return this;
117126
});
118127

session/store.js

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,29 +49,15 @@ util.inherits(Store, EventEmitter)
4949

5050
Store.prototype.regenerate = function(req, fn){
5151
var self = this;
52-
this.destroy(req.sessionID, function(err){
52+
var destroyCallback = function(err){
5353
self.generate(req);
5454
fn(err);
55-
});
56-
};
57-
58-
/**
59-
* Load a `Session` instance via the given `sid`
60-
* and invoke the callback `fn(err, sess)`.
61-
*
62-
* @param {String} sid
63-
* @param {Function} fn
64-
* @api public
65-
*/
66-
67-
Store.prototype.load = function(sid, fn){
68-
var self = this;
69-
this.get(sid, function(err, sess){
70-
if (err) return fn(err);
71-
if (!sess) return fn();
72-
var req = { sessionID: sid, sessionStore: self };
73-
fn(null, self.createSession(req, sess))
74-
});
55+
};
56+
if (req.sessionOptions.passReqToStore) {
57+
this.destroy(req.sessionID, req, destroyCallback);
58+
} else {
59+
this.destroy(req.sessionID, destroyCallback);
60+
}
7561
};
7662

7763
/**

test/cookie.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,12 @@ describe('new Cookie()', function () {
6262
})
6363

6464
it('should set maxAge', function () {
65-
var expires = new Date(Date.now() + 60000)
65+
var now = Date.now()
66+
var expires = new Date(now + 60000)
6667
var cookie = new Cookie({ expires: expires })
6768

68-
assert.ok(expires.getTime() - Date.now() - 1000 <= cookie.maxAge)
69-
assert.ok(expires.getTime() - Date.now() + 1000 >= cookie.maxAge)
69+
assert.ok(expires.getTime() - now - 1000 <= cookie.maxAge)
70+
assert.ok(expires.getTime() - now + 1000 >= cookie.maxAge)
7071
})
7172
})
7273

0 commit comments

Comments
 (0)