Skip to content

Commit

Permalink
Merge commit '6bd4f3ba5f6c5ce88c485be778b21a95c0aab84a'
Browse files Browse the repository at this point in the history
# Conflicts:
#	lib/session.js
  • Loading branch information
voxelperfect committed Sep 4, 2016
2 parents 5ff068f + 6bd4f3b commit 80e1022
Show file tree
Hide file tree
Showing 13 changed files with 465 additions and 47 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
sudo: false
language: node_js
node_js:
- "4"
- "5"
- "6"
script: "make test-travis"
after_script: "npm install [email protected] && cat ./coverage/lcov.info | coveralls"
27 changes: 26 additions & 1 deletion History.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@

1.10.1 / 2015-12-15
1.11.3 / 2016-07-24
==================

* fix: always refresh session (#95)

1.11.2 / 2016-07-12
==================

* fix: npm pack should not include test dir (#92)

1.11.1 / 2016-06-23
==================

* fix: make sure ctx.session exists when cookie.path = '/' (#91)

1.11.0 / 2016-06-20
==================

* Add support for overriding session save conditions per request (#89)

1.10.2 / 2016-03-23
==================

* fix: check if session exits

1.10.1 / 2015-12-15
==================

* feat: ctx.session as a setter/getter
Expand Down
48 changes: 23 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Generic session middleware for koa, easy use with custom stores such as [redis](

This middleware will only set a cookie when a session is manually set. Each time the session is modified (and only when the session is modified), it will reset the cookie and session.

You can use the rolling sessions that will reset the cookie and session for every request which touch the session.
You can use the rolling sessions that will reset the cookie and session for every request which touch the session. Save behavior can be overridden per request.

## Usage

Expand Down Expand Up @@ -85,14 +85,13 @@ app.listen(8080);
* Setting `this.session = null;` will destroy this session.
* Altering `this.session.cookie` changes the cookie options of this user. Also you can use the cookie options in session the store. Use for example `cookie.maxage` as the session store's ttl.
* Calling `this.regenerateSession` will destroy any existing session and generate a new, empty one in its place. The new session will have a different ID.
* Setting `this.sessionSave = true` will force saving the session regardless of any other options or conditions.
* Setting `this.sessionSave = false` will prevent saving the session regardless of any other options or conditions.

### Options


* `key`: cookie name defaulting to `koa.sid`
* `prefix`: session prefix for store, defaulting to `koa:sess:`
* `store`: session store instance
* `cookie`: session cookie settings
* `ttl`: ttl is for sessionStore's expiration time. it is different with `cookie.maxage`, default to null(means get ttl from `cookie.maxage`).
* `rolling`: rolling session, always reset the cookie and sessions, defaults to `false`
* `genSid`: default sid was generated by [uid2](https://github.com/coreh/uid2), you can pass a function to replace it
Expand All @@ -103,26 +102,24 @@ app.listen(8080);
* `sessionIdStore`: object with get, set, reset methods for passing session id throw requests.
* `valid`: valid(ctx, session), valid session value before use it
* `beforeSave`: beforeSave(ctx, session), hook before save session

* Store can be any Object that has the methods `set`, `get`, `destroy` like [MemoryStore](https://github.com/koajs/koa-session/blob/master/lib/store.js).
* cookie defaulting to

```js
{
path: '/',
httpOnly: true,
maxage: null,
rewrite: true,
signed: true
}
```

For a full list of cookie options see [expressjs/cookies](https://github.com/expressjs/cookies#cookiesset-name--value---options--).

if you set`cookie.maxage` to `null`, meaning no "expires" parameter is set so the cookie becomes a browser-session cookie. When the user closes the browser the cookie (and session) will be removed.

Notice that `ttl` is different from `cookie.maxage`, `ttl` set the expire time of sessionStore. So if you set `cookie.maxage = null`, and `ttl=ms('1d')`, the session will expired after one day, but the cookie will destroy when the user closes the browser.
And mostly you can just ignore `options.ttl`, `koa-generic-session` will parse `cookie.maxage` as the tll.
* `store`: session store instance. It can be any Object that has the methods `set`, `get`, `destroy` like [MemoryStore](https://github.com/koajs/koa-session/blob/master/lib/store.js).
* `cookie`: session cookie settings, defaulting to
```js
{
path: '/',
httpOnly: true,
maxage: null,
rewrite: true,
signed: true
}
```

For a full list of cookie options see [expressjs/cookies](https://github.com/expressjs/cookies#cookiesset-name--value---options--).

if you set`cookie.maxage` to `null`, meaning no "expires" parameter is set so the cookie becomes a browser-session cookie. When the user closes the browser the cookie (and session) will be removed.

Notice that `ttl` is different from `cookie.maxage`, `ttl` set the expire time of sessionStore. So if you set `cookie.maxage = null`, and `ttl=ms('1d')`, the session will expired after one day, but the cookie will destroy when the user closes the browser.
And mostly you can just ignore `options.ttl`, `koa-generic-session` will parse `cookie.maxage` as the tll.

## Hooks

Expand Down Expand Up @@ -151,12 +148,13 @@ And use these events to report the store's status.
- [koa-generic-session-mongo](https://github.com/freakycue/koa-generic-session-mongo) to store your session data with MongoDB.
- [koa-pg-session](https://github.com/TMiguelT/koa-pg-session) to store your session data with PostgreSQL.
- [koa-generic-session-rethinkdb](https://github.com/KualiCo/koa-generic-session-rethinkdb) to store your session data with ReThinkDB.
- [koa-sqlite3-session](https://github.com/chichou/koa-sqlite3-session) to store your session data with SQLite3.
## Licences
(The MIT License)
Copyright (c) 2013 - 2014 dead-horse and other contributors
Copyright (c) 2013 - 2016 dead-horse and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Expand Down
67 changes: 61 additions & 6 deletions lib/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,20 @@ module.exports = function (options) {

return options.defer ? deferSession : session;

function addCommonAPI() {

this._sessionSave = null;

// more flexible
this.__defineGetter__('sessionSave', function () {
return this._sessionSave;
});

this.__defineSetter__('sessionSave', function (save) {
this._sessionSave = save;
});
}

/**
* generate a new session
*/
Expand All @@ -140,7 +154,11 @@ module.exports = function (options) {
*/
function matchPath(ctx) {
let pathname = parse(ctx).pathname;
if (pathname.indexOf(cookie.path || '/') !== 0) {
let cookiePath = cookie.path || '/';
if (cookiePath === '/') {
return true;
}
if (pathname.indexOf(cookiePath) !== 0) {
debug('cookie path not match');
return false;
}
Expand Down Expand Up @@ -214,6 +232,12 @@ module.exports = function (options) {
* if session is modified, update cookie and store
*/
function *refreshSession (session, originalHash, isNew) {

// reject any session changes, and do not update session expiry
if(this._sessionSave === false) {
return debug('session save disabled');
}

//delete session
if (!session) {
if (!isNew) {
Expand All @@ -224,6 +248,12 @@ module.exports = function (options) {
return debug('a new session and set to null, ignore destroy');
}

// force saving non-empty session
if(this._sessionSave === true) {
debug('session save forced');
return yield saveNow.call(this, this.sessionId, session);
}

let newHash = hash(session);
// if new session and not modified, just ignore
if (!options.allowEmpty && isNew && newHash === EMPTY_SESSION_HASH) {
Expand All @@ -236,15 +266,21 @@ module.exports = function (options) {
}

debug('session modified');

yield saveNow.call(this, this.sessionId, session);

}

function *saveNow(id, session) {
compatMaxage(session.cookie);

// custom before save hook
beforeSave(this, session);

//update session
try {
yield store.set(this.sessionId, session, this);
sessionIdStore.set.call(this, this.sessionId, session);
yield store.set(id, session, this);
sessionIdStore.set.call(this, id, session);
debug('saved');
} catch (err) {
debug('set session error: ', err.message);
Expand All @@ -262,14 +298,16 @@ module.exports = function (options) {
*/
function *session(next) {
this.sessionStore = store;
if (this._session) {
if (this.session || this._session) {
return yield next;
}
let result = yield getSession.call(this);
if (!result) {
return yield next;
}

addCommonAPI.call(this);

this._session = result.session;

// more flexible
Expand Down Expand Up @@ -297,8 +335,23 @@ module.exports = function (options) {
result.isNew = true;
}

yield next;
yield refreshSession.call(this, this.session, result.originalHash, result.isNew);
// make sure `refreshSession` always called
var firstError = null;
try {
yield next;
} catch (err) {
debug('next logic error: %s', err.message);
firstError = err;
}
// can't use finally because `refreshSession` is async
try {
yield refreshSession.call(this, this.session, result.originalHash, result.isNew);
} catch (err) {
debug('refresh session error: %s', err.message);
if (firstError) this.app.emit('error', err, this);
firstError = firstError || err;
}
if (firstError) throw firstError;
}

/**
Expand All @@ -325,6 +378,8 @@ module.exports = function (options) {
return yield next;
}

addCommonAPI.call(this);

this.__defineGetter__('session', function *() {
if (touchSession) {
return this._session;
Expand Down
21 changes: 13 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "koa-generic-session",
"description": "koa generic session store by memory, redis or others",
"repository": "koajs/generic-session",
"version": "1.10.1",
"version": "1.11.3",
"files": [
"lib",
"index.js"
],
"keywords": [
"koa",
"middleware",
Expand All @@ -11,21 +15,22 @@
"author": "dead_horse <[email protected]>",
"dependencies": {
"copy-to": "~2.0.1",
"crc": "~3.3.0",
"crc": "~3.4.0",
"debug": "*",
"parseurl": "~1.3.0",
"uid-safe": "~2.1.0"
"parseurl": "~1.3.1",
"uid-safe": "~2.1.1"
},
"devDependencies": {
"autod": "~2.4.2",
"blanket": "*",
"contributors": "*",
"istanbul-harmony": "*",
"koa": "~1.1.2",
"koa-redis": "~1.0.1",
"mm": "~1.3.5",
"koa": "~1.2.0",
"koa-redis": "~2.1.1",
"mm": "~1.5.0",
"mocha": "2",
"should": "~7.1.1",
"pedding": "^1.0.0",
"should": "~10.0.0",
"supertest": "~0.13.0"
},
"engines": {
Expand Down
54 changes: 54 additions & 0 deletions test/override.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**!
* koa-generic-session - test/override.test.js
* Copyright(c) 2016
* MIT Licensed
*
* Authors:
* Evan King <[email protected]> (http://honoredsoft.com)
*/

'use strict';

/**
* Module dependencies.
*/

var app = require('./support/override');
var request = require('supertest');
var mm = require('mm');
var should = require('should');

describe('test/override.test.js', function () {
var cookie;
before(function (done) {
request(app)
.get('/session/update')
.expect(/1/)
.end(function (err, res) {
cookie = res.headers['set-cookie'].join(';');
done(err);
});
});

function req(path, expectBody, expectCookie, done) {
request(app)
.get('/session/' + path)
.set('cookie', cookie)
.end(function (err, res) {
should(res.text).match(expectBody);
(expectCookie)
? should.exist(res.headers['set-cookie'])
: should.not.exist(res.headers['set-cookie']);
done(err);
});
}

it('should save modified session', req.bind(null, 'update', /2, null/, true));
it('should prevent saving modified session', req.bind(null, 'update/prevent', /3, false/, false));
it('should force saving unmodified session', req.bind(null, 'read/force', /2, true/, true));
it('should prevent deleting session', req.bind(null, 'remove/prevent', /0, false/, false));
it('should not have fresh session', req.bind(null, 'read', /2, null/, false));
it('should delete session on force-save', req.bind(null, 'remove/force', /0, true/, true));
it('should have fresh session', req.bind(null, 'read', /0, null/, true));

});
Loading

0 comments on commit 80e1022

Please sign in to comment.