1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-04 15:02:09 +03:00

Add magical auth command.

Authentication is now remembered by the client and will be automatically sent to the server
on every connection, including any reconnections.
This commit is contained in:
Matt Ranney
2011-02-27 23:10:44 -10:00
parent 2534f740aa
commit 1a14e24faa
7 changed files with 99 additions and 40 deletions

View File

@@ -181,6 +181,13 @@ objects instead of JavaScript Strings.
`createClient()` returns a `RedisClient` object that is named `client` in all of the examples here. `createClient()` returns a `RedisClient` object that is named `client` in all of the examples here.
## client.auth(password, callback)
When connecting to Redis servers that require authentication, the `AUTH` command must be sent as the
first command after connecting. This can be tricky to coordinate with reconnections, the ready check,
etc. To make this easier, `client.auth()` stashes `password` and will send it after each connection,
including reconnections. `callback` is invoked only once, after the response to the very first
`AUTH` command sent.
## client.end() ## client.end()

View File

@@ -1,6 +1,13 @@
Changelog Changelog
========= =========
## v0.5.7 - February 27, 2011
Add magical auth command.
Authentication is now remembered by the client and will be automatically sent to the server
on every connection, including any reconnections.
## v0.5.6 - February 22, 2011 ## v0.5.6 - February 22, 2011
Fix bug in ready check with `return_buffers` set to `true`. Fix bug in ready check with `return_buffers` set to `true`.

View File

@@ -1,13 +1,5 @@
// Note - Eventually this functionality will be built in to the client library
var redis = require("redis"), var redis = require("redis"),
client = redis.createClient(); client = redis.createClient();
// whenever the client connects, make sure to auth // This command is magical. Client stashes the password and will issue on every connect.
client.on("connect", function () {
client.auth("somepass", redis.print);
});
client.auth("somepass"); client.auth("somepass");
// then do whatever you want

View File

@@ -30,10 +30,9 @@ client1.on("message", function (channel, message) {
} }
}); });
client1.incr("did a thing");
client1.on("ready", function () { client1.on("ready", function () {
// if you need auth, do it here // if you need auth, do it here
client1.incr("did a thing");
client1.subscribe("a nice channel", "another one"); client1.subscribe("a nice channel", "another one");
}); });

100
index.js
View File

