├── .gitignore ├── .travis.yml ├── README.md ├── lib ├── encoder.js ├── index.js └── redisd.js ├── package.json ├── simple_server.js └── test ├── encoder_test.js └── redisd_test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.6 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Node Redis protocol 2 | =================== 3 | 4 | Implement your own server using the redis protocol. 5 | 6 | Build a fake redis or a redis slave. 7 | 8 | Install 9 | ------- 10 | 11 | npm install 12 | 13 | Test 14 | ---- 15 | 16 | npm test 17 | 18 | ## Use cases ## 19 | 20 | ### Fake redis server ### 21 | 22 | ```javascript 23 | var redisd = require('redis-protocol'); 24 | var server = redisd.createServer(function(command) { 25 | console.log('query', command); 26 | this.encode(['pim', 'pam']);// the answer 27 | }); 28 | server.listen(6379, function() { 29 | console.log('fake redis started'); 30 | }); 31 | ``` 32 | This code is available at project root level. 33 | 34 | You can talk to it with any redis tools. Try *redis-cli*. 35 | 36 | Continous integration 37 | --------------------- 38 | 39 | [![Build Status](https://secure.travis-ci.org/athoune/node-redis-protocol.png)](http://travis-ci.org/athoune/node-redis-protocol) 40 | 41 | Licence 42 | ------- 43 | 44 | MIT 45 | -------------------------------------------------------------------------------- /lib/encoder.js: -------------------------------------------------------------------------------- 1 | 2 | var Response = function(writer) { 3 | this.writer = writer; 4 | }; 5 | 6 | Response.prototype.bulk = function(value) { 7 | var b = new Buffer(value.toString()); 8 | this.writer.write('$' + b.length + '\r\n'); 9 | this.writer.write(b); 10 | this.writer.write('\r\n'); 11 | }; 12 | 13 | // Automatic encoding. Binary safe. 14 | Response.prototype.encode = function(value) { 15 | var that = this; 16 | if(Array.isArray(value)) { 17 | this.writer.write('*' + value.length + '\r\n'); 18 | value.forEach(function(v) { that.bulk(v); }); 19 | } else { 20 | switch(typeof value) { 21 | case 'number': 22 | this.writer.write(':' + value + '\r\n'); 23 | break; 24 | case 'boolean': 25 | this.writer.write(':' + (value ? '1':'0') + '\r\n'); 26 | break; 27 | default: 28 | this.bulk(value); 29 | break; 30 | } 31 | } 32 | }; 33 | 34 | // An error. 35 | Response.prototype.error = function(msg) { 36 | this.writer.write('-' + msg + '\r\n'); 37 | }; 38 | 39 | // A simple response. 40 | Response.prototype.singleline = function(line) { 41 | this.writer.write('+' + line + '\r\n'); 42 | }; 43 | 44 | exports.Response = Response; 45 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | exports.createServer = require('./redisd').createServer; 2 | -------------------------------------------------------------------------------- /lib/redisd.js: -------------------------------------------------------------------------------- 1 | var net = require('net'), 2 | path = require('path'), 3 | Response = require('./encoder').Response; 4 | 5 | require('redis'); 6 | var root = path.dirname(require.resolve('redis')); 7 | var Parser; 8 | // this two libraries are not public, so direct path is used. 9 | // hiredis might not be installed 10 | try { 11 | Parser = require(root + '/lib/parser/hiredis').Parser; 12 | } catch (err) { 13 | if (exports.debug_mode) { 14 | console.warn('hiredis parser not installed.'); 15 | } 16 | Parser = require(root + '/lib/parser/javascript').Parser; 17 | } 18 | 19 | var createServer = function(onCommand) { 20 | var server = net.createServer({}, function(connection) { 21 | var parser = new Parser({debug_mode:false}); 22 | var response = new Response(connection); 23 | parser.on('error', function(err) { 24 | console.log(err); 25 | }); 26 | // Parser was build for clients, not servers. 27 | parser.on('reply', function(reply) { 28 | onCommand.call(response, reply); 29 | }); 30 | connection.on('data', function(data) { 31 | parser.execute(data); 32 | }); 33 | }); 34 | return server; 35 | }; 36 | 37 | exports.createServer = createServer; 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "redis-protocol", 3 | "version" : "0.1.10", 4 | "description" : "implement your server with the redis protocol", 5 | "author" : "Mathieu Lecarme ", 6 | "dependencies": { 7 | "redis": ">=0.8" 8 | }, 9 | "devDependencies": { 10 | "nodeunit": ">=0.6" 11 | }, 12 | "main" : "lib/index.js", 13 | "keywords" : ["redis"], 14 | "repositories": [{ 15 | "type": "git", 16 | "path": "git://github.com/athoune/node-redis-protocol.git" 17 | }], 18 | "homepage" : "http://github.com/athoune/node-redis-protocol", 19 | "bugs" : "http://github.com/athoune/node-redis-protocol/issues", 20 | "licenses" : [{ "type": "MIT"}], 21 | "engine" : "node", 22 | "scripts" : { 23 | "test": "./node_modules/nodeunit/bin/nodeunit test/*" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /simple_server.js: -------------------------------------------------------------------------------- 1 | var redisd = require('./lib/redisd'); 2 | 3 | var server = redisd.createServer(function(command) { 4 | this.encode(['pim', 'pam']); 5 | console.log('query', command); 6 | }); 7 | 8 | server.listen(6379, function() { 9 | console.log('fake redis started'); 10 | }); 11 | -------------------------------------------------------------------------------- /test/encoder_test.js: -------------------------------------------------------------------------------- 1 | var Response = require('../lib/encoder').Response; 2 | 3 | var MockWriter = function() { 4 | this.reset(); 5 | }; 6 | MockWriter.prototype.reset = function() { 7 | this.data = ''; 8 | }; 9 | MockWriter.prototype.write = function(something) { 10 | this.data += something; 11 | }; 12 | 13 | module.exports = { 14 | setUp: function(next) { 15 | this.writer = new MockWriter(); 16 | this.response = new Response(this.writer); 17 | next(); 18 | }, 19 | 'test boolean': function(test) { 20 | this.response.encode(true); 21 | test.equals(':1\r\n', this.writer.data); 22 | this.writer.reset(); 23 | this.response.encode(false); 24 | test.equals(':0\r\n', this.writer.data); 25 | test.done(); 26 | }, 27 | 'test integer': function(test) { 28 | this.response.encode(42); 29 | test.equals(':42\r\n', this.writer.data); 30 | test.done(); 31 | }, 32 | 'test string': function(test) { 33 | this.response.encode("pépé"); 34 | test.equals('$6\r\npépé\r\n', this.writer.data); 35 | test.done(); 36 | }, 37 | 'test array': function(test) { 38 | this.response.encode(["a", "b"]); 39 | test.equals('*2\r\n$1\r\na\r\n$1\r\nb\r\n', this.writer.data); 40 | test.done(); 41 | }, 42 | 'test simple': function(test) { 43 | this.response.singleline('OK'); 44 | test.equals('+OK\r\n', this.writer.data); 45 | test.done(); 46 | }, 47 | 'test error': function(test) { 48 | this.response.error('OUPS'); 49 | test.equals('-OUPS\r\n', this.writer.data); 50 | test.done(); 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /test/redisd_test.js: -------------------------------------------------------------------------------- 1 | var redisd = require('../lib/redisd'), 2 | redis = require('redis'); 3 | 4 | var PORT = 4242; 5 | 6 | module.exports = { 7 | setUp: function(next) { 8 | var that = this; 9 | this.server = redisd.createServer(function(command) { 10 | if(command[0] === 'info') { 11 | this.encode('redis_version:2.4.5'); 12 | } else { 13 | this.singleline('OK'); 14 | } 15 | that.command = command; 16 | }); 17 | this.client = redis.createClient(PORT); 18 | this.server.listen(PORT, function() { 19 | next(); 20 | }); 21 | }, 22 | tearDown: function(next) { 23 | this.client.end(); 24 | this.server.close(); 25 | next(); 26 | }, 27 | 'test simple': function(test) { 28 | var that = this; 29 | this.client.send_command("PLOP", [42], function (err, replies) { 30 | test.deepEqual([ 'PLOP', '42' ], that.command); 31 | test.done(); 32 | }); 33 | } 34 | }; 35 | --------------------------------------------------------------------------------