Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ client.get("missingkey", function(err, reply) {
});
```

If you want receive reply as Buffers instead of Strings just add "b_" prefix before command name:

```js
client.b_get("some key", function(err, buf) {});
```

For a list of Redis commands, see [Redis Command Reference](http://redis.io/commands)

Minimal parsing is done on the replies. Commands that return a integer return JavaScript Numbers, arrays return JavaScript Array. `HGETALL` returns an Object keyed by the hash keys. All strings will either be returned as string or as buffer depending on your setting.
Expand Down Expand Up @@ -666,6 +672,10 @@ The command_name has to be lower case.

All commands are sent as multi-bulk commands. `args` can either be an Array of arguments, or omitted / set to undefined.

## client.send_command_buf(command_name[, [args][, callback]])

The same as client.send_command but is sending reply as Buffers instead of Strings.

## client.connected

Boolean tracking the state of the connection to the Redis server.
Expand Down
70 changes: 41 additions & 29 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,6 @@ if (typeof EventEmitter !== 'function') {

function noop () {}

function handle_detect_buffers_reply (reply, command, buffer_args) {
if (buffer_args === false || this.message_buffers) {
// If detect_buffers option was specified, then the reply from the parser will be a buffer.
// If this command did not use Buffer arguments, then convert the reply to Strings here.
reply = utils.reply_to_strings(reply);
}

if (command === 'hgetall') {
reply = utils.reply_to_object(reply);
}
return reply;
}

exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG);

// Attention: The second parameter might be removed at will and is not officially supported.
Expand Down Expand Up @@ -111,10 +98,6 @@ function RedisClient (options, stream) {
self.warn('WARNING: You activated return_buffers and detect_buffers at the same time. The return value is always going to be a buffer.');
options.detect_buffers = false;
}
if (options.detect_buffers) {
// We only need to look at the arguments if we do not know what we have to return
this.handle_reply = handle_detect_buffers_reply;
}
this.should_buffer = false;
this.max_attempts = options.max_attempts | 0;
if ('max_attempts' in options) {
Expand Down Expand Up @@ -143,17 +126,18 @@ function RedisClient (options, stream) {
this.pub_sub_mode = 0;
this.subscription_set = {};
this.monitoring = false;
this.message_buffers = false;
this.message_buffers = false; // Do we have subscribes on message_buffer event
this.closing = false;
this.server_info = {};
this.auth_pass = options.auth_pass || options.password;
this.selected_db = options.db; // Save the selected db here, used when reconnecting
this.old_state = null;
this.fire_strings = true; // Determine if strings or buffers should be written to the stream
this.cur_command_ret_buf = 0;
this.pipeline = false;
this.sub_commands_left = 0;
this.times_connected = 0;
this.buffers = options.return_buffers || options.detect_buffers;
this.using_buffer_parser = options.return_buffers || options.detect_buffers;
this.options = options;
this.reply = 'ON'; // Returning replies is the default
// Init parser
Expand All @@ -171,17 +155,23 @@ function RedisClient (options, stream) {
'The drain event listener is deprecated and will be removed in v.3.0.0.\n' +
'If you want to keep on listening to this event please listen to the stream drain event directly.'
);
} else if (event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer' && !this.buffers) {
} else if (event === 'message_buffer' || event === 'pmessage_buffer' || event === 'messageBuffer' || event === 'pmessageBuffer') {
this.message_buffers = true;
this.handle_reply = handle_detect_buffers_reply;
this.reply_parser = create_parser(this);
if (!this.using_buffer_parser) {
this.switchToBufferParser();
}
}
});
}
util.inherits(RedisClient, EventEmitter);

RedisClient.connection_id = 0;

RedisClient.prototype.switchToBufferParser = function() {
this.using_buffer_parser = true;
this.reply_parser = create_parser(this);
}

function create_parser (self) {
return new Parser({
returnReply: function (data) {
Expand All @@ -206,7 +196,7 @@ function create_parser (self) {
self.emit('error', err);
self.create_stream();
},
returnBuffers: self.buffers || self.message_buffers,
returnBuffers: self.using_buffer_parser,
name: self.options.parser || 'javascript',
stringNumbers: self.options.string_numbers || false
});
Expand Down Expand Up @@ -302,7 +292,12 @@ RedisClient.prototype.create_stream = function () {
}
};

RedisClient.prototype.handle_reply = function (reply, command) {
RedisClient.prototype.handle_reply = function (reply, command, buffer_reply) {
if (!buffer_reply && this.using_buffer_parser) {
// If nobody wants buffers for any reasons then we must convert them to strings
reply = utils.reply_to_strings(reply);
}

if (command === 'hgetall') {
reply = utils.reply_to_object(reply);
}
Expand Down Expand Up @@ -709,7 +704,7 @@ function normal_reply (self, reply) {
var command_obj = self.command_queue.shift();
if (typeof command_obj.callback === 'function') {
if (command_obj.command !== 'exec') {
reply = self.handle_reply(reply, command_obj.command, command_obj.buffer_args);
reply = self.handle_reply(reply, command_obj.command, command_obj.buffer_reply);
}
command_obj.callback(null, reply);
} else {
Expand All @@ -721,8 +716,7 @@ function subscribe_unsubscribe (self, reply, type) {
// Subscribe commands take an optional callback and also emit an event, but only the _last_ response is included in the callback
// The pub sub commands return each argument in a separate return value and have to be handled that way
var command_obj = self.command_queue.get(0);
var buffer = self.options.return_buffers || self.options.detect_buffers && command_obj.buffer_args;
var channel = (buffer || reply[1] === null) ? reply[1] : reply[1].toString();
var channel = command_obj.buffer_reply || reply[1] === null ? reply[1] : reply[1].toString();
var count = +reply[2]; // Return the channel counter as number no matter if `string_numbers` is activated or not
debug(type, channel);

Expand Down Expand Up @@ -796,7 +790,7 @@ RedisClient.prototype.return_reply = function (reply) {
// the average performance of all other commands in case of no monitor mode
if (this.monitoring) {
var replyStr;
if (this.buffers && Buffer.isBuffer(reply)) {
if (this.using_buffer_parser && Buffer.isBuffer(reply)) {
replyStr = reply.toString();
} else {
replyStr = reply;
Expand Down Expand Up @@ -856,6 +850,11 @@ function handle_offline_command (self, command_obj) {
self.should_buffer = true;
}

RedisClient.prototype.internal_send_command_buf = function (command_obj) {
command_obj.buffer_reply = true;
return this.internal_send_command(command_obj);
};

// Do not call internal_send_command directly, if you are not absolutly certain it handles everything properly
// e.g. monitor / info does not work with internal_send_command only
RedisClient.prototype.internal_send_command = function (command_obj) {
Expand All @@ -868,6 +867,11 @@ RedisClient.prototype.internal_send_command = function (command_obj) {
var big_data = false;
var args_copy = new Array(len);

if (this.cur_command_ret_buf) {
// Needed for send_command_buf and overwritten individual commands with "b_" prefix
command_obj.buffer_reply = true;
}

if (this.ready === false || this.stream.writable === false) {
// Handle offline commands right away
handle_offline_command(this, command_obj);
Expand Down Expand Up @@ -899,7 +903,7 @@ RedisClient.prototype.internal_send_command = function (command_obj) {
args_copy[i] = 'null'; // Backwards compatible :/
} else if (Buffer.isBuffer(args[i])) {
args_copy[i] = args[i];
command_obj.buffer_args = true;
if (this.options.detect_buffers) command_obj.buffer_reply = true;
big_data = true;
} else {
this.warn(
Expand All @@ -922,6 +926,14 @@ RedisClient.prototype.internal_send_command = function (command_obj) {
}
}

if (this.options.return_buffers) {
command_obj.buffer_reply = true;
}

if (command_obj.buffer_reply && !this.using_buffer_parser) {
this.switchToBufferParser();
}

if (this.options.prefix) {
prefix_keys = commands.getKeyIndexes(command, args_copy);
for (i = prefix_keys.pop(); i !== undefined; i = prefix_keys.pop()) {
Expand Down
4 changes: 2 additions & 2 deletions lib/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

var betterStackTraces = /development/i.test(process.env.NODE_ENV) || /\bredis\b/i.test(process.env.NODE_DEBUG);

function Command (command, args, callback, call_on_write) {
function Command (command, args, callback, call_on_write, buffer_reply) {
this.command = command;
this.args = args;
this.buffer_args = false;
this.callback = callback;
this.call_on_write = call_on_write;
this.buffer_reply = buffer_reply;
if (betterStackTraces) {
this.error = new Error();
}
Expand Down
Loading