@@ -50,6 +50,7 @@ function RedisClient(stream, options) {
this.subscriptions = false; this.subscriptions = false;
this.closing = false; this.closing = false;
this.server_info = {}; this.server_info = {};
this.auth_pass = null;
var parser_module, self = this; var parser_module, self = this;
@@ -90,30 +91,9 @@ function RedisClient(stream, options) {
}); });
this.stream.on("connect", function () { this.stream.on("connect", function () {
if (exports.debug_mode) { self.on_connect();
console.log("Stream connected fd " + self.stream.fd);
}
self.connected = true;
self.ready = false;
self.connections += 1;
self.command_queue = new Queue();
self.emitted_end = false;
self.retry_timer = null;
self.retry_delay = 250;
self.stream.setNoDelay();
self.stream.setTimeout(0);
self.emit("connect");
if (self.options.no_ready_check) {
self.ready = true;
self.send_offline_queue();
} else {
self.ready_check();
}
}); });
this.stream.on("data", function (buffer_from_socket) { this.stream.on("data", function (buffer_from_socket) {
self.on_data(buffer_from_socket); self.on_data(buffer_from_socket);
}); });
@@ -164,6 +144,55 @@ function RedisClient(stream, options) {
util.inherits(RedisClient, events.EventEmitter); util.inherits(RedisClient, events.EventEmitter);
exports.RedisClient = RedisClient; exports.RedisClient = RedisClient;
RedisClient.prototype.on_connect = function () {
if (exports.debug_mode) {
console.log("Stream connected " + this.host + ":" + this.port + " fd " + this.stream.fd);
}
var self = this;
this.connected = true;
this.ready = false;
this.connections += 1;
this.command_queue = new Queue();
this.emitted_end = false;
this.retry_timer = null;
this.retry_delay = 250;
this.stream.setNoDelay();
this.stream.setTimeout(0);
if (this.auth_pass) {
if (exports.debug_mode) {
console.log("Sending auth to " + this.host + ":" + this.port + " fd " + this.stream.fd);
}
self.send_anyway = true;
self.send_command("auth", this.auth_pass, function (err, res) {
if (err) {
return self.emit("error", "Auth error: " + err);
}
if (res.toString() !== "OK") {
return self.emit("error", "Auth failed: " + res.toString());
}
if (exports.debug_mode) {
console.log("Auth succeeded " + self.host + ":" + self.port + " fd " + self.stream.fd);
}
if (self.auth_callback) {
self.auth_callback(err, res);
self.auth_callback = null;
}
});
self.send_anyway = false;
}
this.emit("connect");
if (this.options.no_ready_check) {
this.ready = true;
this.send_offline_queue();
} else {
this.ready_check();
}
};
RedisClient.prototype.ready_check = function () { RedisClient.prototype.ready_check = function () {
var self = this; var self = this;
@@ -175,8 +204,7 @@ RedisClient.prototype.ready_check = function () {
self.send_anyway = true; // secret flag to send_command to send something even if not "ready" self.send_anyway = true; // secret flag to send_command to send something even if not "ready"
self.info(function (err, res) { self.info(function (err, res) {
if (err) { if (err) {
self.emit("error", "Ready check failed: " + err); return self.emit("error", "Ready check failed: " + err);
return;
} }
var lines = res.toString().split("\r\n"), obj = {}, retry_time; var lines = res.toString().split("\r\n"), obj = {}, retry_time;
@@ -291,7 +319,7 @@ RedisClient.prototype.connection_gone = function (why) {
RedisClient.prototype.on_data = function (data) { RedisClient.prototype.on_data = function (data) {
if (exports.debug_mode) { if (exports.debug_mode) {
console.log("net read fd " + this.stream.fd + ": " + data.toString()); console.log("net read " + this.host + ":" + this.port + " fd " + this.stream.fd + ": " + data.toString());
} }
try { try {
@@ -480,7 +508,7 @@ RedisClient.prototype.send_command = function () {
command_str += "$" + Buffer.byteLength(arg) + "\r\n" + arg + "\r\n"; command_str += "$" + Buffer.byteLength(arg) + "\r\n" + arg + "\r\n";
} }
if (exports.debug_mode) { if (exports.debug_mode) {
console.log("send fd " + this.stream.fd + ": " + command_str); console.log("send " + this.host + ":" + this.port + " fd " + this.stream.fd + ": " + command_str);
} }
stream.write(command_str); stream.write(command_str);
} else { } else {
@@ -547,7 +575,7 @@ function Multi(client, args) {
//bit commands //bit commands
"getbit", "setbit", "getrange", "setrange", "getbit", "setbit", "getrange", "setrange",
// misc // misc
"getset", "mset", "msetnx", "randomkey", "select", "move", "rename", "renamenx", "expire", "expireat", "keys", "dbsize", "auth", "ping", "echo", "getset", "mset", "msetnx", "randomkey", "select", "move", "rename", "renamenx", "expire", "expireat", "keys", "dbsize", "ping", "echo",
"save", "bgsave", "bgwriteaof", "shutdown", "lastsave", "type", "sync", "flushdb", "flushall", "sort", "info", "save", "bgsave", "bgwriteaof", "shutdown", "lastsave", "type", "sync", "flushdb", "flushall", "sort", "info",
"monitor", "ttl", "persist", "slaveof", "debug", "config", "subscribe", "unsubscribe", "psubscribe", "punsubscribe", "publish", "watch", "unwatch", "monitor", "ttl", "persist", "slaveof", "debug", "config", "subscribe", "unsubscribe", "psubscribe", "punsubscribe", "publish", "watch", "unwatch",
"quit" "quit"
@@ -568,6 +596,22 @@ function Multi(client, args) {
Multi.prototype[command.toUpperCase()] = Multi.prototype[command]; Multi.prototype[command.toUpperCase()] = Multi.prototype[command];
}); });
// Stash auth for connect and reconnect. Send immediately if already connected.
RedisClient.prototype.auth = function () {
var args = to_array(arguments);
this.auth_pass = args[0];
this.auth_callback = args[1];
if (exports.debug_mode) {
console.log("Saving auth as " + this.auth_pass);
}
if (this.connected) {
args.unshift("auth");
this.send_command.apply(this, args);
}
};
RedisClient.prototype.AUTH = RedisClient.prototype.auth;
RedisClient.prototype.hmset = function () { RedisClient.prototype.hmset = function () {
var args = to_array(arguments), tmp_args; var args = to_array(arguments), tmp_args;
if (args.length >= 2 && typeof args[0] === "string" && typeof args[1] === "object") { if (args.length >= 2 && typeof args[0] === "string" && typeof args[1] === "object") {

View File

@@ -1,5 +1,5 @@
{ "name" : "redis", { "name" : "redis",
"version" : "0.5.6", "version" : "0.5.7",
"description" : "Redis client library", "description" : "Redis client library",
"author": "Matt Ranney <mjr@ranney.com>", "author": "Matt Ranney <mjr@ranney.com>",
"contributors": [ "contributors": [

10
test.js
View File

@@ -3,6 +3,7 @@ var redis = require("./index"),
client = redis.createClient(), client = redis.createClient(),
client2 = redis.createClient(), client2 = redis.createClient(),
client3 = redis.createClient(), client3 = redis.createClient(),
client4 = redis.createClient(9006, "filefish.redistogo.com"),
assert = require("assert"), assert = require("assert"),
util = require("./lib/util").util, util = require("./lib/util").util,
test_db_num = 15, // this DB will be flushed and used for testing test_db_num = 15, // this DB will be flushed and used for testing
@@ -1049,6 +1050,7 @@ function run_next_test() {
console.log('\n completed \x1b[32m%d\x1b[0m tests in \x1b[33m%d\x1b[0m ms\n', test_count, new Date() - all_start); console.log('\n completed \x1b[32m%d\x1b[0m tests in \x1b[33m%d\x1b[0m ms\n', test_count, new Date() - all_start);
client.quit(); client.quit();
client2.quit(); client2.quit();
client4.quit();
} }
} }
@@ -1066,6 +1068,14 @@ client.on('end', function () {
ended = true; ended = true;
}); });
// TODO - need a better way to test auth, maybe auto-config a local Redis server?
client4.auth("664b1b6aaf134e1ec281945a8de702a9", function (err, res) {
if (err) {
assert.fail(err, name);
}
assert.strictEqual("OK", res.toString(), "auth");
});
// Exit immediately on connection failure, which triggers "exit", below, which fails the test // Exit immediately on connection failure, which triggers "exit", below, which fails the test
client.on("error", function (err) { client.on("error", function (err) {
console.error("client: " + err.stack); console.error("client: " + err.stack);