heroku-debug
Version:
debugging plugin for the CLI
209 lines (193 loc) • 6.4 kB
JavaScript
;
var _ = require('lodash');
var Command = require('../command');
var SubscriptionSet = require('../subscription_set');
var debug = require('debug')('ioredis:reply');
var Parser = require('redis-parser');
var ReplyError = require('../reply_error');
/**
* Init the parser
*
* @method _initParser
* @memberOf Redis#
* @private
*/
exports.initParser = function () {
var _this = this;
this.replyParser = new Parser({
name: this.options.parser,
stringNumbers: this.options.stringNumbers,
returnBuffers: !this.options.dropBufferSupport,
returnError: function (err) {
_this.returnError(new ReplyError(err.message));
},
returnReply: function (reply) {
_this.returnReply(reply);
},
returnFatalError: function (err) {
_this.flushQueue(err, { offlineQueue: false });
_this.silentEmit('error', new Error('Redis parser fatal error: ' + err.stack));
_this.disconnect(true);
}
});
if (this.replyParser.name === 'hiredis' && !this.options.dropBufferSupport) {
console.warn('[WARN] ioredis is using hiredis parser, however "dropBufferSupport" is disabled. ' +
'It\'s highly recommanded to enable this option. ' +
'Refer to https://github.com/luin/ioredis/wiki/Improve-Performance for more details.');
}
};
exports.returnError = function (err) {
var item = this.commandQueue.shift();
err.command = {
name: item.command.name,
args: item.command.args
};
var needReconnect = false;
if (this.options.reconnectOnError) {
needReconnect = this.options.reconnectOnError(err);
}
switch (needReconnect) {
case 1:
case true:
if (this.status !== 'reconnecting') {
this.disconnect(true);
}
item.command.reject(err);
break;
case 2:
if (this.status !== 'reconnecting') {
this.disconnect(true);
}
if (this.condition.select !== item.select && item.command.name !== 'select') {
this.select(item.select);
}
this.sendCommand(item.command);
break;
default:
item.command.reject(err);
}
};
var sharedBuffers = {};
_.forEach(['message', 'pmessage', 'subscribe', 'psubscribe', 'unsubscribe', 'punsubscribe'], function (str) {
sharedBuffers[str] = new Buffer(str);
});
exports.returnReply = function (reply) {
if (this.status === 'monitoring') {
// Valid commands in the monitoring mode are AUTH and MONITOR,
// both of which always reply with 'OK'.
var replyStr = reply.toString();
// If not the reply to AUTH & MONITOR
if (replyStr !== 'OK') {
// Since commands sent in the monitoring mode will trigger an exception,
// any replies we received in the monitoring mode should consider to be
// realtime monitor data instead of result of commands.
var len = replyStr.indexOf(' ');
var timestamp = replyStr.slice(0, len);
var argindex = replyStr.indexOf('"');
var args = replyStr.slice(argindex + 1, -1).split('" "').map(function (elem) {
return elem.replace(/\\"/g, '"');
});
var dbAndSource = replyStr.slice(len + 2, argindex - 2).split(' ');
this.emit('monitor', timestamp, args, dbAndSource[1], dbAndSource[0]);
return;
}
}
var item, channel, count;
if (this.condition.subscriber) {
var replyType = Array.isArray(reply) ? reply[0].toString() : null;
debug('receive reply "%s" in subscriber mode', replyType);
switch (replyType) {
case 'message':
if (this.listeners('message').length > 0) {
this.emit('message', reply[1].toString(), reply[2].toString());
}
if (this.listeners('messageBuffer').length > 0) {
this.emit('messageBuffer', reply[1], reply[2]);
}
break;
case 'pmessage':
var pattern = reply[1].toString();
if (this.listeners('pmessage').length > 0) {
this.emit('pmessage', pattern, reply[2].toString(), reply[3].toString());
}
if (this.listeners('pmessageBuffer').length > 0) {
this.emit('pmessageBuffer', pattern, reply[2], reply[3]);
}
break;
case 'subscribe':
case 'psubscribe':
channel = reply[1].toString();
this.condition.subscriber.add(replyType, channel);
item = this.commandQueue.shift();
if (!fillSubCommand(item.command, reply[2])) {
this.commandQueue.unshift(item);
}
break;
case 'unsubscribe':
case 'punsubscribe':
channel = reply[1] ? reply[1].toString() : null;
if (channel) {
this.condition.subscriber.del(replyType, channel);
}
count = reply[2];
if (count === 0) {
this.condition.subscriber = false;
}
item = this.commandQueue.shift();
if (!fillUnsubCommand(item.command, count)) {
this.commandQueue.unshift(item);
}
break;
default:
item = this.commandQueue.shift();
item.command.resolve(reply);
}
} else {
item = this.commandQueue.shift();
if (!item) {
return this.emit('error',
new Error('Command queue state error. If you can reproduce this, please report it. Last reply: ' +
reply.toString()));
}
if (Command.checkFlag('ENTER_SUBSCRIBER_MODE', item.command.name)) {
this.condition.subscriber = new SubscriptionSet();
this.condition.subscriber.add(item.command.name, reply[1].toString());
if (!fillSubCommand(item.command, reply[2])) {
this.commandQueue.unshift(item);
}
} else if (Command.checkFlag('EXIT_SUBSCRIBER_MODE', item.command.name)) {
if (!fillUnsubCommand(item.command, reply[2])) {
this.commandQueue.unshift(item);
}
} else {
item.command.resolve(reply);
}
}
function fillSubCommand(command, count) {
if (typeof command.remainReplies === 'undefined') {
command.remainReplies = command.args.length;
}
if (--command.remainReplies === 0) {
command.resolve(count);
return true;
}
return false;
}
function fillUnsubCommand(command, count) {
if (typeof command.remainReplies === 'undefined') {
command.remainReplies = command.args.length;
}
if (command.remainReplies === 0) {
if (count === 0) {
command.resolve(reply[2]);
return true;
}
return false;
}
if (--command.remainReplies === 0) {
command.resolve(reply[2]);
return true;
}
return false;
}
};