Skip to content

Commit 5f261c5

Browse files
author
Ruben Bridgewater
committed
Minor changes
Move utility functions in lib/utils.js Improve the js parser in cases the buffer is incomplete Rename lib/parser to lib/parsers Fix smaller issues with test suite and fix parser errors not being catched Fixed wrong test for the new .end flush parameter Fixed test suite options being partly mutated Add some more tests
1 parent ff47dc3 commit 5f261c5

File tree

13 files changed

+212
-102
lines changed

13 files changed

+212
-102
lines changed

index.js

Lines changed: 15 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
var net = require("net"),
44
URL = require("url"),
55
util = require("util"),
6+
utils = require("./lib/utils"),
67
Queue = require("./lib/queue"),
7-
to_array = require("./lib/to_array"),
88
events = require("events"),
99
parsers = [],
1010
// This static list of commands is updated from time to time.
@@ -23,14 +23,13 @@ exports.debug_mode = /\bredis\b/i.test(process.env.NODE_DEBUG);
2323

2424
// hiredis might not be installed
2525
try {
26-
require("./lib/parser/hiredis");
27-
parsers.push(require("./lib/parser/hiredis"));
26+
parsers.push(require("./lib/parsers/hiredis"));
2827
} catch (err) {
2928
/* istanbul ignore next: won't be reached with tests */
3029
debug("Hiredis parser not installed.");
3130
}
3231

33-
parsers.push(require("./lib/parser/javascript"));
32+
parsers.push(require("./lib/parsers/javascript"));
3433

