commit 30700581e647fb804df556f6c5ed1491f4776e0d Author: brettlangdon Date: Fri Oct 18 19:57:24 2013 -0700 initial commit, version 0.1.0 diff --git a/README.md b/README.md new file mode 100644 index 0000000..9d63d60 --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +cmdsrv +====== +[![NPM version](https://badge.fury.io/js/cmdsrv.png)](http://badge.fury.io/js/cmdsrv) + +`cmdsrv` is a simple text protocol server that allows you to easily define the protocol and it's handlers. + +The goal of `cmdsrv` is to be a very simplistic way to create your own text based protocol (like the memcached protocol). + +## Example Protocol +``` +GET +SET +DEL +STATS +``` + +## Installation +### Via Git +```bash +git clone git://github.com/brettlangdon/cmdsrv.git +cd ./cmdsrv +npm install +``` +### Via NPM +```bash +npm install cmdsrv +``` + +## Usage +```javascript +var cmdsrv = require("cmdsrv"); + +var data = {}; + +var server = new cmdsrv(); + +server.on("get", function(connection, key){ + // `connection` is just a normal `net.Socket` connection + var result = data[key] or ""; + connection.write("RESULT " + result + "\r\n"); +}); + +server.on("set", function(connection, key, value){ + data[key] = value; + connection.write("SAVED\r\n"); +}); + +server.start(); +``` + +## API + +### cmdsrv([options]) +This is the constructor for `cmdsrv` and should be invoked as `new cmdsrv()`. + +#### options +* `port` - which port to bind to when calling `start` (default: `3223`) +* `delimiter` - which character to split each command on (default: `" "`) +* `caseSensitive` - whether or not the commands should be case sensitive (default: `false`) + +### cmdsrv.on(command, handler) +Received commands are emitted to any handlers listening for that command. + +#### command +`command` is the command you want to be able to handle with `handler`. +For example, `"get"` would mean that `handler` gets called for each call to the `"get"` command. + +If `caseSensitive == true` then `"GET" !== "get"` + +#### handler +`handler` is a function which gets called every time the `command` is seen. + +The definition for `handler` will be `(connection, [arg1, arg2, ...])`, where `connection` is +the client connection (useful for sending responses back with `connection.write(data)`) and the args +are the arguments sent with the command. For example if the client sent the command `"get hello"` then +the function definition for `handler` might look like `function(connection, key)` where `connection` is the +`net.Socket` of the user who sent the command and `key` is `"hello"`. + +### cmdsrv.start(callback) +Start the server. + +#### callback +An options callback that gets called when the server is listening. + +## Default Events +* `error` - this is simply the client connection error event being bubbled up to the server. + +## License +``` +The MIT License (MIT) + +Copyright (c) 2013 Brett Langdon (http://www.brett.is) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` diff --git a/example.js b/example.js new file mode 100644 index 0000000..52b8d63 --- /dev/null +++ b/example.js @@ -0,0 +1,17 @@ +var cmdsrv = require("./"); + +var data = {}; + +var server = new cmdsrv(); + +server.on("get", function(connection, key){ + var result = data[key] || ""; + connection.write("RESULT " + result + "\r\n"); +}); + +server.on("set", function(connection, key, value){ + data[key] = value; + connection.write("SAVED\r\n"); +}); + +server.start(); diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..eeab8e3 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,83 @@ +var EventEmitter = require("events").EventEmitter; +var net = require("net"); +var util = require("util"); + +var emitLines = function(buffer, emitter){ + var pos = buffer.indexOf("\n"); + while(~pos){ + emitter.emit("line", buffer.substring(0, pos)); + buffer = buffer.substring(pos + 1); + pos = buffer.indexOf("\n"); + } + return buffer; +}; + +var cmdsrv = function(options){ + EventEmitter.call(this); + + options = options || {}; + + this.port = options["port"] || 3223; + if(!this.port || typeof(this.port) !== "number"){ + throw new Error("cmdsrv: `port` paramter must be a 'number'"); + } + + this.caseSensitive = options["caseSensitive"] || false; + this.delimiter = options["delimiter"] || " "; + + var self = this; + this.server = net.createServer(function(connection){ + self.handle(connection); + }); +}; +util.inherits(cmdsrv, EventEmitter); + +cmdsrv.prototype.handle = function(connection){ + var self = this; + var buffer = ""; + + connection.on("line", function(line){ + var parts = line.trim().split(self.delimiter); + + if(!parts.length){ + return; + } + + if(!self.caseSensitive){ + parts[0] = parts[0].toLowerCase(); + } + + var cmd = parts.shift(); + parts.unshift(connection); + parts.unshift(cmd); + + self.emit.apply(self, parts); + }); + + connection.on("data", function(data){ + buffer += data; + buffer = emitLines(buffer, connection); + }); + + connection.on("end", function(){ + buffer = emitLines(buffer, connection); + if(buffer){ + connection.emit("line", buffer); + } + }); + + connection.on("error", function(err){ + self.emit("error", err); + }); +}; + +cmdsrv.prototype.start = function(callback){ + var self = this; + callback = callback || function(){ + console.log("cmdsrv listening on port " + self.port); + } + this.server.listen(this.port, callback); +}; + + +return module.exports = cmdsrv; diff --git a/package.json b/package.json new file mode 100644 index 0000000..7b517b1 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "cmdsrv", + "version": "0.1.0", + "description": "simple text protocol command server", + "main": "lib/index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git://github.com/brettlangdon/cmdsrv.git" + }, + "keywords": [ + "text", + "protocol", + "command", + "server" + ], + "author": "Brett Langdon (brett@blangdon.com)", + "license": "MIT", + "bugs": { + "url": "https://github.com/brettlangdon/cmdsrv/issues" + } +}