Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Moved redis to lib/redis

  • Loading branch information...
commit 1b4d5da617f7baf94c68367e0917040963fc63a3 1 parent 317ae54
TJ Holowaychuk authored July 27, 2010
1  index.js
2  index.js
... ...
@@ -0,0 +1,2 @@
  1
+
  2
+module.exports = require('./lib/connect-redis');
2  lib/connect-redis.js
@@ -11,7 +11,7 @@
11 11
 
12 12
 var sys = require('sys'),
13 13
     Store = require('connect/middleware/session/store'),
14  
-    redis = require('./support/redis/lib/redis-client');
  14
+    redis = require('./redis/lib/redis-client');
15 15
 
16 16
 /**
17 17
  * Initialize RedisStore with the given `options`.
1  lib/redis
... ...
@@ -0,0 +1 @@
  1
+Subproject commit 30e9f41e7f62137ba47a0f5929ddd8a1e40a6553
1  support/redis/.gitignore
... ...
@@ -1 +0,0 @@
1  
-*.swp
19  support/redis/LICENSE
... ...
@@ -1,19 +0,0 @@
1  
-© 2010 by Fictorial LLC
2  
-
3  
-Permission is hereby granted, free of charge, to any person obtaining a copy
4  
-of this software and associated documentation files (the "Software"), to deal
5  
-in the Software without restriction, including without limitation the rights
6  
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7  
-copies of the Software, and to permit persons to whom the Software is
8  
-furnished to do so, subject to the following conditions:
9  
-
10  
-The above copyright notice and this permission notice shall be included in
11  
-all copies or substantial portions of the Software.
12  
-
13  
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19  
-THE SOFTWARE.
103  support/redis/README.md
Source Rendered
... ...
@@ -1,103 +0,0 @@
1  
-# Redis client for Node.js
2  
-
3  
-## In a nutshell
4  
-
5  
-- Talk to Redis from Node.js 
6  
-- Fully asynchronous; your code is called back when an operation completes
7  
-- [Binary-safe](http://github.com/fictorial/redis-node-client/blob/master/test/test.js#L353-363); uses Node.js Buffer objects for request serialization and reply parsing
8  
-    - e.g. store a PNG in Redis if you'd like
9  
-- Client API directly follows Redis' [command specification](http://code.google.com/p/redis/wiki/CommandReference) 
10  
-- *You have to understand how Redis works and the semantics of its command set to most effectively use this client*
11  
-- Supports Redis' new exciting [PUBSUB](http://code.google.com/p/redis/wiki/PublishSubscribe) commands
12  
-- Automatically reconnects to Redis (doesn't drop commands sent while waiting to reconnect either) using [exponential backoff](http://en.wikipedia.org/wiki/Exponential_backoff)
13  
-    - Be sure to see [this script](http://github.com/fictorial/redis-node-client/blob/master/test/test_shutdown_reconnect.js) for a deeper discussion
14  
-
15  
-## Synopsis
16  
-
17  
-When working from a git clone:
18  
-
19  
-    var sys = require("sys");
20  
-    var client = require("../lib/redis-client").createClient();
21  
-    client.info(function (err, info) {
22  
-        if (err) throw new Error(err);
23  
-        sys.puts("Redis Version is: " + info.redis_version);
24  
-        client.close();
25  
-    });
26  
-
27  
-When working with a Kiwi-based installation:
28  
-
29  
-    // $ kiwi install redis-client
30  
-
31  
-    var sys = require("sys"), 
32  
-        kiwi = require("kiwi"),
33  
-        client = kiwi.require("redis-client").createClient();
34  
-
35  
-    client.info(function (err, info) {
36  
-        if (err) throw new Error(err);
37  
-        sys.puts("Redis Version is: " + info.redis_version);
38  
-        client.close();
39  
-    });
40  
-
41  
-- Refer to the many tests in `test/test.js` for many usage examples.
42  
-- Refer to the `examples/` directory for focused examples.
43  
-
44  
-## Installation
45  
-
46  
-This version requires at least `Node.js v0.1.90` and Redis `1.3.8`.
47  
-
48  
-Tested with Node.js `v0.1.95` and `v0.1.96` and Redis `2.1.1` (the current unstable).
49  
-
50  
-You have a number of choices:
51  
-
52  
-- git clone this repo or download a tarball and simply copy `lib/redis-client.js` into your project
53  
-- use git submodule
54  
-- use the [Kiwi](http://github.com/visionmedia/kiwi) package manager for Node.js
55  
-
56  
-Please let me know if the package manager "seeds" and/or metadata have issues.
57  
-Installation via Kiwi or NPM at this point isn't really possible since this repo
58  
-depends on a unreleased version of Node.js.
59  
-
60  
-## Running the tests
61  
-
62  
-A good way to learn about this client is to read the test code.
63  
-
64  
-To run the tests, install and run redis on the localhost on port 6379 (defaults).
65  
-Then run `node test/test.js [-v|-q]` where `-v` is for "verbose" and `-q` is for "quiet".
66  
-
67  
-    $ node test/test.js
68  
-    ..................................................................
69  
-    ...........................++++++++++++++++++++++++++++++++++++
70  
-
71  
-    [INFO] All tests have passed.
72  
-
73  
-If you see something like "PSUBSCRIBE: unknown command" then it is time to upgrade
74  
-your Redis installation.
75  
-
76  
-## Documentation
77  
-
78  
-There is a method per Redis command.  E.g. `SETNX` becomes `client.setnx`.
79  
-
80  
-For example, the Redis command [INCRBY](http://code.google.com/p/redis/wiki/IncrCommand)
81  
-is specified as `INCRBY key integer`.  Also, the INCRBY spec says that the reply will
82  
-be "... the new value of key after the increment or decrement."
83  
-
84  
-This translates to the following client code which increments key 'foo' by 42.  If
85  
-the value at key 'foo' was 0 or non-existent, 'newValue' will take value 42 when
86  
-the callback function is called.
87  
-
88  
-    client.incrby('foo', 42, function (err, newValue) {
89  
-        // ...
90  
-    });
91  
-
92  
-This can get [a little wacky](http://github.com/fictorial/redis-node-client/blob/master/test/test.js#L1093-1097). 
93  
-I'm open to suggestions for improvement here.
94  
-
95  
-Note: for PUBSUB, you should use `subscribeTo` and `unsubscribeFrom` instead of the generated
96  
-methods for Redis' `SUBSCRIBE` and `UNSUBSCRIBE` commands.  See [this](http://github.com/fictorial/redis-node-client/blob/master/lib/redis-client.js#L682-694)
97  
-and [this](http://github.com/fictorial/redis-node-client/blob/master/examples/subscriber.js#L14).
98  
-
99  
-## Notes
100  
-
101  
-All commands/requests use the Redis *multi-bulk request* format which will be
102  
-the only accepted request protocol come Redis 2.0.
103  
-
23  support/redis/TODO.md
Source Rendered
... ...
@@ -1,23 +0,0 @@
1  
-## Soon
2  
-
3  
-- Support MULTI/EXEC/DISCARD. This is a little tricky given that its
4  
-  referred to as a "transaction" (but it's really a macro).  The API
5  
-  must clearly define what the hell is going on in error cases, etc.
6  
-  Also, we'll need to add support for nested multi-bulk replies. The
7  
-  reply parser, while it handles non-bulk replies inside a multi-bulk
8  
-  reply, does not handle multi-bulk replies inside multi-bulk replies.
9  
-  This is required for MULTI/EXEC.
10  
-
11  
-- WATCH support
12  
-
13  
-- Now that Node.js has UDP support and Redis 2.0 will have UDP support,
14  
-  I suppose we should add, you know, UDP support here.
15  
-
16  
-## Later
17  
-
18  
-- Provide wrapper around the pretty-raw sort method?
19  
-
20  
-## Maybe
21  
-
22  
-- Add support for consistent hashing ala redis-rb and txRedisAPI
23  
-- Add a higher-level interface similar to Ohm (Ruby)
32  support/redis/examples/README.md
Source Rendered
... ...
@@ -1,32 +0,0 @@
1  
-## Some examples.
2  
-
3  
-Note: a large number of usage examples can be found in `../test/test.js`.
4  
-
5  
-## Publisher-Subscriber (PUBSUB)
6  
-
7  
-In one terminal:
8  
-
9  
-    $ ./publisher.js
10  
-    Published message to no one.
11  
-    Published message to no one.
12  
-    Published message to no one.
13  
-    Published message to 1 subscriber(s).        <-- Started the subscriber.
14  
-    Published message to 1 subscriber(s).
15  
-    Published message to 1 subscriber(s).
16  
-    Published message to 1 subscriber(s).
17  
-    Published message to no one.                 <-- Killed (^C) the subscriber.
18  
-    ^C
19  
-
20  
-In another terminal:
21  
-
22  
-    $ ./subscriber.js 
23  
-    waiting for messages...
24  
-    [channel-6702921148389578]: The time is Fri Apr 02 2010 16:52:19 GMT-0400 (EDT)
25  
-    [channel-9212789069861174]: The time is Fri Apr 02 2010 16:52:20 GMT-0400 (EDT)
26  
-    [channel-30327219143509865]: The time is Fri Apr 02 2010 16:52:21 GMT-0400 (EDT)
27  
-    [channel-35810230672359467]: The time is Fri Apr 02 2010 16:52:22 GMT-0400 (EDT)
28  
-    [channel-5208229701966047]: The time is Fri Apr 02 2010 16:52:23 GMT-0400 (EDT)
29  
-    [channel-26559297926723957]: The time is Fri Apr 02 2010 16:52:24 GMT-0400 (EDT)
30  
-    [channel-9280104916542768]: The time is Fri Apr 02 2010 16:52:25 GMT-0400 (EDT)
31  
-    ^C
32  
-
21  support/redis/examples/publisher.js
... ...
@@ -1,21 +0,0 @@
1  
-#!/usr/bin/env node
2  
-
3  
-// This script plays the role of publisher.
4  
-
5  
-var 
6  
-  sys = require("sys"),
7  
-  client = require("../lib/redis-client").createClient();
8  
-
9  
-// Publish a message once a second to a random channel.
10  
-
11  
-setInterval(function () {
12  
-  var 
13  
-    channelName = "channel-" + Math.random().toString().substr(2),
14  
-    payload = "The time is " + (new Date());
15  
-
16  
-  client.publish(channelName, payload, 
17  
-    function (err, reply) {
18  
-      sys.puts("Published message to " + 
19  
-        (reply === 0 ? "no one" : (reply + " subscriber(s).")));
20  
-    });
21  
-}, 1000); 
7  support/redis/examples/redis-version.js
... ...
@@ -1,7 +0,0 @@
1  
-var sys = require("sys");
2  
-var client = require("../lib/redis-client").createClient();
3  
-client.info(function (err, info) {
4  
-    if (err) throw new Error(err);
5  
-    sys.puts("Redis Version is: " + info.redis_version);
6  
-    client.close();
7  
-});
19  support/redis/examples/subscriber.js
... ...
@@ -1,19 +0,0 @@
1  
-#!/usr/bin/env node
2  
-
3  
-// This script plays the role of listener/subscriber/consumer
4  
-// to **all** channels/classes.
5  
-
6  
-var 
7  
-  sys = require("sys"),
8  
-  client = require("../lib/redis-client").createClient();
9  
-
10  
-sys.puts("waiting for messages...");
11  
-
12  
-client.subscribeTo("*", 
13  
-  function (channel, message, subscriptionPattern) {
14  
-    var output = "[" + channel;
15  
-    if (subscriptionPattern)
16  
-      output += " (from pattern '" + subscriptionPattern + "')";
17  
-    output += "]: " + message;
18  
-    sys.puts(output);
19  
-  });
16  support/redis/examples/using-kiwi.js
... ...
@@ -1,16 +0,0 @@
1  
-// Kiwi is a package manager for Node.js
2  
-// http://wiki.github.com/visionmedia/kiwi/getting-started
3  
-//
4  
-// $ kiwi install redis-client
5  
-
6  
-var sys = require("sys"), 
7  
-    kiwi = require("kiwi"),
8  
-    client = kiwi.require("redis-client").createClient();
9  
-
10  
-client.stream.addListener("connect", function () {
11  
-    client.info(function (err, info) {
12  
-        if (err) throw new Error(err);
13  
-        sys.puts("Redis Version is: " + info.redis_version);
14  
-        client.close();
15  
-    });
16  
-});
917  support/redis/lib/redis-client.js
... ...
@@ -1,917 +0,0 @@
1  
-/*
2  
-
3  
-© 2010 by Fictorial LLC
4  
-
5  
-Permission is hereby granted, free of charge, to any person obtaining a copy
6  
-of this software and associated documentation files (the "Software"), to deal
7  
-in the Software without restriction, including without limitation the rights
8  
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  
-copies of the Software, and to permit persons to whom the Software is
10  
-furnished to do so, subject to the following conditions:
11  
-
12  
-The above copyright notice and this permission notice shall be included in
13  
-all copies or substantial portions of the Software.
14  
-
15  
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21  
-THE SOFTWARE.
22  
-
23  
-*/
24  
-
25  
-// To add support for new commands, edit the array called "commands" at the
26  
-// bottom of this file.
27  
-
28  
-// Set this to true to aid in debugging wire protocol input/output,
29  
-// parsing methods, etc.
30  
-
31  
-exports.debugMode = false;
32  
-
33  
-var net = require("net"),
34  
-    sys = require("sys"),
35  
-    Buffer = require('buffer').Buffer,
36  
-    events = require('events'),
37  
-
38  
-    CRLF = "\r\n",
39  
-    CRLF_LEN = 2,
40  
-
41  
-    PLUS      = exports.PLUS      = 0x2B, // +
42  
-    MINUS     = exports.MINUS     = 0x2D, // -
43  
-    DOLLAR    = exports.DOLLAR    = 0x24, // $
44  
-    STAR      = exports.STAR      = 0x2A, // *
45  
-    COLON     = exports.COLON     = 0x3A, // :
46  
-    CR        = exports.CR        = 0x0D, // \r
47  
-    LF        = exports.LF        = 0x0A, // \n
48  
-                                
49  
-    NONE      = exports.NONE      = "NONE",
50  
-    BULK      = exports.BULK      = "BULK",     
51  
-    MULTIBULK = exports.MULTIBULK = "MULTIBULK",
52  
-    INLINE    = exports.INLINE    = "INLINE",   
53  
-    INTEGER   = exports.INTEGER   = "INTEGER",  
54  
-    ERROR     = exports.ERROR     = "ERROR";    
55  
-
56  
-exports.DEFAULT_HOST = '127.0.0.1';
57  
-exports.DEFAULT_PORT = 6379;
58  
-
59  
-exports.COMMAND_ORPHANED_ERROR = "connection lost before reply received";
60  
-exports.NO_CONNECTION_ERROR = "failed to establish a connection to Redis";
61  
-
62  
-function debugFilter(buffer, len) {
63  
-    // Redis is binary-safe but assume for debug display that 
64  
-    // the encoding of textual data is UTF-8.
65  
-
66  
-    var filtered = buffer.utf8Slice(0, len || buffer.length);
67  
-
68  
-    filtered = filtered.replace(/\r\n/g, '<CRLF>');
69  
-    filtered = filtered.replace(/\r/g, '<CR>');
70  
-    filtered = filtered.replace(/\n/g, '<LF>');
71  
-
72  
-    return filtered;
73  
-}
74  
-
75  
-// A fully interruptable, binary-safe Redis reply parser.
76  
-// 'callback' is called with each reply parsed in 'feed'.
77  
-// 'thisArg' is the "thisArg" for the callback "call".
78  
-
79  
-function ReplyParser(callback, thisArg) {
80  
-    this.onReply = callback;
81  
-    this.thisArg = thisArg;
82  
-    this.clearState();
83  
-    this.clearMultiBulkState();
84  
-}
85  
-
86  
-exports.ReplyParser = ReplyParser;
87  
-
88  
-ReplyParser.prototype.clearState = function () {
89  
-    this.type = NONE;
90  
-    this.bulkLengthExpected = null;
91  
-    this.valueBufferLen = 0;
92  
-    this.skip = 0;
93  
-    this.valueBuffer = new Buffer(4096);
94  
-};
95  
-
96  
-ReplyParser.prototype.clearMultiBulkState = function () {
97  
-    this.multibulkReplies = null; 
98  
-    this.multibulkRepliesExpected = null;
99  
-};
100  
-
101  
-ReplyParser.prototype.feed = function (inbound) {
102  
-    for (var i=0; i < inbound.length; ++i) {
103  
-        if (this.skip > 0) {
104  
-            this.skip--;
105  
-            continue;
106  
-        }
107  
-
108  
-        var typeBefore = this.type;
109  
-
110  
-        if (this.type === NONE) {
111  
-            switch (inbound[i]) {
112  
-                case DOLLAR: this.type = BULK;      break;
113  
-                case STAR:   this.type = MULTIBULK; break;
114  
-                case COLON:  this.type = INTEGER;   break;
115  
-                case PLUS:   this.type = INLINE;    break;
116  
-                case MINUS:  this.type = ERROR;     break;
117  
-            }
118  
-        }
119  
-
120  
-        // Just a state transition on '*', '+', etc.?  
121  
-
122  
-        if (typeBefore != this.type)
123  
-            continue;
124  
-
125  
-        // If the reply is a part of a multi-bulk reply.  Save it.  If we have
126  
-        // received all the expected replies of a multi-bulk reply, then
127  
-        // callback.  If the reply is not part of a multi-bulk. Call back
128  
-        // immediately.
129  
-
130  
-        var self = this;
131  
-
132  
-        var maybeCallbackWithReply = function (reply) {
133  
-            if (self.multibulkReplies != null) {
134  
-                self.multibulkReplies.push(reply);
135  
-                if (--self.multibulkRepliesExpected == 0) {
136  
-                    self.onReply.call(self.thisArg, { 
137  
-                        type:  MULTIBULK, 
138  
-                        value: self.multibulkReplies 
139  
-                    });
140  
-                    self.clearMultiBulkState();
141  
-                }
142  
-            } else {
143  
-                self.onReply.call(self.thisArg, reply);
144  
-            }
145  
-            self.clearState();
146  
-            self.skip = 1; // Skip LF
147  
-        };
148  
-
149  
-        switch (inbound[i]) {
150  
-        case CR:
151  
-            switch (this.type) {
152  
-                case INLINE:
153  
-                case ERROR:
154  
-                    // CR denotes end of the inline/error value.  
155  
-                    // +OK\r\n
156  
-                    //    ^
157  
-
158  
-                    var inlineBuf = new Buffer(this.valueBufferLen);
159  
-                    this.valueBuffer.copy(inlineBuf, 0, 0, this.valueBufferLen);
160  
-                    maybeCallbackWithReply({ type:this.type, value:inlineBuf });
161  
-                    break;
162  
-
163  
-                case INTEGER:
164  
-                    // CR denotes the end of the integer value.  
165  
-                    // :42\r\n
166  
-                    //    ^
167  
-
168  
-                    var n = parseInt(this.valueBuffer.asciiSlice(0, this.valueBufferLen), 10);
169  
-                    maybeCallbackWithReply({ type:INTEGER, value:n });
170  
-                    break;
171  
-
172  
-                case BULK:
173  
-                    if (this.bulkLengthExpected == null) {
174  
-                        // CR denotes end of first line of a bulk reply,
175  
-                        // which is the length of the bulk reply value.
176  
-                        // $5\r\nhello\r\n
177  
-                        //   ^
178  
-
179  
-                        var bulkLengthExpected = 
180  
-                            parseInt(this.valueBuffer.asciiSlice(0, this.valueBufferLen), 10);
181  
-
182  
-                        if (bulkLengthExpected <= 0) {
183  
-                            maybeCallbackWithReply({ type:BULK, value:null });
184  
-                        } else {
185  
-                            this.clearState();
186  
-
187  
-                            this.bulkLengthExpected = bulkLengthExpected;
188  
-                            this.type = BULK;
189  
-                            this.skip = 1;  // skip LF
190  
-                        }
191  
-                    } else if (this.valueBufferLen == this.bulkLengthExpected) {
192  
-                        // CR denotes end of the bulk reply value.
193  
-                        // $5\r\nhello\r\n
194  
-                        //            ^
195  
-
196  
-                        var bulkBuf = new Buffer(this.valueBufferLen);
197  
-                        this.valueBuffer.copy(bulkBuf, 0, 0, this.valueBufferLen);
198  
-                        maybeCallbackWithReply({ type:BULK, value:bulkBuf });
199  
-                    } else {
200  
-                        // CR is just an embedded CR and has nothing to do
201  
-                        // with the reply specification.
202  
-                        // $11\r\nhello\rworld\r\n
203  
-                        //             ^
204  
-                        
205  
-                        this.valueBuffer[this.valueBufferLen++] = inbound[i];
206  
-                    }
207  
-                    break;
208  
-
209  
-                case MULTIBULK:
210  
-                    // Parse the count which is the number of expected replies
211  
-                    // in the multi-bulk reply.
212  
-                    // *2\r\n$5\r\nhello\r\n$5\r\nworld\r\n
213  
-                    //   ^
214  
-
215  
-                    var multibulkRepliesExpected = 
216  
-                        parseInt(this.valueBuffer.asciiSlice(0, this.valueBufferLen), 10);
217  
-
218  
-                    if (multibulkRepliesExpected <= 0) {
219  
-                        maybeCallbackWithReply({ type:MULTIBULK, value:null });
220  
-                    } else {
221  
-                        this.clearState();
222  
-                        this.skip = 1;    // skip LF
223  
-                        this.multibulkReplies = [];
224  
-                        this.multibulkRepliesExpected = multibulkRepliesExpected;
225  
-                    }
226  
-                    break;
227  
-            }
228  
-            break;
229  
-
230  
-        default:
231  
-            this.valueBuffer[this.valueBufferLen++] = inbound[i];
232  
-            break;
233  
-        }
234  
-
235  
-        // If the current value buffer is too big, create a new buffer, copy in
236  
-        // the old buffer, and replace the old buffer with the new buffer.
237  
- 
238  
-        if (this.valueBufferLen === this.valueBuffer.length) {
239  
-            var newBuffer = new Buffer(this.valueBuffer.length * 2);
240  
-            this.valueBuffer.copy(newBuffer, 0, 0);
241  
-            this.valueBuffer = newBuffer;
242  
-        }
243  
-    }
244  
-};
245  
-
246  
-/**
247  
- * Emits:
248  
- *
249  
- * - 'connected' when connected (or on a reconnection, reconnected).
250  
- * - 'reconnecting' when about to retry to connect to Redis.
251  
- * - 'reconnected' when connected after a reconnection was established.
252  
- * - 'noconnection' when a connection (or reconnection) cannot be established.
253  
- * - 'drained' when no submitted commands are expecting a reply from Redis.
254  
- *
255  
- * Options: 
256  
- *
257  
- * - maxReconnectionAttempts (default: 10)
258  
- */
259  
-
260  
-function Client(stream, options) {
261  
-    events.EventEmitter.call(this);
262  
-
263  
-    this.stream = stream;
264  
-    this.originalCommands = [];
265  
-    this.queuedOriginalCommands = [];
266  
-    this.queuedRequestBuffers = [];
267  
-    this.channelCallbacks = {};
268  
-    this.requestBuffer = new Buffer(512);
269  
-    this.replyParser = new ReplyParser(this.onReply_, this);
270  
-    this.reconnectionTimer = null;
271  
-    this.maxReconnectionAttempts = 10;
272  
-    this.reconnectionAttempts = 0;
273  
-    this.reconnectionDelay = 500;    // doubles, so starts at 1s delay
274  
-    this.connectionsMade = 0;
275  
-
276  
-    if (options !== undefined) 
277  
-        this.maxReconnectionAttempts = Math.abs(options.maxReconnectionAttempts || 10);
278  
-
279  
-    var client = this;
280  
-
281  
-    stream.addListener("connect", function () {
282  
-        if (exports.debugMode)
283  
-            sys.debug("[CONNECT]");
284  
-
285  
-        stream.setNoDelay();
286  
-        stream.setTimeout(0);
287  
-
288  
-        client.reconnectionAttempts = 0;
289  
-        client.reconnectionDelay = 500;
290  
-        if (client.reconnectionTimer) {
291  
-            clearTimeout(client.reconnectionTimer);
292  
-            client.reconnectionTimer = null;
293  
-        }
294  
-
295  
-        var eventName = client.connectionsMade == 0 
296  
-                      ? 'connected' 
297  
-                      : 'reconnected';
298  
-
299  
-        client.connectionsMade++;
300  
-        client.expectingClose = false;
301  
-
302  
-        // If this a reconnection and there were commands submitted, then they
303  
-        // are gone!  We cannot say with any confidence which were processed by
304  
-        // Redis; perhaps some were processed but we never got the reply, or
305  
-        // perhaps all were processed but Redis is configured with less than
306  
-        // 100% durable writes, etc.  
307  
-        //
308  
-        // We punt to the user by calling their callback with an I/O error.
309  
-        // However, we provide enough information to allow the user to retry
310  
-        // the interrupted operation.  We are certainly not retrying anything
311  
-        // for them as it is too dangerous and application-specific.
312  
-
313  
-        if (client.connectionsMade > 1 && client.originalCommands.length > 0) {
314  
-            if (exports.debug) {
315  
-                sys.debug("[RECONNECTION] some commands orphaned (" + 
316  
-                    client.originalCommands.length + "). notifying...");
317  
-            }
318  
-
319  
-            client.callbackOrphanedCommandsWithError();
320  
-        }
321  
-        
322  
-        client.originalCommands = [];
323  
-        client.flushQueuedCommands();
324  
-
325  
-        client.emit(eventName, client);
326  
-    });
327  
-
328  
-    stream.addListener('error', function (e) {
329  
-        if (exports.debugMode)
330  
-            sys.debug("[ERROR] Connection to redis encountered an error: " + e);
331  
-    });
332  
-
333  
-    stream.addListener("data", function (buffer) {
334  
-        if (exports.debugMode)
335  
-            sys.debug("[RECV] " + debugFilter(buffer));
336  
-
337  
-        client.replyParser.feed(buffer);
338  
-    });
339  
-
340  
-    stream.addListener("error", function (e) {
341  
-	if (exports.debugMode)
342  
-	  sys.debug('[ERROR] ' + e);
343  
-	client.replyParser.clearState();
344  
-	client.maybeReconnect();
345  
-	throw e;
346  
-    });
347  
-
348  
-    stream.addListener("end", function () {
349  
-        if (exports.debugMode && client.originalCommands.length > 0) {
350  
-            sys.debug("Connection to redis closed with " + 
351  
-                      client.originalCommands.length + 
352  
-                      " commands pending replies that will never arrive!");
353  
-        }
354  
-
355  
-        stream.end();
356  
-    });
357  
-
358  
-    stream.addListener("close", function (hadError) {
359  
-        if (exports.debugMode)
360  
-            sys.debug("[NO CONNECTION]");
361  
-
362  
-        client.maybeReconnect();
363  
-    });
364  
-}
365  
-
366  
-sys.inherits(Client, events.EventEmitter);
367  
-
368  
-exports.Client = Client;
369  
-
370  
-exports.createClient = function (port, host, options) {
371  
-    var port = port || exports.DEFAULT_PORT;
372  
-    var host = host || exports.DEFAULT_HOST;
373  
-
374  
-    var client = new Client(net.createConnection(port, host), options);
375  
-
376  
-    client.port = port;
377  
-    client.host = host;
378  
-
379  
-    return client;
380  
-};
381  
-
382  
-Client.prototype.close = function () {
383  
-    this.expectingClose = true;
384  
-    this.stream.end();
385  
-};
386  
-
387  
-Client.prototype.onReply_ = function (reply) {
388  
-    this.flushQueuedCommands();
389  
-
390  
-    if (this.handlePublishedMessage_(reply)) 
391  
-        return;
392  
-
393  
-    var originalCommand = this.originalCommands.shift();
394  
-    var callback = originalCommand[originalCommand.length - 1];
395  
-
396  
-    // Callbacks expect (err, reply) as args.
397  
-
398  
-    if (typeof callback == "function") {
399  
-        if (reply.type == ERROR) {
400  
-            callback(reply.value.utf8Slice(0, reply.value.length), null);
401  
-        } else {
402  
-            callback(null, maybeConvertReplyValue(originalCommand[0], reply));
403  
-        }
404  
-    }
405  
-
406  
-    if (this.originalCommands.length == 0)
407  
-      this.emit('drained', this);
408  
-};
409  
-
410  
-Client.prototype.handlePublishedMessage_ = function (reply) {
411  
-    // We're looking for a multibulk resembling 
412  
-    // ["message", "channelName", messageBuffer]; or
413  
-    // ["pmessage", "matchingPattern", "channelName", messageBuffer]
414  
-    // The latter is sent when the client subscribed to a channel by a pattern;
415  
-    // the former when subscribed to a channel by name.
416  
-    // If the client subscribes by name -and- by pattern and there's some
417  
-    // overlap, the client -will- receive multiple p/message notifications.
418  
-
419  
-    if (reply.type != MULTIBULK || !(reply.value instanceof Array))
420  
-        return false;
421  
-
422  
-    var isMessage = (reply.value.length == 3 &&
423  
-                     reply.value[0].value.length == 7 &&
424  
-                     reply.value[0].value.asciiSlice(0, 7) == 'message');
425  
-
426  
-    var isPMessage = (reply.value.length == 4 &&
427  
-                      reply.value[0].value.length == 8 &&
428  
-                      reply.value[0].value.asciiSlice(0, 8) == 'pmessage');
429  
-
430  
-    if (!isMessage && !isPMessage)
431  
-        return false;
432  
-
433  
-    // This is tricky. We are returning true even though there 
434  
-    // might not be any callback called! This may happen when a
435  
-    // caller subscribes then unsubscribes while a published
436  
-    // message is in transit to us. When the message arrives, no
437  
-    // one is there to consume it. In essence, as long as the 
438  
-    // reply type is a published message (see above), then we've
439  
-    // "handled" the reply.
440  
-        
441  
-    if (Object.getOwnPropertyNames(this.channelCallbacks).length == 0) 
442  
-        return true;
443  
-
444  
-    var channelName, channelPattern, channelCallback, payload;
445  
-
446  
-    if (isMessage) {
447  
-        channelName = reply.value[1].value;
448  
-        channelCallback = this.channelCallbacks[channelName];
449  
-        payload = reply.value[2].value;
450  
-    } else if (isPMessage) {
451  
-        channelPattern = reply.value[1].value;
452  
-        channelName = reply.value[2].value;
453  
-        channelCallback = this.channelCallbacks[channelPattern];
454  
-        payload = reply.value[3].value;
455  
-    } else {
456  
-        return false;
457  
-    }
458  
-
459  
-    if (typeof channelCallback == "function") {
460  
-        channelCallback(channelName, payload, channelPattern);
461  
-        return true;
462  
-    }
463  
-
464  
-    return false;
465  
-}
466  
-
467  
-function maybeAsNumber(str) {
468  
-    var value = parseInt(str, 10);
469  
-
470  
-    if (isNaN(value)) 
471  
-        value = parseFloat(str);
472  
-
473  
-    if (isNaN(value)) 
474  
-        return str;
475  
-
476  
-    return value;
477  
-}
478  
-
479  
-function maybeConvertReplyValue(commandName, reply) {
480  
-    if (reply.value === null)
481  
-        return null;
482  
-
483  
-    // Redis' INFO command returns a BULK reply of the form:
484  
-    // "redis_version:1.3.8
485  
-    // arch_bits:64
486  
-    // multiplexing_api:kqueue
487  
-    // process_id:11604
488  
-    // ..."
489  
-    // 
490  
-    // We convert that to a JS object like:
491  
-    // { redis_version: '1.3.8'
492  
-    // , arch_bits: '64'
493  
-    // , multiplexing_api: 'kqueue'
494  
-    // , process_id: '11604'
495  
-    // , ... }
496  
-
497  
-    if (commandName === 'info' && reply.type === BULK) {
498  
-        var info = {};
499  
-        reply.value.asciiSlice(0, reply.value.length).split(/\r\n/g)
500  
-            .forEach(function (line) {
501  
-                var parts = line.split(':');
502  
-                if (parts.length === 2)
503  
-                    info[parts[0]] = parts[1];
504  
-            });
505  
-        return info;
506  
-    }
507  
-
508  
-    // HGETALL returns a MULTIBULK where each consecutive reply-pair
509  
-    // is a key and value for the Redis HASH.  We convert this into
510  
-    // a JS object.
511  
-
512  
-    if (commandName === 'hgetall' && 
513  
-        reply.type === MULTIBULK &&
514  
-        reply.value.length % 2 === 0) {
515  
-
516  
-        var hash = {};
517  
-        for (var i=0; i<reply.value.length; i += 2) 
518  
-            hash[reply.value[i].value] = reply.value[i + 1].value;
519  
-        return hash;
520  
-    }
521  
-
522  
-    // Redis returns "+OK\r\n" to signify success.
523  
-    // We convert this into a JS boolean with value true.
524  
-    
525  
-    if (reply.type === INLINE && reply.value.asciiSlice(0,2) === 'OK')
526  
-        return true;
527  
-
528  
-    // ZSCORE returns a string representation of a floating point number.
529  
-    // We convert this into a JS number.
530  
-
531  
-    if (commandName === "zscore")
532  
-        return maybeAsNumber(reply.value);
533  
-
534  
-    // Multibulk replies are returned from our reply parser as an
535  
-    // array like: [ {type:BULK, value:"foo"}, {type:BULK, value:"bar"} ]
536  
-    // But, end-users want the value and don't care about the
537  
-    // Redis protocol reply types.  We here extract the value from each
538  
-    // object in the multi-bulk array.
539  
-
540  
-    if (reply.type === MULTIBULK)
541  
-        return reply.value.map(function (element) { return element.value; });
542  
-
543  
-    // Otherwise, we have no conversions to offer.
544  
-
545  
-    return reply.value;
546  
-}
547  
-
548  
-exports.maybeConvertReplyValue_ = maybeConvertReplyValue;
549  
-
550  
-var commands = [ 
551  
-    "append",
552  
-    "auth",
553  
-    "bgsave",
554  
-    "blpop",
555  
-    "brpop",
556  
-    "dbsize",
557  
-    "decr",
558  
-    "decrby",
559  
-    "del",
560  
-    "exists",
561  
-    "expire",
562  
-    "expireat",
563  
-    "flushall",
564  
-    "flushdb",
565  
-    "get",
566  
-    "getset",
567  
-    "hdel",
568  
-    "hexists",
569  
-    "hget",
570  
-    "hgetall",
571  
-    "hincrby",
572  
-    "hkeys",
573  
-    "hlen",
574  
-    "hmget",
575  
-    "hmset",
576  
-    "hset",
577  
-    "hvals",
578  
-    "incr",
579  
-    "incrby",
580  
-    "info",
581  
-    "keys",
582  
-    "lastsave",
583  
-    "len",
584  
-    "lindex",
585  
-    "llen",
586  
-    "lpop",
587  
-    "lpush",
588  
-    "lrange",
589  
-    "lrem",
590  
-    "lset",
591  
-    "ltrim",
592  
-    "mget",
593  
-    "move",
594  
-    "mset",
595  
-    "msetnx",
596  
-    "psubscribe",
597  
-    "publish",
598  
-    "punsubscribe",
599  
-    "randomkey",
600  
-    "rename",
601  
-    "renamenx",
602  
-    "rpop",
603  
-    "rpoplpush",
604  
-    "rpush",
605  
-    "sadd",
606  
-    "save",
607  
-    "scard",
608  
-    "sdiff",
609  
-    "sdiffstore",
610  
-    "select",
611  
-    "set",
612  
-    "setex",
613  
-    "setnx",
614  
-    "shutdown",
615  
-    "sinter",
616  
-    "sinterstore",
617  
-    "sismember",
618  
-    "smembers",
619  
-    "smove",
620  
-    "sort",
621  
-    "spop",
622  
-    "srandmember",
623  
-    "srem",
624  
-    "subscribe",
625  
-    "sunion",
626  
-    "sunionstore",
627  
-    "ttl",
628  
-    "type",
629  
-    "unsubscribe",
630  
-    "zadd",
631  
-    "zcard",
632  
-    "zcount",
633  
-    "zincrby",
634  
-    "zinter",
635  
-    "zrange",
636  
-    "zrangebyscore",
637  
-    "zrank",
638  
-    "zrem",
639  
-    "zrembyrank",
640  
-    "zremrangebyrank",
641  
-    "zremrangebyscore",
642  
-    "zrevrange",
643  
-    "zrevrank",
644  
-    "zscore",
645  
-    "zunion",
646  
-];
647  
-
648  
-// For internal use but maybe useful in rare cases or when the client command
649  
-// set is not 100% up to date with Redis' latest commands.
650  
-// client.sendCommand('GET', 'foo', function (err, value) {...});
651  
-//
652  
-// arguments[0]      = commandName
653  
-// arguments[1..N-2] = Redis command arguments
654  
-// arguments[N-1]    = callback function
655  
-
656  
-Client.prototype.sendCommand = function () {
657  
-    var originalCommand = Array.prototype.slice.call(arguments);
658  
-
659  
-    // If this client has given up trying to connect/reconnect to Redis,
660  
-    // just call the errback (if any). Regardless, don't enqueue the command.
661  
-
662  
-    if (this.noConnection) {
663  
-        if (arguments.length > 0 && typeof arguments[arguments.length - 1] == 'function')
664  
-            arguments[arguments.length - 1](this.makeErrorForCommand(originalCommand, exports.NO_CONNECTION_ERROR));
665  
-        return;
666  
-    }
667  
-
668  
-    this.flushQueuedCommands();
669  
-
670  
-    var commandName = arguments[0].toLowerCase();
671  
-
672  
-    // Invariant: number of queued callbacks == number of commands sent to
673  
-    // Redis whose replies have not yet been received and processed.  Thus,
674  
-    // if no callback was given, we create a dummy callback.
675  
-
676  
-    var argCount = arguments.length;
677  
-    if (typeof arguments[argCount - 1] == 'function')
678  
-        --argCount;
679  
-
680  
-    // All requests are formatted as multi-bulk.
681  
-    // The first line of a multi-bulk request is "*<number of parts to follow>\r\n".
682  
-    // Next is: "$<length of the command name>\r\n<command name>\r\n".
683  
-
684  
-    // Write the request as we go into a request Buffer.  Recall that buffers
685  
-    // are fixed length.  We thus guess at how much space is needed.  If we
686  
-    // need to grow beyond this, we create a new buffer, copy the old one, and
687  
-    // continue.  Once we're ready to write the buffer, we use a 0-copy slice
688  
-    // to send just that which we've written to the buffer.
689  
-    //
690  
-    // We reuse the buffer after each request. When the buffer "grows" to
691  
-    // accomodate a request, it stays that size until it needs to grown again,
692  
-    // which may of course be never.
693  
-
694  
-    var offset = this.requestBuffer.utf8Write('*' + argCount.toString() + CRLF +
695  
-                                              '$' + commandName.length + CRLF +
696  
-                                              commandName + CRLF, 0);
697  
-
698  
-    var self = this;
699  
-
700  
-    function ensureSpaceFor(atLeast) {
701  
-      var currentLength = self.requestBuffer.length;
702  
-
703  
-      if (offset + atLeast > currentLength) {
704  
-        // If we know how much space we need, use that + 10%.
705  
-        // Else double the size of the buffer.
706  
-
707  
-        var bufferLength = Math.max(currentLength * 2, atLeast * 1.1);
708  
-        var newBuffer = new Buffer(Math.round(bufferLength));
709  
-        self.requestBuffer.copy(newBuffer, 0, 0, offset); // target, targetStart, srcStart, srcEnd
710  
-        self.requestBuffer = newBuffer;
711  
-      }
712  
-    }
713  
-
714  
-    // Serialize the arguments into the request buffer
715  
-    // If the request is a Buffer, just copy.  Else if
716  
-    // the arg has a .toString() method, call it and write
717  
-    // it to the request buffer as UTF8.
718  
-
719  
-    var extrasLength = 5;   // '$', '\r\n', '\r\n'
720  
-
721  
-    for (var i=1; i < argCount; ++i) {
722  
-        var arg = arguments[i];
723  
-        if (arg instanceof Buffer) {
724  
-            ensureSpaceFor(arg.length + arg.length.toString().length + extrasLength);
725  
-            offset += this.requestBuffer.asciiWrite('$' + arg.length + CRLF, offset);
726  
-            offset += arg.copy(this.requestBuffer, offset, 0);  // target, targetStart, srcStart
727  
-            offset += this.requestBuffer.asciiWrite(CRLF, offset);
728  
-        } else if (arg.toString) {
729  
-            var asString = arg.toString();
730  
-            var serialized = '$' + Buffer.byteLength(asString, "binary") + CRLF + asString + CRLF;
731  
-            ensureSpaceFor(Buffer.byteLength(serialized, "binary"));
732  
-            offset += this.requestBuffer.binaryWrite(serialized, offset);
733  
-        }
734  
-    }
735  
-
736  
-    // If the stream is writable, write the command.  Else enqueue the command
737  
-    // for when we first establish a connection or reconnect.
738  
-
739  
-    if (this.stream.writable) {
740  
-        this.originalCommands.push(originalCommand);
741  
-        var outBuffer = new Buffer(offset);
742  
-        this.requestBuffer.copy(outBuffer, 0, 0, offset);
743  
-        this.stream.write(outBuffer, 'binary');
744  
-
745  
-        if (exports.debugMode) 
746  
-            sys.debug("[SEND] " + debugFilter(this.requestBuffer, offset) + 
747  
-                " originalCommands = " + this.originalCommands.length);
748  
-    } else {
749  
-        var toEnqueue = new Buffer(offset);
750  
-        this.requestBuffer.copy(toEnqueue, 0, 0, offset);  // dst, dstStart, srcStart, srcEnd
751  
-        this.queuedRequestBuffers.push(toEnqueue);
752  
-        this.queuedOriginalCommands.push(originalCommand);
753  
-
754  
-        if (exports.debugMode) {
755  
-            sys.debug("[ENQUEUE] Not connected. Request queued. There are " + 
756  
-                this.queuedRequestBuffers.length + " requests queued.");
757  
-        }
758  
-    }
759  
-};
760  
-
761  
-commands.forEach(function (commandName) {
762  
-    Client.prototype[commandName] = function () {
763  
-        var args = Array.prototype.slice.call(arguments);
764  
-        // [[1,2,3],function(){}] => [1,2,3,function(){}]
765  
-        if (args.length > 0 && Array.isArray(args[0])) 
766  
-          args = args.shift().concat(args);
767  
-        args.unshift(commandName);
768  
-        this.sendCommand.apply(this, args);
769  
-    };
770  
-});
771  
-
772  
-// Send any commands that were queued while we were not connected.
773  
-
774  
-Client.prototype.flushQueuedCommands = function () {
775  
-    if (exports.debugMode && this.queuedRequestBuffers.length > 0) 
776  
-        sys.debug("[FLUSH QUEUE] " + this.queuedRequestBuffers.length + 
777  
-                  " queued request buffers.");
778  
-
779  
-    for (var i=0; i<this.queuedRequestBuffers.length && this.stream.writable; ++i) {
780  
-        var buffer = this.queuedRequestBuffers.shift();
781  
-        this.stream.write(buffer, 'binary');
782  
-        this.originalCommands.push(this.queuedOriginalCommands.shift());
783  
-
784  
-        if (exports.debugMode) 
785  
-            sys.debug("[DEQUEUE/SEND] " + debugFilter(buffer) + 
786  
-                      ". queued buffers remaining = " + 
787  
-                      this.queuedRequestBuffers.length);
788  
-    }
789  
-};
790  
-
791  
-Client.prototype.makeErrorForCommand = function (command, errorMessage) {
792  
-    var err = new Error(errorMessage);
793  
-    err.originalCommand = command;
794  
-    return err;
795  
-};
796  
-
797  
-Client.prototype.callbackCommandWithError = function (command, errorMessage) {
798  
-    var callback = command[command.length - 1];
799  
-    if (typeof callback == "function") 
800  
-        callback(this.makeErrorForCommand(command, errorMessage));
801  
-};
802  
-
803  
-Client.prototype.callbackOrphanedCommandsWithError = function () {
804  
-    for (var i=0, n=this.originalCommands.length; i<n; ++i) 
805  
-        this.callbackCommandWithError(this.originalCommands[i], exports.COMMAND_ORPHANED_ERROR);
806  
-    this.originalCommands = [];
807  
-};
808  
-
809  
-Client.prototype.callbackQueuedCommandsWithError = function () {
810  
-    for (var i=0, n=this.queuedOriginalCommands.length; i<n; ++i) 
811  
-        this.callbackCommandWithError(this.queuedOriginalCommands[i], exports.NO_CONNECTION_ERROR);
812  
-    this.queuedOriginalCommands = [];
813  
-    this.queuedRequestBuffers = [];
814  
-};
815  
-
816  
-Client.prototype.giveupConnectionAttempts = function () {
817  
-    this.callbackOrphanedCommandsWithError();
818  
-    this.callbackQueuedCommandsWithError();
819  
-    this.noConnection = true;
820  
-    this.emit('noconnection', this);
821  
-};
822  
-
823  
-Client.prototype.maybeReconnect = function () {
824  
-    if (this.stream.writable && this.stream.readable)
825  
-        return;
826  
-
827  
-    if (this.expectingClose)
828  
-        return;
829  
-
830  
-    // Do not reconnect on first connection failure.
831  
-    // Else try to reconnect if we're asked to. 
832  
-
833  
-    if (this.connectionsMade == 0) {
834  
-        this.giveupConnectionAttempts();
835  
-    } else if (this.maxReconnectionAttempts > 0) {
836  
-        if (this.reconnectionAttempts++ >= this.maxReconnectionAttempts) {
837  
-            this.giveupConnectionAttempts();
838  
-        } else {
839  
-            this.reconnectionDelay *= 2;
840  
-
841  
-            if (exports.debugMode) {
842  
-                sys.debug("[RECONNECTING " + this.reconnectionAttempts + "/" + 
843  
-                    this.maxReconnectionAttempts + "]");
844  
-
845  
-                sys.debug("[WAIT " + this.reconnectionDelay + " ms]");