socket.io and Express. Tying it all together.

NOTE: This article was written for Express 2.x.x. It might not work for Express 3 without modification.
Express is a great web development framework for node.js. It provides easy access to stuff like routing, requests and sessions. socket.io is an abstraction layer for Websockets, with Flash and XHR fallbacks, that runs in both node.js and the client.

The Basics

You can have socket.io run with Express easily. By simply invoking socket.io’s listen method and passing it the Express app as a parameter:

var io = require('socket.io'),
    express = require('express'),
    app = express.createServer();
 
app.configure(function () {
    app.use(express.cookieParser());
    app.use(express.session({secret: 'secret', key: 'express.sid'}));
    app.use(function (req, res) {
        res.end('<h2>Hello, your session id is ' + req.sessionID + '</h2>');
    });
});
 
app.listen();
var sio = io.listen(app);
 
sio.sockets.on('connection', function (socket) {
    console.log('A socket connected!');
});

However, socket.io will not be aware of Express – and the other way around. So if a socket connects, we do not know to which Express session it belongs, but in most scenarios this is an essential information. Since socket.io 0.7 however we can easily obtain this information through the handshake/authorization mechanism. Through this we can tell socket.io to invoke a user-defined function whenever a new Websocket connection is incoming. The important point is, that it is called, before the connection is completed. This provides multiple benefits. For one, we can accept or revoke the connection based on various conditions and for the other part it allows us to inspect the header information of the HTTP request that is trying to establish the Websocket connection – including the cookie. The cookie will contain the sessionID of our Express session.

var parseCookie = require('connect').utils.parseCookie;
 
sio.set('authorization', function (data, accept) {
    // check if there's a cookie header
    if (data.headers.cookie) {
        // if there is, parse the cookie
        data.cookie = parseCookie(data.headers.cookie);
        // note that you will need to use the same key to grad the
        // session id, as you specified in the Express setup.
        data.sessionID = data.cookie['express.sid'];
    } else {
       // if there isn't, turn down the connection with a message
       // and leave the function.
       return accept('No cookie transmitted.', false);
    }
    // accept the incoming connection
    accept(null, true);
});

All the attributes, that are assigned to the data object are now accessible through the handshake attribute of the socket.io connection object.

sio.sockets.on('connection', function (socket) {
    console.log('A socket with sessionID ' + socket.handshake.sessionID 
        + ' connected!');
});

Getting, serious.

But what I find much more interesting is that we can not only extract the sessionID from the cookie, but we can also load the actual session and use, modify or even destroy it. For that we need to get hold of the session store that Express uses to save our sessions. By default this is a MemoryStore and will be created by Express. Instead we can create our own and pass it to Express. That way we have a reference to that store for ourself.

var io = require('socket.io'),
    express = require('express'),
    MemoryStore = express.session.MemoryStore,
    app = express.createServer(),
    sessionStore = new MemoryStore();
 
app.configure(function () {
    app.use(express.cookieParser());
    app.use(express.session({store: sessionStore
        , secret: 'secret'
        , key: 'express.sid'}));
    app.use(function (req, res) {
        res.end('<h2>Hello, your session id is ' + req.sessionID + '</h2>');
    });
});

Now, in our handshake function, we can not only get the sessionID from the cookie, but actually load the session data from the session store.

sio.set('authorization', function (data, accept) {
    if (data.headers.cookie) {
        data.cookie = parseCookie(data.headers.cookie);
        data.sessionID = data.cookie['express.sid'];
        // (literally) get the session data from the session store
        sessionStore.get(data.sessionID, function (err, session) {
            if (err || !session) {
                // if we cannot grab a session, turn down the connection
                accept('Error', false);
            } else {
                // save the session data and accept the connection
                data.session = session;
                accept(null, true);
            }
        });
    } else {
       return accept('No cookie transmitted.', false);
    }
});

All the good stuff

Now we have access to all of the session’s data through socket.handshake.session. But to be able to change the session data we will need to create an actual Express Session object. The constructor for the Express session object requires the request associated with the session aswell as the session data. We just acquired the session data from the session store, so we have that. But we do not have access to the HTTP request that is associated to the session. socket.io does not expose it to us. If you look closer at the Session prototype though you see, that there are only 3 properties, that are required on the request object passed to the constructor: sessionID, session and sessionStore. This is good news, since our handshake data object already has the properties sessionID and session. Both (after we created the Session object) with the values that the Session prototype expects to be there. Well and the last property, sessionStore, we can easily add to the data object.

var Session = require('connect').middleware.session.Session;
sio.set('authorization', function (data, accept) {
    if (data.headers.cookie) {
        data.cookie = parseCookie(data.headers.cookie);
        data.sessionID = data.cookie['express.sid'];
        // save the session store to the data object 
        // (as required by the Session constructor)
        data.sessionStore = sessionStore;
        sessionStore.get(data.sessionID, function (err, session) {
            if (err || !session) {
                accept('Error', false);
            } else {
                // create a session object, passing data as request and our
                // just acquired session data
                data.session = new Session(data, session);
                accept(null, true);
            }
        });
    } else {
       return accept('No cookie transmitted.', false);
    }
});

Now we can use the session object to modify and even destroy the session. Eg. you could use Session’s touch() method to keep the session from timing out for as long as the Websocket connection remains

sio.sockets.on('connection', function (socket) {
    var hs = socket.handshake;
    console.log('A socket with sessionID ' + hs.sessionID 
        + ' connected!');
    // setup an inteval that will keep our session fresh
    var intervalID = setInterval(function () {
        // reload the session (just in case something changed,
        // we don't want to override anything, but the age)
        // reloading will also ensure we keep an up2date copy
        // of the session with our connection.
        hs.session.reload( function () { 
            // "touch" it (resetting maxAge and lastAccess)
            // and save it back again.
            hs.session.touch().save();
        });
    }, 60 * 1000);
    socket.on('disconnect', function () {
        console.log('A socket with sessionID ' + hs.sessionID 
            + ' disconnected!');
        // clear the socket interval to stop refreshing the session
        clearInterval(intervalID);
    });
 
});

Sum it all up

From this code base you can keep going. You have everything set up to make your Express/socket.io hybrid application run and interact with each of it’s components as you wish. If you have any comments or suggestions, please feel free to use the comment box below. If you would like to see socket.io and Express in action together, have a look at my demo application.