Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError: Converting circular structure to JSON - when using RedisStore #575

Closed
alessioalex opened this issue Oct 13, 2011 · 6 comments
Closed

Comments

@alessioalex
Copy link

 node server.js 
   info  - socket.io started
Server started on port 8080
   warn  - handshake error Converting circular structure to JSON

/var/www/node_apps/chat/node_modules/socket.io/lib/stores/redis.js:103
  this.pub.publish(name, this.pack({ nodeId: this.nodeId, args: args }));
                              ^
TypeError: Converting circular structure to JSON
    at Redis.stringify [as pack] (native)
    at Redis.publish (/var/www/node_apps/chat/node_modules/socket.io/lib/stores/redis.js:103:31)
    at Manager.handleClient (/var/www/node_apps/chat/node_modules/socket.io/lib/manager.js:644:18)
    at Manager.handleUpgrade (/var/www/node_apps/chat/node_modules/socket.io/lib/manager.js:582:8)
    at HTTPServer.<anonymous> (/var/www/node_apps/chat/node_modules/socket.io/lib/manager.js:108:10)
    at HTTPServer.emit (events.js:81:20)
    at Socket.<anonymous> (http.js:1045:14)
    at Socket._onReadable (net.js:677:27)
    at IOWatcher.onReadable [as callback] (net.js:177:10)

I was trying to achieve the reloading session from here: www.danielbaulig.de/socket-ioexpress/

It works perfectly with Socket.IO and MemoryStore, but when I use RedisStore I get that error.

Any ideas?

@alessioalex
Copy link
Author

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;// << if I delete this line then the handshake is ok
        // else it throws that error from my first post
        sessionStore.get(data.sessionID, function (err, session) {
            if (err) {
                accept(err.message, 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);
    }
});

So the line that does all the "damage" is this one:

data.sessionStore = sessionStore;

@tj
Copy link
Contributor

tj commented Oct 13, 2011

why do you need to do that? I posted on that dude's blog somewhere, you shouldn't need to do that

@alessioalex
Copy link
Author

Indeed, didn't see that post. It fixed my problem. For others who are interested, this is TJ's useful post on how to load a Connect session in Socket.IO:

http://www.danielbaulig.de/socket-ioexpress/#comment-2705

io.set('authorization', function(data, fn){
  var cookies = utils.parseCookie(data.headers.cookie)
    , sid = cookies['connect.sid'];
  app.store.load(sid, function(err, sess){
    if (err) return fn(err);
    data.session = sess;
    fn(null, true);
  });
});

@katanacrimson
Copy link

@alessioalex sorry to bother, but what are you referring to with the "utils" var there? What part is this from - connect? express? socket.io?

I'm trying to handle connect's signed cookies here, and man it's being downright evil.

edit: I'm going to guess this is from connect. Here goes several hours.

edit: aawww, fuck. app.store.load has been removed.

                                        app.store.load(data.sessionID, function(err, session) {
                                                  ^
TypeError: Cannot call method 'load' of undefined

edit 3:

This is absolutely awful...I'm having to go for a messy solution as mentioned in this gist: https://gist.github.com/2946792

The state between socket.io and express3 is absolutely awful in terms of authorization.

@alessioalex
Copy link
Author

@damianb require('connect').utils

Btw I haven't tested yet Express 3 with SIO auth, so I can't tell you if this still works or not.

@katanacrimson
Copy link

@alessioalex took me 3+ hours and a lot of nasty code to get it working.

For the record, my own code (very debug state still) in case someone else needs a reference:

requires....uhhh, npm connect, also npm cookie alongside socket.io, and I'm using connect-redis here.

        io.set('authorization', function (data, accept) {
            if (data.headers.cookie) {
                data.cookie = cookie.parse(data.headers.cookie)
                data.cookie = connect.utils.parseSignedCookies(data.cookie, nconf.get('app:cookie:secret'))
                data.cookie = connect.utils.parseJSONCookies(data.cookie)
                data.sessionID = data.cookie[nconf.get('app:cookie:key')]
                sessionStore.load(data.sessionID, function (err, session) {
                    if (err || !session) {
                        // invalid session identifier. tl;dr gtfo.
                        accept('session error', false)
                    } else {
                        data.session = session
                        accept(null, true)
                    }
                })

            } else {
                accept('no authentication cookie received', false)
            }
        })

    // [...]

    io.sockets.on('connection', function (socket) {
        var sessionID = socket.handshake.sessionID,
            session = new connect.middleware.session.Session({ sessionStore: sessionStore }, socket.handshake.session)

        console.log('socket: new ' + sessionID)
        socket.broadcast.emit('arpNewConn', session.passport.user)

        var intervalID = setInterval(function() {
            socket.handshake.session.reload(function() {
                socket.handshake.session.touch().save()
            })
            socket.emit('pulse', { heartbeat: new Date().getTime() })
        }, 5 * 1000) // every 300 seconds, pulse to maintain the session (5s for testing)
    })

Should also note that you can't even use connect's MemoryStore with this as-is, otherwise it will leak memory like crazy and OOM within minutes - took way too long to get it to work. (the socket.handshake.session.touch().save() line seems to leak a metric shitton of memory every time it's called...)

Important thing to note is that sessionStore is your sessionStore - either RedisStore if it's npm package connect-redis in use or connect's MemoryStore. For some reason it was very problematic to get connect to wrap the session (via new connect.middleware.session.Session() up until sessionStore.load was being used, even though there was nil difference in the data being provided from my own observations. It's a very frail and finicky beast, alas.

Remember, no MemoryStore, gotta use either RedisStore or something else - otherwise it leaks like hell.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants