7

I can connect my Node app to Redis just fine without a password, but when I add a password, nothing I do is right.

Here's my code right now, taken right from an example:

var redis = require('redis')
  , sio = require('socket.io')
  , RedisStore = sio.RedisStore
  , io = sio.listen();

var port = 6379
  , hostname = 'localhost'
  , password = 'password';

var redisClient = redis.createClient(port, hostname);
redisClient.auth(password, function (err) { if (err) throw err; });

var redisSubscriber = redis.createClient(port, hostname);
redisSubscriber.auth(password, function (err) { if (err) throw err; });

io.set('store', new RedisStore({ redisPub: redisClient, redisSub: redisSubscriber, redisClient: redisClient }));

On running the app, I get this stack trace:

/home/eric/christmas/sockets/node_modules/socket.io/node_modules/redis/index.js:506
                throw callback_err;
                      ^
Error: Ready check failed: ERR operation not permitted
    at RedisClient.on_info_cmd (/home/eric/christmas/sockets/node_modules/socket.io/node_modules/redis/index.js:319:35)
    at Command.RedisClient.ready_check.send_anyway [as callback] (/home/eric/christmas/sockets/node_modules/socket.io/node_modules/redis/index.js:367:14)
    at RedisClient.return_error (/home/eric/christmas/sockets/node_modules/socket.io/node_modules/redis/index.js:502:25)
    at RedisReplyParser.RedisClient.init_parser (/home/eric/christmas/sockets/node_modules/socket.io/node_modules/redis/index.js:262:14)
    at RedisReplyParser.EventEmitter.emit (events.js:93:17)
    at RedisReplyParser.send_error (/home/eric/christmas/sockets/node_modules/socket.io/node_modules/redis/lib/parser/javascript.js:266:14)
    at RedisReplyParser.execute (/home/eric/christmas/sockets/node_modules/socket.io/node_modules/redis/lib/parser/javascript.js:125:22)
    at RedisClient.on_data (/home/eric/christmas/sockets/node_modules/socket.io/node_modules/redis/index.js:478:27)
    at Socket.<anonymous> (/home/eric/christmas/sockets/node_modules/socket.io/node_modules/redis/index.js:79:14)
    at Socket.EventEmitter.emit (events.js:93:17)

The line generating this is the final one - if I comment out the attempt to set the RedisStore, I do not get any errors.

I'm sure the password is right (I can verify it in redis-cli, and if I change the password to be wrong I can verify that the auth callbacks don't fire). This code also works if I remove the password and comment out the two auth lines.

All of the working examples on blog posts and docs and the like show that this should work, and I don't know why mine isn't. I don't know which part of the stack to look at.

Here's what the redis-cli monitor looks like when I run the code above:

1353227107.912512 [0 127.0.0.1:56759] "auth" "password"
1353227107.912719 [0 127.0.0.1:56758] "auth" "password"
1353227107.913470 [0 127.0.0.1:56759] "info"
1353227107.913639 [0 127.0.0.1:56758] "info"

And here's what redis-cli monitor shows if I turn off the password, comment out the auth lines above, and successfully run the app:

1353227252.401667 [0 127.0.0.1:56771] "info"
1353227252.402020 [0 127.0.0.1:56770] "info"
1353227252.402131 [0 127.0.0.1:56769] "info"
1353227252.402423 [0 127.0.0.1:56768] "info"
1353227252.402611 [0 127.0.0.1:56767] "info"
1353227252.406254 [0 127.0.0.1:56770] "subscribe" "handshake"
1353227252.406287 [0 127.0.0.1:56770] "subscribe" "connect"
1353227252.406314 [0 127.0.0.1:56770] "subscribe" "open"
1353227252.406321 [0 127.0.0.1:56770] "subscribe" "join"
1353227252.406326 [0 127.0.0.1:56770] "subscribe" "leave"
1353227252.406337 [0 127.0.0.1:56770] "subscribe" "close"
1353227252.406354 [0 127.0.0.1:56770] "subscribe" "dispatch"
1353227252.406372 [0 127.0.0.1:56770] "subscribe" "disconnect"

The successful (passwordless) connection makes 5 "info" commands, and my unsuccessful (passworded) command makes 2 - and then dies on a call to an "on_info_cmd" method.

Can anyone make sense of this? Thanks for any help you can give.

1

2 Answers 2

9

I solved this by passing the redis module itself as an option to the RedisStore constructor.

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

This was necessary for the client objects to pass the instanceof RedisClient test and not be re-initialized without a password. Apparently, when RedisStore re-requires the redis module, redis clients created with the createClient method are members of some new class or something.

I figured this out by looking at a related issue someone was having on socket.io's issue #808.

Sign up to request clarification or add additional context in comments.

1 Comment

Had different error but the same solution helped... Probably error happened because Redis is used for Express sessions too and, maybe, the different module was used for store and for client?
1

Try calling redisClient.auth(password, function (err) { if (err) throw err; }); only once, redis stores the auth information for every call, calling it twice may throw an error.

4 Comments

It is actually only called once for each client - the first time is on redisClient, and the second is on redisSubscriber, a separate instance.
Yes I understand, but the client "stashes" the password, so you don't need to call it for multiple instances. Please look at Node_redis and Auth Example
That's not correct - it stashes it per-instance, not for the whole class. Every auth example I've seen of redis that creates a pub, sub, and a store client uses this pattern. Example: github.com/LearnBoost/socket.io/pull/551
@yeah, the stashing is for reconnects of the same client instance AFAIK, it's not shared between different clients.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.