konversi
v1.0.0
Published
WebSocket chat server.
Downloads
6
Readme
konversi
WebSocket chat server.
NOTE: Client side module is still only in planning phase and is not available yet.
Why
Because no one should be spending any more time building scalable chat service.
Example
Server:
& npm install -g konversi
& konversi -c /path/to/config/file.json
> ==== konversi server ======================
> version: x.x.x
> environment: dev
> time: Thu, 01 Jan 1970 00:00:00 GMT
> Listening on: 0.0.0.0:80/chat
> ===========================================
That's it, you are done. Now just use a socket.io client to connect and send messages.
Client:
var socket_1 = io(path, { path: '/chat' });
// Perform handshake first
socket_1.emit('rooms:handshake', Date.now(), null, 'userID=ClientName');
// Only this socket will receive handshake confirmation or error
socket_1.on('rooms:handshake', function () {
socket_1.emit('rooms:join', Date.now(), 'my-room-id', 'my-name');
});
socket_1.on('rooms:join', function (id, from, name) {
// Make sure this join event is for this socket
if (from === socket._1.id) {
socket.emit(
'rooms:message', // event ID
Date.now(), // arbitrary message id number
'my-room-id', // destination
'greetings!' // message content
);
return;
}
// otherwise maybe make note about other sockets connecting
// or do whatever else with this information
otherSocketsInRoom.push([id, name]);
});
Usage
When you install and run Konversi $ konversi
, it will look for the
./konversi
folder in the current directory which may contain config
folder.
Note: if you specify custom environment, corresponding config file, must exist.
Available application flags are:
-c | cfg
- path to custom configuration json file.
Several config files can be provided this was which would be mixed into one in the order they appear in application arguments. This is useful when using one or more configuration files to provide default values.
See config
section for more details.
Documentation
If you do need to customize Konversi functionality, do read alon. Create a Github issue, ask a question, make a pull request all is welcome.
Extending functionality of a base Konversi module is done using plugins. All that is, is just a folder structure that konversi plugin loader expects to find 3 types of implementation files in.
- ./konversi/config/ -
environmentName.json
files defining environment vars. - ./konversi/plugins/ - your plugin definitions.
- ./konversi/routers/ - underlying is a restify server which you can use to define http routes on top of web sockets.
Module index
ChatServer
Application http server and socket.io manager.Namespace
Wraps socket.io namespaces and manages its attached APIs (SocketRouters).SocketRouter
Defines a collection of socket.io events and response logic functions.SocketHandler
Wrapper for socket.io connection object.config
Stores and retrieves application config valuesconstants
Stores application constantsmodules
RootNamespaceRouter
An empty api router to be filled with custom events.ChatRoomRouter
Defines socket.io rooms interaction api.PmRouter
Defines a simple personal messages api.
middleware
Provides mechanism to validate and parse user input.- validationRules
- queryParser
utils
Control flow helper- stack
run()
Main application entry point.
ChatServer
Singleton main application server. This manages attached namespaces and underlying http server.
Module index exposes only ChatServer factory and a reference to the restify
library.
{
<function(): ChatServer>: instance
<Restify>: restify
}
ChatServer#namespace(Namespace: name)
Attach new namespace definition to this server.
- throws - namespace already registered.
ChatServer#getNamespace(string: name)
Finds and return a namespace
ChatServer#listen()
Builds server namespaces and its routers and starts listening for incoming connections.
ChatServer#close()
Closes server and socket connections.
ChatServer#getSocketsCount()
Returns number of connected sockets
ChatServer#toString()
Returns server header string.
Namespace
Defines socket.io namespace and manages event routers attached to it.
Namespace(string:id)
Creates new instance of namespace.
Namespace#id
String id
Namespace#url
Url address for this namespace
Namespace#namespace
Socket.io namespace object.
Namespace#use(SocketRouter|function: handler)
Register SocketRouter
or if handler is a function a
middleware function.
The difference is that middleware function executes before all routers in
this namespace.
Namespace#has(string: routerName)
Returns true if this namespace has a router with provided name.
Namespace#on(string: name, function: handler)
Register socket event on the namespace scope.
Namespace#getSocket(string: socketId, boolean isConnected)
Returns socket by the provided id, if isConnected
is false
then this
functions will also search sockets in recent connections pool.
SocketRouter
Defines and serves as a group for a set of custom socket.io events.
SocketRouter(options)
Options are:
- : name - Must provide router name
- : set_events_prefix - Default=true. Prepends socket name to all of this router's event definitions.
- : max_listeners - nodejs EventEmitter value.
SocketRouter#name
String name
SocketRouter#initialiseRouter()
Abstract function, use this to define router content.
SocketRouter#reset()
Deletes and resets all previously defined events for this router.
SocketRouter#use(function: middleware)
Attaches a middleware function to this router.
SocketRouter event functions
All event function signatures are as follows:
- function[]|function: tasks - Event callback stack
- number: priority - Sort order of this stack when calling event register multiple times.
function(socketHandler, eventId, input, next)
- response stack function
SocketRouter#beforeAll(function[]: tasks, boolean: priority): SocketRouter
SocketRouter#afterAll(function[]: tasks, boolean: priority): SocketRouter
SocketRouter#response(function[]: tasks, boolean: priority): SocketRouter
SocketRouter#connection(function[]: tasks, boolean: priority): SocketRouter
SocketRouter#disconnect(function[]: tasks, boolean: priority): SocketRouter
SocketRouter#validation(function[]: tasks, boolean: priority): SocketRouter
SocketRouter#error(function[]: tasks, boolean: priority): SocketRouter
SocketRouter#on(function[]: tasks, boolean: priority): SocketRouter
Register event
SocketRouter#expect(function[]: tasks, boolean: priority): SocketRouter
Register event which has to be called before any other.
SocketRouter#bounceMessage([string: asEvent][, boolean: withBody]) : function
Creates an even stack function which will echo the message back to the
originating socket connection. Specifying withBody=true
will result
in message body to be included in the echo message.
SocketRouter#proxyMessage([string: asEvent]) : function
Creates an event stack function which will propagate received message to the addressed room by the event's address argument.
SocketRouter#bounceError([string: asEvent]) : function
Will create a function which echoes any errors back to the originating client.
SocketHandler
Socket handler serves as a proxy to socket
object and provides custom
functionality within Conversi framework.
SocketHandler#id
Same as socket.id
.
Id is used as an address
property of all messages from a given socket.
SocketHandler#address
This value overrides default id
property for message address references.
Default is null
.
SocketHandler#socket
Reference to underlying socket object.
SocketHandler#isRoomMember(string: roomID)
Returns true
if this socket joned provided room.
SocketHandler#getStore(SocketRouter: router)
Returns a persistent hash object associated with a given router. This object lives as long as this connection stays open.
SocketHandler#on(string: eventID, function: callback)
This is a decorator function used to, well decorate event transmitted data into a common to Konversi framework form.
Callback function definition is: function(socketRouter, eventId, input)
* socketRouter - This socket handler object
* eventID - String id of the event
* input - Emitted event content
* id - Arbitrary id number (as string)
SocketHandler#to
SocketHandler#in
SocketHandler#emit
These three functions are here for convenience and are equivalent to those found on socket.io socket object.
config
Serves as a simple value dictionary with an added safety mechanism which will throw an error if a non existing parameter is tried.
This is using a ConfigManager utility, which can be found here.
It short, users can provide .json files filled with actual values or use "{{ENV_VAR_NAME}}"
pattern to access those values from node.env
during application runtime.
config#get(string: argumentAddress);
Returns existing config member value.
- argumentAddress - full stop delimited string value pointing to a member inside a config object hash.
Default config object.
{
pluginDirectory: './conversi', // where konversi should expect to find extentions
// Options expected by restify server
server: {
port: 80,
options: {} // same as restify.js server config
},
// Options expected by socket.io
io: {
path: '/chat',
serveClient: false
},
// Options expected by redis adapter
redisAdapter: {
enable: false, // by default it is turned off
host: 'localhost',
port: 6379
},
// Configures verious aspecs of Konversi module
behavior: {
userInput: {
maxAddressIdLength: 50,
maxMessageLength: 5000
}
}
}
Constants
A collection of predefined values used by this module.
{
DEFAULT_NAMESPACE_ID,
DEFAULT_NAMESPACE_NAME,
EVENT_CONNECTION,
EVENT_DISCONNECT,
EVENT_ERROR,
EVENT_HANDSHAKE,
EVENT_JOIN,
EVENT_LEAVE,
EVENT_MESSAGE,
PARAM_RECONNECTION
}
Modules
These are implementations of vanilla Konversi functionality. Custom modules should be authored in a similar fashion as these core modules.
When you start basic Konversi application these api's is what you interact with.
Synopsis
The following APIs are described using the folllowing notation:
Left side is event trigger and right side is a response.
<request> --> <response>
Left side is event trigger and right side is a response.-->
This is a messageproxy
, i.e. message propagation to addressed clients.<->
This is a messagebounce
, i.e. message being echoed back to the origination client. It's primary use is confirmation mechanism.
Request/response arguments
Konversi expects all event trigger requests to be in the following form:
- string: id - Arbitrary numeric value.
- string: address - Destination id value either socket id or room name.
- string: body - Message body
RootNamespaceRouter
This does not do anything and serves the only purpose of being provided to plugins to manipulate and extend with custom events.
ChatRoomRouter
The meat of the Konversi application. This modules provides chat room routing api.
<connection>
- use queryParam[reconnection]=Y/N to specify if the
connection is a re-connection. This will avoid broadcasting duplicate
'rooms:join' events.
<handshake>
: setup socket connection--> <rooms:join>
, <-> <rooms:join>
- when default rooms is set<-> <rooms:handshake>
<rooms:message>
: send message to a room by ID--> <rooms:message>
- broadcast event to all sockets in a room<-> <rooms:error>
- If addressing to a room before joining it.
<rooms:join>
:--> <rooms:join>
<-> <rooms:join>
<rooms:leave>
:--> <rooms:leave>
<-> <rooms:leave>
PmRouter
Routes personal messages.
<pm:message>
: send a direct personal message
--> <pm:message>
Middleware
validationRules - Collection of validation rules ready to be used
- default - {number:id, string:addr, sting:body}
- noAddress - {null:addr}
- bodyParser - validates 'body' string as a query string
queryParser - maps query params to
SocketHandler.queryParams
- create - creates query params middleware function
- parser - query params implementations
- number - maps a query parameter as a number
- translate - transforms query parameter to a specific value
e.g :
var router = new Router('chat'); var queryParser = queryParser.create({ [paramName]: queryParser.parser.translate({ "Y": true, "N": false //,"undefined": true // makes this parameter optional }); router.use(queryParser); router.connection(function (socketRouter, next) { // http://localhost/socket.io?paramName=Y socketRouter.queryParams // {paramName: true} });
Utils
stack
Flow control utility helper functions, wrapping response stack functions
into async callback functions.
- series(function|function[] : stack, function:callback)
- conditional(function : condition, function|function[] : trueStack, function|function[] : falseStack)
Run function
This is the entry point of the module and is what invoked when Konversi is started as a global module.
If you rather use this module as an internal dependency make sure you call this function yourself.
License
MIT. Copyright (c) 2015 Dmitry Matveev See LICENSE