how can I get sessions to work using redis, express & socket.io?
Asked Answered
P

2

9

So I am trying to get Sessions to work inside my socket.on('connection', ...) I am trying to get this working using recent versions: Socket.io - 0.9.13, Express - 3.1.0 and latest versions of other modules.

Anyway I have tried using both modules 'connect-redis' and 'session.socket.io' and they both have similar problems.

In my code I have 2 redis stores (socketio.RedisStore and require('connect-redis')(express)), now this program all runs fine, but because express and socket.io need to share session data, I was wondering if this setup will use sessions correctly? do the session stores need to be the same object for express/socketio? A bit of a gray area to me, because the 2 RedisStore's will use the same db in the background?

I have tried using either the socket.io redisStore or the connect-redis redisStore in both places, but socket.io doesnt like the connect-redis redisStore and express doesnt like the socketio.redisStore.

If I use the connect-redis RedisStore then socket.io/lib/manager.js complains: this.store.subscribe(... TypeError Object # has no method 'subscribe'

If I use socketio.RedisStore then express/node_modules/connect/lib/middleware/session.js complains: TypeError: Object # has no method 'get'

*Note I would rather get the session.socket.io plugin working, but when I do the same setup with that plugin, express (also) complains: TypeError: Object # has no method 'get'

So is it ok that I use 2 different RedisStores for sessions, or do I need to somehow get one or the other working for both, and if so any ideas on how to fix?

My current code looks like this:

var
    CONST = {
        port: 80,
        sessionKey: 'your secret sauce'
    };

var 
    redis = require('redis');

var 
    express = require('express'),
    socketio = require('socket.io'),
    RedisStore = require('connect-redis')(express);

var 
    redisStore = new RedisStore(),
    socketStore = new socketio.RedisStore();

var 
    app = express(),
    server = require('http').createServer(app),
    io = socketio.listen(server);

app.configure(function(){
    app.use(express.cookieParser( CONST.sessionKey ));
    app.use(express.session({ secret: CONST.sessionKey, store: redisStore }));
    app.use(express.static(__dirname + '/test'));
    app.get('/', function (req, res) {res.sendfile(__dirname + '/test/' + 'index.htm');});
});

io.configure(function(){
    io.set('log level', 1);
    io.enable('browser client minification');
    io.enable('browser client etag');
    io.enable('browser client gzip');
    io.set('store', socketStore);
});

io.sockets.on('connection', function(socket){
    socket.emit('message', 'Test 1 from server')
});

server.listen( CONST.port );

console.log('running...');
Pastorale answered 2/3, 2013 at 0:58 Comment(0)
G
4

inside the io.configure, you have to link the socket with the http session.

Here's a piece of code that extracts the cookie (This is using socket.io with xhr-polling, I don't know if this would work for websocket, although I suspect it would work).

var cookie = require('cookie');
var connect = require('connect');

var sessionStore = new RedisStore({
  client: redis // the redis client
});

socketio.set('authorization', function(data, cb) {
  if (data.headers.cookie) {
    var sessionCookie = cookie.parse(data.headers.cookie);
    var sessionID = connect.utils.parseSignedCookie(sessionCookie['connect.sid'], secret);
    sessionStore.get(sessionID, function(err, session) {
      if (err || !session) {
        cb('Error', false);
      } else {
        data.session = session;
        data.sessionID = sessionID;
        cb(null, true);
      }
    });
  } else {
    cb('No cookie', false);
  }
});

Then you can access the session using:

socket.on("selector", function(data, reply) {
  var session = this.handshake.session;
  ...
}

This also has the added benefit that it checks there is a valid session, so only your logged in users can use sockets. You can use a different logic, though.

Globose answered 2/3, 2013 at 1:31 Comment(4)
Thanks for the quick reply,so is this the same solution if I wanted to use the session.socket.io plugin? or is that a different problem alltogether?Pastorale
This does not use the session.socket.io plugin and works on recent versions of socket.io and express.Globose
Appears to be working, was hoping to use the session.socket.io but this works well, so thanks.Pastorale
Note to get this working I had to remove 'io.set('store', socketStore);' which is really quite an unfortunate compromise as this means socket.io will not be able to share its state over multiple processes using redis :( Hopefully I will find a way in the future.Pastorale
R
4

Looking at your last note (won't be able to share its state over multiple processes using redis) I had the same problem and found a solution:

var express = require("express.io");
var swig = require('swig');
var redis = require('redis');
var RedisStore = require('connect-redis')(express);

workers = function() {
    var app = express().http().io();

    app.use(express.cookieParser());
    app.use(express.session({
        secret: 'very cool secretcode',
        store: new RedisStore({ client: redis.createClient() })
    }));

    app.io.set('store', new express.io.RedisStore({
        redisPub: redis.createClient(),
        redisSub: redis.createClient(),
        redisClient: redis.createClient()
    }));


    app.get('/', function(req, res) {
        res.render('index');
    });

    app.listen(3000);


    app.io.route('ready', function(req){
        //setup session stuff, use session stuff, etc.  Or make new routes
    });
};

cluster = require('cluster');
numCPUs = require('os').cpus().length;

if (cluster.isMaster)
{
    for (var i = 0; i < numCPUs; i++)
    {
        cluster.fork();
    }
}
else
{
    workers();
}
Rely answered 16/11, 2013 at 3:46 Comment(1)
The key here is app.io.set('store', new express.io.RedisStore({ using the express.io.RedisStore. Thanks!Trimmer

© 2022 - 2024 — McMap. All rights reserved.