3534
function RedisClient(stream, options) {
3635
options = options || {};
@@ -135,13 +134,15 @@ RedisClient.prototype.flush_and_error = function (error) {
135134

136135
while (command_obj = this.offline_queue.shift()) {
137136
if (typeof command_obj.callback === "function") {
137+
error.command = command_obj.command.toUpperCase();
138138
command_obj.callback(error);
139139
}
140140
}
141141
this.offline_queue = new Queue();
142142

143143
while (command_obj = this.command_queue.shift()) {
144144
if (typeof command_obj.callback === "function") {
145+
error.command = command_obj.command.toUpperCase();
145146
command_obj.callback(error);
146147
}
147148
}
@@ -530,41 +531,6 @@ RedisClient.prototype.return_error = function (err) {
530531
}
531532
};
532533

533-
// hgetall converts its replies to an Object. If the reply is empty, null is returned.
534-
function reply_to_object(reply) {
535-
var obj = {}, j, jl, key, val;
536-
537-
if (reply.length === 0 || !Array.isArray(reply)) {
538-
return null;
539-
}
540-
541-
for (j = 0, jl = reply.length; j < jl; j += 2) {
542-
key = reply[j].toString('binary');
543-
val = reply[j + 1];
544-
obj[key] = val;
545-
}
546-
547-
return obj;
548-
}
549-
550-
function reply_to_strings(reply) {
551-
var i;
552-
553-
if (Buffer.isBuffer(reply)) {
554-
return reply.toString();
555-
}
556-
557-
if (Array.isArray(reply)) {
558-
for (i = 0; i < reply.length; i++) {
559-
// Recusivly call the function as slowlog returns deep nested replies
560-
reply[i] = reply_to_strings(reply[i]);
561-
}
562-
return reply;
563-
}
564-
565-
return reply;
566-
}
567-
568534
RedisClient.prototype.return_reply = function (reply) {
569535
var command_obj, len, type, timestamp, argindex, args, queue_len;
570536

@@ -598,12 +564,12 @@ RedisClient.prototype.return_reply = function (reply) {
598564
if (this.options.detect_buffers && command_obj.buffer_args === false) {
599565
// If detect_buffers option was specified, then the reply from the parser will be Buffers.
600566
// If this command did not use Buffer arguments, then convert the reply to Strings here.
601-
reply = reply_to_strings(reply);
567+
reply = utils.reply_to_strings(reply);
602568
}
603569

604570
// TODO - confusing and error-prone that hgetall is special cased in two places
605571
if (reply && 'hgetall' === command_obj.command) {
606-
reply = reply_to_object(reply);
572+
reply = utils.reply_to_object(reply);
607573
}
608574
}
609575

@@ -614,7 +580,7 @@ RedisClient.prototype.return_reply = function (reply) {
614580
} else if (this.pub_sub_mode || command_obj && command_obj.sub_command) {
615581
if (Array.isArray(reply)) {
616582
if (!this.options.return_buffers && (!command_obj || this.options.detect_buffers && command_obj.buffer_args === false)) {
617-
reply = reply_to_strings(reply);
583+
reply = utils.reply_to_strings(reply);
618584
}
619585
type = reply[0].toString();
620586

@@ -850,7 +816,7 @@ RedisClient.prototype.end = function (flush) {
850816

851817
// Flush queue if wanted
852818
if (flush) {
853-
this.flush_and_error("Redis connection ended.");
819+
this.flush_and_error(new Error("The command can't be processed. The connection has already been closed."));
854820
}
855821

856822
this.connected = false;
@@ -894,7 +860,7 @@ commands.forEach(function (fullCommand) {
894860
arg = [key].concat(arg);
895861
return this.send_command(command, arg, callback);
896862
}
897-
return this.send_command(command, to_array(arguments));
863+
return this.send_command(command, utils.to_array(arguments));
898864
};
899865
RedisClient.prototype[command.toUpperCase()] = RedisClient.prototype[command];
900866

@@ -910,7 +876,7 @@ commands.forEach(function (fullCommand) {
910876
}
911877
this.queue.push([command, key].concat(arg));
912878
} else {
913-
this.queue.push([command].concat(to_array(arguments)));
879+
this.queue.push([command].concat(utils.to_array(arguments)));
914880
}
915881
return this;
916882
};
@@ -979,7 +945,7 @@ RedisClient.prototype.hmset = RedisClient.prototype.HMSET = function (key, args,
979945
}
980946
return this.send_command("hmset", tmp_args, callback);
981947
}
982-
return this.send_command("hmset", to_array(arguments));
948+
return this.send_command("hmset", utils.to_array(arguments));
983949
};
984950

985951
Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) {
@@ -1008,7 +974,7 @@ Multi.prototype.hmset = Multi.prototype.HMSET = function (key, args, callback) {
1008974
tmp_args.push(callback);
1009975
}
1010976
} else {
1011-
tmp_args = to_array(arguments);
977+
tmp_args = utils.to_array(arguments);
1012978
tmp_args.unshift("hmset");
1013979
}
1014980
this.queue.push(tmp_args);
@@ -1087,11 +1053,11 @@ Multi.prototype.execute_callback = function (err, replies) {
10871053
replies[i].command = args[0].toUpperCase();
10881054
} else if (replies[i]) {
10891055
if (this._client.options.detect_buffers && this.wants_buffers[i + 1] === false) {
1090-
replies[i] = reply_to_strings(replies[i]);
1056+
replies[i] = utils.reply_to_strings(replies[i]);
10911057
}
10921058
if (args[0] === "hgetall") {
10931059
// TODO - confusing and error-prone that hgetall is special cased in two places
1094-
replies[i] = reply_to_object(replies[i]);
1060+
replies[i] = utils.reply_to_object(replies[i]);
10951061
}
10961062
}
10971063

lib/parser/hiredis.js renamed to lib/parsers/hiredis.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ HiredisReplyParser.prototype.execute = function (data) {
1818
var reply;
1919
this.reader.feed(data);
2020
while (true) {
21-
reply = this.reader.get();
21+
try {
22+
reply = this.reader.get();
23+
} catch (err) {
24+
// Protocol errors land here
25+
this.send_error(err);
26+
break;
27+
}
2228

2329
if (reply === undefined) {
2430
break;

lib/parser/javascript.js renamed to lib/parsers/javascript.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,13 @@ ReplyParser.prototype._parseResult = function (type) {
3030
end = this._packetEndOffset() - 1;
3131
start = this._offset;
3232

33-
// include the delimiter
34-
this._offset = end + 2;
35-
3633
if (end > this._buffer.length) {
37-
this._offset = start;
3834
throw new IncompleteReadBuffer("Wait for more data.");
3935
}
4036

37+
// include the delimiter
38+
this._offset = end + 2;
39+
4140
if (type === 45) {
4241
return new Error(this._buffer.toString(this._encoding, start, end));
4342
} else if (this.return_buffers) {
@@ -49,14 +48,13 @@ ReplyParser.prototype._parseResult = function (type) {
4948
end = this._packetEndOffset() - 1;
5049
start = this._offset;
5150

52-
// include the delimiter
53-
this._offset = end + 2;
54-
5551
if (end > this._buffer.length) {
56-
this._offset = start;
5752
throw new IncompleteReadBuffer("Wait for more data.");
5853
}
5954

55+
// include the delimiter
56+
this._offset = end + 2;
57+
6058
// return the coerced numeric value
6159
return +this._buffer.toString('ascii', start, end);
6260
} else if (type === 36) { // $
@@ -74,14 +72,13 @@ ReplyParser.prototype._parseResult = function (type) {
7472
end = this._offset + packetHeader.size;
7573
start = this._offset;
7674

77-
// set the offset to after the delimiter
78-
this._offset = end + 2;
79-
8075
if (end > this._buffer.length) {
81-
this._offset = offset;
8276
throw new IncompleteReadBuffer("Wait for more data.");
8377
}
8478

79+
// set the offset to after the delimiter
80+
this._offset = end + 2;
81+
8582
if (this.return_buffers) {
8683
return this._buffer.slice(start, end);
8784
}
@@ -157,6 +154,11 @@ ReplyParser.prototype.execute = function (buffer) {
157154
ret = this._parseResult(type);
158155

159156
this.send_reply(ret);
157+
} else if (type === 10 || type === 13) {
158+
break;
159+
} else {
160+
var err = new Error('Protocol error, got "' + String.fromCharCode(type) + '" as reply type byte');
161+
this.send_error(err);
160162
}
161163
} catch (err) {
162164
// catch the error (not enough data), rewind, and wait

lib/to_array.js

Lines changed: 0 additions & 14 deletions
This file was deleted.

lib/utils.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
3+
// hgetall converts its replies to an Object. If the reply is empty, null is returned.
4+
function replyToObject(reply) {
5+
var obj = {}, j, jl, key, val;
6+
7+
if (reply.length === 0 || !Array.isArray(reply)) {
8+
return null;
9+
}
10+
11+
for (j = 0, jl = reply.length; j < jl; j += 2) {
12+
key = reply[j].toString('binary');
13+
val = reply[j + 1];
14+
obj[key] = val;
15+
}
16+
17+
return obj;
18+
}
19+
20+
function replyToStrings(reply) {
21+
var i;
22+
23+
if (Buffer.isBuffer(reply)) {
24+
return reply.toString();
25+
}
26+
27+
if (Array.isArray(reply)) {
28+
for (i = 0; i < reply.length; i++) {
29+
// Recusivly call the function as slowlog returns deep nested replies
30+
reply[i] = replyToStrings(reply[i]);
31+
}
32+
return reply;
33+
}
34+
35+
return reply;
36+
}
37+
38+
function toArray(args) {
39+
var len = args.length,
40+
arr = new Array(len), i;
41+
42+
for (i = 0; i < len; i += 1) {
43+
arr[i] = args[i];
44+
}
45+
46+
return arr;
47+
}
48+
49+
module.exports = {
50+
reply_to_strings: replyToStrings,
51+
reply_to_object: replyToObject,
52+
to_array: toArray
53+
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"devDependencies": {
2121
"coveralls": "^2.11.2",
2222
"jshint": "^2.8.0",
23-
"metrics": ">=0.1.5",
23+
"metrics": "^0.1.9",
2424
"mocha": "^2.3.2",
2525
"nyc": "^3.2.2",
2626
"optional-dev-dependency": "^1.1.0",

test/commands/geoadd.spec.js.future

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
'use strict';
2+
3+
var config = require("../lib/config");
4+
var helper = require("../helper");
5+
var redis = config.redis;
6+
7+
describe("The 'getoadd' method", function () {
8+
9+
helper.allTests(function(parser, ip, args) {
10+
11+
describe("using " + parser + " and " + ip, function () {
12+
var client;
13+
14+
beforeEach(function (done) {
15+
client = redis.createClient.apply(redis.createClient, args);
16+
client.once("connect", function () {
17+
client.flushdb(done);
18+
});
19+
});
20+
21+
it('returns 1 if the key exists', function (done) {
22+
client.geoadd("mycity:21:0:location", "13.361389","38.115556","COR", function(err, res) {
23+
console.log(err, res);
24+
// geoadd is still in the unstable branch. As soon as it reaches the stable one, activate this test
25+
done();
26+
});
27+
});
28+
29+
afterEach(function () {
30+
client.end();
31+
});
32+
});
33+
});
34+
});

test/commands/set.spec.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ describe("The 'set' method", function () {
8888
});
8989
}, 100);
9090
});
91+
92+
it("sets the value correctly with the array syntax", function (done) {
93+
client.set([key, value]);
94+
setTimeout(function () {
95+
client.get(key, function (err, res) {
96+
helper.isString(value)(err, res);
97+
done();
98+
});
99+
}, 100);
100+
});
91101
});
92102

93103
describe("with undefined 'key' and missing 'value' parameter", function () {

0 commit comments

Comments
 (0)