diff --git a/History.md b/History.md new file mode 100644 index 0000000..0073b2f --- /dev/null +++ b/History.md @@ -0,0 +1,100 @@ + +1.2.0 / 2011-11-17 +================== + + * Added an option 'client' to reuse an existing redis Client [Thomas Fritz] + +1.1.0 / 2011-10-05 +================== + + * Added `prefix` option + * Removed `clear()` and `length()` methods + +1.0.7 / 2011-08-04 +================== + + * Fixed: re-select db on connection (reconnection logic issue with node_redis) + +1.0.6 / 2011-06-21 +================== + + * Added `socket` option so that we can connect to a sockets as well [mekwall] + +1.0.5 / 2011-06-02 +================== + + * Implemented `require("connect-redis")(connect)` for npm 1.x. Closes #23 + +1.0.4 / 2011-05-01 +================== + + * Changed; issue SELECT immediately since it is queued + +1.0.3 / 2011-04-17 +================== + + * Fixed auth support again [garrensmith] + +1.0.2 / 2011-04-15 +================== + + * Fixed auth support [garrensmith] + +1.0.1 / 2011-04-14 +================== + + * Added authentication support [garrensmith] + +1.0.0 / 2011-02-25 +================== + + * Added connect 1.0 support. + This release will _not_ work with older versions of connect. + +0.2.3 / 2011-02-01 +================== + + * Refactoring + +0.2.2 / 2011-01-02 +================== + + * Added `db` option [Clément] + +0.2.1 / 2010-12-20 +================== + + * Redis is now an npm dep + +0.2.0 / 2010-11-01 +================== + + * Use __SETEX__ instead of __SET__ / __EXPIRE__ combo + this should be reasonably faster. + +0.1.3 / 2010-10-25 +================== + + * Updated redis + +0.1.2 / 2010-09-22 +================== + + * Updated redis + +0.1.1 / 2010-09-20 +================== + + * Fixed expires, `maxAge` in seconds + * Updated redis + +0.1.0 / 2010-09-17 +================== + + * Now using node_redis as the client, much faster + +0.0.2 / 2010-07-27 +================== + + * Moved redis to lib/redis + * Added lib/connect-redis.js diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..621191e --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ + +index.html: lib/connect-redis.js + dox \ + --title "Connect Redis" \ + --desc "Redis session store for connect backed by [node_redis](http://github.com/mranney/node_redis)." \ + --ribbon "http://github.com/visionmedia/connect-redis" \ + $< > $@ diff --git a/README.md b/README.md deleted file mode 100644 index 4ec1ece..0000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -connect-mredis -============== - -conenct-redis for multi_redis \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..b244934 --- /dev/null +++ b/Readme.md @@ -0,0 +1,37 @@ + +# Connect Redis + +connect-redis is a Redis session store backed by [node_redis](http://github.com/mranney/node_redis), and is insanely fast :). Requires redis >= `1.3.10` for the _SETEX_ command. + + connect-redis `>= 1.0.0` support only connect `>= 1.0.0`. + +## Installation + + $ npm install connect-redis + +## Options + + - `client` An existing redis client object you normally get from `redis.createClient()` + - `host` Redis server hostname + - `port` Redis server portno + - `db` Database index to use + - `pass` Password for Redis authentication + - `prefix` Key prefix defaulting to "sess:" + - ... Remaining options passed to the redis `createClient()` method. + +## Usage + + Due to npm 1.x changes, we now need to pass connect to the function `connect-redis` exports in order to extend `connect.session.Store`: + + var connect = require('connect') + , RedisStore = require('connect-redis')(connect); + + connect.createServer( + connect.cookieParser(), + // 5 minutes + connect.session({ store: new RedisStore, secret: 'keyboard cat' }) + ); + + This means express users may do the following, since `express.session.Store` points to the `connect.session.Store` function: + + var RedisStore = require('connect-redis')(express); diff --git a/bm.js b/bm.js new file mode 100644 index 0000000..54741f1 --- /dev/null +++ b/bm.js @@ -0,0 +1,21 @@ + +var redis = require('./lib/redis') + , times = 100000; + +var n = times + , pending = n + , client = redis.createClient() + , start = new Date; + +client.on('connect', function(){ + while (n--) { + client.set('foo:' + n, 'bar', function(err){ + if (err) throw err; + --pending || report(); + }); + } +}); + +function report() { + console.log('\x1b[33m%d\x1b[0m sets in \x1b[32m%d\x1b[0m milliseconds', times, new Date - start); +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..8b54ea8 --- /dev/null +++ b/index.html @@ -0,0 +1,232 @@ +Fork me on GitHub + + Connect Redis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Connect Redis

Redis session store for connect backed by node_redis.

connect-redis

lib/connect-redis.js
+

Module dependencies. +

+
+
var Store = require('connect').session.Store,
+    redis = require('./redis');
+
+

Initialize RedisStore with the given options.

+ +

+ +
  • param: Object options

  • api: public

+
+
var RedisStore = module.exports = function RedisStore(options) {
+    options = options || {};
+    Store.call(this, options);
+    this.client = new redis.createClient(options.port, options.host, options);
+};
+
+

Inherit from Store. +

+
+
RedisStore.prototype.__proto__ = Store.prototype;
+
+

Attempt to fetch session by the given hash.

+ +

+ +
  • param: String hash

  • param: Function fn

  • api: public

+
+
RedisStore.prototype.get = function(hash, fn){
+    this.client.get(hash, function(err, data){
+        try {
+            fn(null, data
+                ? JSON.parse(data.toString())
+                : data);
+        } catch (err) {
+            fn(err);
+        } 
+    });
+};
+
+

Commit the given sess object associated with the given hash.

+ +

+ +
  • param: String hash

  • param: Session sess

  • param: Function fn

  • api: public

+
+
RedisStore.prototype.set = function(hash, sess, fn){
+    var self = this;
+    try {
+        this.client.set(hash, JSON.stringify(sess), function(){
+            self.client.expire(hash, self.maxAge / 1000 | 0);
+            fn &amp;&amp; fn.apply(this, arguments);
+        });
+    } catch (err) {
+        fn &amp;&amp; fn(err);
+    } 
+};
+
+

Destroy the session associated with the given hash.

+ +

+ +
  • param: String hash

  • api: public

+
+
RedisStore.prototype.destroy = function(hash, fn){
+    this.client.del(hash, fn);
+};
+
+

Fetch number of sessions.

+ +

+ +
  • param: Function fn

  • api: public

+
+
RedisStore.prototype.length = function(fn){
+    this.client.dbsize(fn);
+};
+
+

Clear all sessions.

+ +

+ +
  • param: Function fn

  • api: public

+
+
RedisStore.prototype.clear = function(fn){
+    this.client.flushdb(fn);
+};
+
\ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..64efef5 --- /dev/null +++ b/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/connect-mredis'); diff --git a/lib/connect-mredis.js b/lib/connect-mredis.js new file mode 100644 index 0000000..fb4a18d --- /dev/null +++ b/lib/connect-mredis.js @@ -0,0 +1,131 @@ +/*! + * Connect - Redis + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var redis = require('mredis'); + +/** + * One day in seconds. + */ + +var oneDay = 86400; + +/** + * Return the `RedisStore` extending `connect`'s session Store. + * + * @param {object} connect + * @return {Function} + * @api public + */ + +module.exports = function(connect){ + + /** + * Connect's Store. + */ + + var Store = connect.session.Store; + + /** + * Initialize RedisStore with the given `options`. + * + * @param {Object} options + * @api public + */ + + function RedisStore(options) { + options = options || {}; + Store.call(this, options); + this.prefix = null == options.prefix + ? 'sess:' + : options.prefix; + //this.client = options.client || new redis.createClient(options.port || options.socket, options.host, options); + this.client = options.client || new redis.createClient(options); + if (options.pass) { + this.client.auth(options.pass, function(err){ + if (err) throw err; + }); + } + + if (options.db) { + var self = this; + self.client.select(options.db); + self.client.on("connect", function(client) { + client.send_anyways = true; + client.select(options.db); + client.send_anyways = false; + }); + } + }; + + /** + * Inherit from `Store`. + */ + + RedisStore.prototype.__proto__ = Store.prototype; + + /** + * Attempt to fetch session by the given `sid`. + * + * @param {String} sid + * @param {Function} fn + * @api public + */ + + RedisStore.prototype.get = function(sid, fn){ + sid = this.prefix + sid; + this.client.get(sid, function(err, data){ + if (!data) return fn(); + try { + var dataJson = JSON.parse(data.toString()); + } catch (err) { + return fn(err); + } + fn(null, dataJson); + }); + }; + /** + * Commit the given `sess` object associated with the given `sid`. + * + * @param {String} sid + * @param {Session} sess + * @param {Function} fn + * @api public + */ + + RedisStore.prototype.set = function(sid, sess, fn){ + sid = this.prefix + sid; + try { + var maxAge = sess.cookie.maxAge + , ttl = 'number' == typeof maxAge + ? maxAge / 1000 | 0 + : oneDay + , sess = JSON.stringify(sess); + }catch(err){ + return fn&&fn(err); + } + this.client.setex(sid, ttl, sess, function(){ + fn && fn.apply(this, arguments); + }); + }; + + /** + * Destroy the session associated with the given `sid`. + * + * @param {String} sid + * @api public + */ + + RedisStore.prototype.destroy = function(sid, fn){ + sid = this.prefix + sid; + this.client.del(sid, fn); + }; + + return RedisStore; +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..80d0804 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "connect-mredis", + "description": "Redis session store for Connect", + "version": "1.0.6", + "author": { + "name": "dead_horse", + "email": "heyiyu.deadhorse@gmail.com" + }, + "main": "./index.js", + "dependencies": { + "mredis": ">= 1.1.0" + }, + "devDependencies": { + "connect": ">=1.4.x" + }, + "engines": { + "node": ">= 0.1.98" + }, + "_id": "connect-mredis@1.0.6", + "dist": { + "shasum": "45352c25c299fe5e42bb62b5bab3d8a08bd8edbd" + }, + "_from": "connect-mredis@=1.0.6" +} diff --git a/test.js b/test.js new file mode 100644 index 0000000..1b96973 --- /dev/null +++ b/test.js @@ -0,0 +1,40 @@ + +/** + * Module dependencies. + */ + +var assert = require('assert') + , connect = require('connect') + , RedisStore = require('./')(connect); + +var store = new RedisStore; +store.client.on('connect', function(){ + // #set() + store.set('123', { cookie: { maxAge: 2000 }, name: 'tj' }, function(err, ok){ + assert.ok(!err, '#set() got an error'); + assert.ok(ok, '#set() is not ok'); + // #get() + store.get('123', function(err, data){ + assert.ok(!err, '#get() got an error'); + //throw new Error('uncaughtException'); + assert.deepEqual({ cookie: { maxAge: 2000 }, name: 'tj' }, data); + store.destroy('123', function(){ + store.get('123',function(err, data){ + assert.ok(!err, '#get() null got an error'); + assert.ok(!data, '#get() null got data error'); + throw new Error('uncaughtException'); + store.client.end(); + }) + }); + }) + }); +}); + +process.on('uncaughtException', function (err) { + if(err.message==='uncaughtException'){ + console.log('done'); + }else{ + console.log(err.message); + } + store.client.end(); +});