summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlassulus <lassulus@googlemail.com>2013-11-12 02:01:29 +0100
committerlassulus <lassulus@googlemail.com>2013-11-12 02:01:29 +0100
commit3531d46cc3644a64b990f904e40e4846b8242a07 (patch)
treea0289317e14a8011d7a6615010b70e5d21eff02c
parentc6a7cd9027150fca42500b3bbc78be01993856f6 (diff)
webchat: refactor to rpc
-rw-r--r--webchat/index.js129
-rw-r--r--webchat/proto_spec62
-rw-r--r--webchat/public/client.js75
-rw-r--r--webchat/public/commands.js11
-rw-r--r--webchat/public/functions.js20
-rw-r--r--webchat/public/handler.js35
-rw-r--r--webchat/public/rpc.js99
-rw-r--r--webchat/public/sockjs_client_transport.js27
-rw-r--r--webchat/serverCommands.js26
-rw-r--r--webchat/sockjs_server_connection_transport.js26
10 files changed, 327 insertions, 183 deletions
diff --git a/webchat/index.js b/webchat/index.js
index 52f41d1c..70b32d14 100644
--- a/webchat/index.js
+++ b/webchat/index.js
@@ -4,34 +4,15 @@ var http = require('https');
var sockjs = require('sockjs');
var connect = require('connect');
var irc = require('irc');
-var serverCommands = require('./serverCommands.js');
-
-var serverstate = {
- connected: false,
- nicks: [],
- lastping: 0,
-};
+var make_sockjs_server_connection_transport = require('./sockjs_server_connection_transport.js')
+var RPC = require('./public/rpc.js');
var clients = [];
-clients.notifyAll = function (method, params) {
- var object = {
- method: method,
- params: params,
- }
- clients.forEach(function (client) {
- client.conn.write(JSON.stringify(object));
- });
-}
-
-serverstate.clients = clients;
-
var irc_reconnect = function() { //reconnt to irc
- serverstate.connected = false
console.log("reconnecting due to pingtimeout")
irc_client.disconnect()
irc_client.connect()
- serverstate.connected = true
}
var pingTimeoutDelay = 3*60*1000
@@ -52,7 +33,6 @@ var irc_client = new irc.Client('irc.freenode.net', 'kweb', { //create irc_clien
autoConnect: true,
stripColors: true
});
-serverstate.irc_client = irc_client;
irc_client.on('ping', function(server) { //restart timer on server ping
console.log("got ping from server, renewing timeout for automatic reconnect");
@@ -62,25 +42,32 @@ irc_client.on('ping', function(server) { //restart timer on server ping
irc_client.on('message#krebs', function(from, message) {
console.log({ from: from, message: message });
- clients.notifyAll('message', { nick: from, msg: message });
+ clients.map(pluck('rpc')).forEach(function (rpc) {
+ rpc.send('msg', {nick: from, msg: message})
+ })
clearTimeout(lastping);
});
irc_client.on('names#krebs', function(nicks) {
- clients.notifyAll('nicklist', { nicklist: nicks })
- serverstate.connected = true
- serverstate.irc_nicklist = nicks
+ clients.map(pluck('rpc')).forEach(function (rpc) {
+ Object.keys(nicks).forEach(function (nick) {
+ rpc.send('join', {type: 'irc', nick: nick})
+ })
+ })
});
irc_client.on('join#krebs', function(nick, msg) {
if (nick !== 'kweb'){
- clients.notifyAll('join', { from: nick })
+ clients.map(pluck('rpc')).forEach(function (rpc) {
+ rpc.send('join', {type: 'irc', nick: nick})
+ })
}
-});
+})
irc_client.on('part#krebs', function(nick, rs, msg) {
- clients.notifyAll('quit', { from: nick })
-// clients.broadcast({method: 'quit', params: { from: nick }});
+ clients.map(pluck('rpc')).forEach(function (rpc) {
+ rpc.send('part', {type: 'irc', nick: nick})
+ })
});
irc_client.on('error', function (error) {
@@ -93,40 +80,57 @@ var web_serv_options = { //certificates for https
};
var echo = sockjs.createServer();
-echo.on('connection', function(conn) {
- var origin = conn.remoteAddress;
- var settings = {
- conn: conn,
- addr: conn.remoteAddress,
- nick: conn.remoteAddress
+
+function pluck (key) {
+ return function (object) {
+ return object[key]
}
- clients.push(settings);
- clients.notifyAll('join', { from: settings.nick })
-// irc_client.say("#krebs", origin + ' has joined');
- if (typeof irc_client.chans['#krebs'] === 'object') {
- conn.write(JSON.stringify({method: 'nicklist', params: { nicklist: irc_client.chans['#krebs'].users }})); //send current nicklist
- };
- conn.write(JSON.stringify({method: 'message', params: { nick: 'system', msg: 'hello' }})) //welcome message
- console.log(irc_client.chans['#krebs'])
- conn.on('data', function(data) {
- console.log('data:',data);
- try {
- var command = JSON.parse(data);
- } catch (error) {
- console.log(error);
- }
- if (!command || typeof command !== 'object') {
- command = {}
+}
+var total_clients_ever_connected = 0
+
+echo.on('connection', function (connection) {
+ var client = {}
+ client.rpc = new RPC(make_sockjs_server_connection_transport(connection))
+ client.nick = 'anon'+(++total_clients_ever_connected)
+ client.rpc.send('your_nick', {nick: client.nick})
+ client.rpc.register('msg', {msg: 'string'}, function (params, callback) {
+ callback(null)
+ clients.map(pluck('rpc')).forEach(function (rpc) {
+ rpc.send('msg', {type: 'web', nick: client.nick, msg: params.msg})
+ })
+ })
+ client.rpc.register('nick', {nick: 'string'}, function (params, callback) {
+ if (!!~clients.map(pluck('nick')).indexOf(params.nick)) {
+ callback('name already taken')
+ } else if (/^anon[0-9]+$/.test(params.nick)) {
+ callback('bad nick')
+ } else {
+ var oldnick = client.nick
+ client.nick = params.nick
+ callback(null)
+ clients.map(pluck('rpc')).forEach(function (rpc) {
+ rpc.send('nick', {type: 'web', newnick: client.nick, oldnick: oldnick})
+ })
}
- return (serverCommands[command.method] || serverCommands.badcommand)(serverstate, settings, command.params, command.id)
- });
- conn.on('close', function() { //propagate if client quits the page
- clients.splice(clients.indexOf(conn));
- clients.notifyAll('quit', { from: settings.nick })
-// irc_client.say("#krebs", origin + ' has quit');
-});
-});
+ })
+ connection.on('close', function() { //propagate if client quits the page
+ clients.splice(clients.indexOf(client));
+ clients.map(pluck('rpc')).forEach(function (rpc) {
+ rpc.send('part', {type: 'web', nick: client.nick})
+ })
+ })
+ //send nicklist to newly joined client
+ clients.map(pluck('nick')).forEach(function (nick) {
+ client.rpc.send('join', {type: 'web', nick: nick})
+ })
+ //add new client to list
+ clients.push(client)
+ //send all including the new client the join
+ clients.map(pluck('rpc')).forEach(function (rpc) {
+ rpc.send('join', {type: 'web', nick: client.nick})
+ })
+})
var app = connect()
.use(connect.logger('dev'))
@@ -139,7 +143,8 @@ var app = connect()
page_template+='<script src="jquery-2.0.3.min.js"></script>\n';
page_template+='<script src="commands.js"></script>\n';
page_template+='<script src="functions.js"></script>\n';
- page_template+='<script src="handler.js"></script>\n';
+ page_template+='<script src="sockjs_client_transport.js"></script>\n';
+ page_template+='<script src="rpc.js"></script>\n';
page_template+='<script src="client.js"></script>\n';
page_template+='<div id="bg">';
page_template+='<div id="chatter">';
diff --git a/webchat/proto_spec b/webchat/proto_spec
new file mode 100644
index 00000000..fffce165
--- /dev/null
+++ b/webchat/proto_spec
@@ -0,0 +1,62 @@
+server -> client:
+#old
+type: 'message' | 'join' | 'quit' | 'nicklist' | 'nickchange' | 'usererror'
+nick: the clients nickname ('message','nickchange')
+newnick: new nick after nickchange ('nickchange')
+from: the clients ip ('message','quit','join')
+message: the data send ('message', 'nicklist','usererror'
+
+
+#new
+type: 'irc_msg' | 'irc_join' | 'irc_quit' | 'irc_nickchange' | 'irc_client_connect' | 'irc_client_disconnect' | 'web_welcome' |'web_msg' | 'web_join' | 'web_quit' | 'web_nickchange' | 'usererror'
+params:{ nick:, oldnick:, nicklist:, msg:, errormsg: }
+
+'irc_msg': nick, msg
+'irc_join': nick
+'irc_quit': nick
+'irc_nickchange': oldnick, nick
+'kweb_irc_connect': nicklist
+'kweb_irc_disconnect': --
+'web_welcome': msg, nicklist, nick
+'web_msg': nick, msg
+'web_join': nick
+'web_quit': nick
+'web_nickchange': oldnick, nick
+'usererror': msg
+
+
+client -> server
+#old
+method: 'say', 'nick'
+params:{ msg:, nick: }
+
+'say': msg
+'nick': nick
+
+
+##############JSON RPC################
+server->client:
+{method: 'say', params: {msg: msg}, id: id}
+-> {result: {ok: ok}, error {error muted?}, id: id}
+
+{method: 'nick', params: {nick: nick}, id: id}
+-> {result: {nick: nick}, error: {error name taken?/reserved/not allowed}, id: id}
+
+
+client->server:
+broadcast:
+ {method: 'irc_msg', params: {nick: nick, msg: msg}, id: 0} #notification
+ {method: 'irc_join', params: {nick: nick}, id: 0} #notification
+ {method: 'irc_quit', params: {nick: nick}, id: 0} #notification
+ {method: 'irc_nickchange', params: {nick: nick, oldnick: oldnick}, id: 0} #notification
+ {method: 'kweb_irc_connect', params: {nicklist: nicklist}, id: 0} #notification
+ {method: 'kweb_irc_disconnect', params: {}, id: 0} #notification
+ {method: 'web_msg', params: {nick: nick, msg: msg}, id: 0} #notification
+ {method: 'web_join', params: {nick: nick}, id: 0} #notification
+ {method: 'web_quit', params: {nick: nick}, id: 0} #notification
+ {method: 'web_nickchange', params: {nick: nick, oldnick: oldnick}, id: 0} #notification
+
+unicast:
+ {method: 'coi', params: {}, id: id}
+ -> {result: {result: {nick: nick, addr: addr}, error: {connection error?}, id: id}
+ {method: 'usererror', params: {msg: 'error type'}, id: 0} #notification
diff --git a/webchat/public/client.js b/webchat/public/client.js
index 8f7becb7..8865fcc0 100644
--- a/webchat/public/client.js
+++ b/webchat/public/client.js
@@ -23,44 +23,43 @@ function request (settings, method, params, callback) {
$(function connect() {
settings.sock = new SockJS('/echo');
-
- settings.sock.onopen = function() {
- console.log('open');
- request(settings, 'coi', {}, function (error, result) {
- if (error) {
- console.log('coi error', error)
- } else {
- settings.nick = result.nick //TODO: write to display
- settings.addr = result.addr //TODO: write to display
- }
- })
- };
- settings.sock.onmessage = function(e) {
- console.log('message', e.data);
- try {
- var object = JSON.parse(e.data);
- console.log(object);
-
- } catch (error) {
- console.log(error);
- throw error;
- }
- if (typeof object.method === 'string') {
- return methodDispatcher(settings, object);
- } else if (typeof object.result === 'string') {
- return resultDispatcher(settings, object);
- } else {
- console.log('bad message:', object)
- }
- }
- settings.sock.onclose = function(event) {
- console.log('close');
- switch (event.code) {
- case 1006: //abnormal closure
- return setTimeout(connect, 1000);
- };
- };
-
+ var transport = make_sockjs_client_transport(settings.sock)
+ var rpc = new RPC(transport)
+ rpc.register('msg', {type: 'string', nick: 'string', msg: 'string'}, function(params, callback) {
+ var safe_message = $('<div/>').text(params.msg).html();
+ safe_message = replaceURLWithHTMLLinks(safe_message);
+ var safe_from = $('<div/>').text(params.nick).html();
+ chatboxAppend(safe_from, safe_message, 'web_msg')
+ return callback(null)
+ })
+ rpc.register('nick', {type: 'string', newnick: 'string', oldnick: 'string'}, function(params, callback) {
+ var safe_oldnick = $('<div/>').text(params.oldnick).html();
+ var safe_newnick = $('<div/>').text(params.newnick).html();
+ var safe_type = $('<div/>').text(params.type).html();
+ $(getNicklistElement(safe_oldnick,safe_type)).remove();
+ $('#nicklist').append('<div class="'+safe_type+'_name">' + safe_newnick + '</div>') ;
+ chatboxAppend(safe_oldnick, 'is now known as ' + safe_newnick, 'nick');
+ return callback(null)
+ })
+ rpc.register('your_nick', {nick: 'string'}, function(params, callback) {
+ var safe_nick = $('<div/>').text(params.nick).html();
+ settings.nick = safe_nick
+ return callback(null)
+ })
+ rpc.register('join', {type: 'string', nick: 'string'}, function(params, callback) {
+ var safe_nick = $('<div/>').text(params.nick).html();
+ var safe_type = $('<div/>').text(params.type).html();
+ $('#nicklist').append('<div class="'+safe_type+'_name">' + safe_nick + '</div>') ;
+ chatboxAppend(safe_nick, 'has joined');
+ return callback(null)
+ })
+ rpc.register('part', {type: 'string', nick: 'string'}, function(params, callback) {
+ var safe_nick = $('<div/>').text(params.nick).html();
+ var safe_type = $('<div/>').text(params.type).html();
+ $(getNicklistElement(safe_nick,safe_type)).remove();
+ chatboxAppend(safe_nick, 'has parted');
+ return callback(null)
+ })
});
$(function() {
$('#input').keydown(function(e) {
diff --git a/webchat/public/commands.js b/webchat/public/commands.js
index 5a570556..d4408c4c 100644
--- a/webchat/public/commands.js
+++ b/webchat/public/commands.js
@@ -1,14 +1,15 @@
var commands = {}
-commands.say = function (settings, params) {
+commands.msg = function (settings, params) {
var sendObj = {
- method: 'say',
- params: { msg: params },
- };
+ method: 'msg',
+ params: { msg: params }
+ }
settings.sock.send(JSON.stringify(sendObj))
}
commands.nick = function (settings, params) {
+ settings.nick = params
var sendObj = {
method: 'nick',
params: { nick: params },
@@ -17,7 +18,7 @@ commands.nick = function (settings, params) {
}
commands.badcommand = function (settings, params) {
- console.log("error");
+ console.log("error", params);
chatboxAppend( '<span class="from_system">error</span>', 'command not found' )
diff --git a/webchat/public/functions.js b/webchat/public/functions.js
index 45c8ad3f..244af67b 100644
--- a/webchat/public/functions.js
+++ b/webchat/public/functions.js
@@ -3,25 +3,11 @@ function inputParser (str) {
if (match) {
return { method: match[1], params: match[2] }
} else {
- return { method: 'say', params: str }
+ return { method: 'msg', params: str }
}
}
-function methodDispatcher (settings, object) {
- console.log('parser: ',object)
- return (handler[object.method] || console.log)(settings, object.params)
-};
-
-function resultDispatcher (settings, object) {
- console.log('parser: ',object)
- var callback = settings.waiting_callbacks[object.id]
- delete settings.waiting_callbacks[object.id]
- if (typeof callback === 'function') {
- callback(object.error, object.result)
- }
-};
-
function replaceURLWithHTMLLinks (text) {
var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
@@ -37,9 +23,9 @@ function setMaybeNick (input) {
function sortNicklist () {
};
-function getNicklistElement(name) {
+function getNicklistElement(name, type) {
var el;
- $('.name').each(function (i,e) {
+ $('.'+type+'_name').each(function (i,e) {
if (e.innerHTML === name) {
if (typeof el !== 'undefined') {
throw new Error('duplicate name: ' + name);
diff --git a/webchat/public/handler.js b/webchat/public/handler.js
deleted file mode 100644
index 0930646d..00000000
--- a/webchat/public/handler.js
+++ /dev/null
@@ -1,35 +0,0 @@
-var handler = {}
-
-handler.message = function(settings, object) {
- var safe_message = $('<div/>').text(object.msg).html();
- safe_message = replaceURLWithHTMLLinks(safe_message);
- var safe_from = $('<div/>').text(object.nick).html();
- return chatboxAppend(safe_from, safe_message, 'msg')
-};
-
-handler.join = function(settings, object) {
- var safe_from = $('<div/>').text(object.from).html();
- $('#nicklist').append('<div class="name">' + safe_from + '</div>') ;
- return chatboxAppend(safe_from, 'joined', 'join')
-};
-
-handler.quit = function(settings, object) {
- var safe_from = $('<div/>').text(object.from).html();
- $(getNicklistElement(safe_from)).remove();
- return chatboxAppend(safe_from, 'quit', 'quit')
-};
-
-handler.nicklist = function(settings, object) {
- Object.keys(object.nicklist).forEach(function (nick) {
- var hash_from = btoa(nick).replace(/=/g,'_');
- $('#nicklist').append('<div class="name">' + nick + '</div>') ;
- });
-};
-
-handler.nickchange = function(settings, object) {
- var safe_from = $('<div/>').text(object.nick).html();
- var safe_newnick = $('<div/>').text(object.newnick).html();
- $(getNicklistElement(safe_from)).remove();
- $('#nicklist').append('<div class="name">' + safe_newnick + '</div>') ;
- return chatboxAppend(safe_from, 'is now known as ' + safe_newnick, 'nick');
-};
diff --git a/webchat/public/rpc.js b/webchat/public/rpc.js
new file mode 100644
index 00000000..8e911e1d
--- /dev/null
+++ b/webchat/public/rpc.js
@@ -0,0 +1,99 @@
+try {
+ module.exports = RPC
+}
+catch(e){
+}
+
+function RPC (transport) {
+ this._id = 0
+ this._waiting_callbacks = {}
+ this._methods = {}
+ this._transport = transport
+
+ transport.onmessage = this.onmessage.bind(this)
+}
+
+RPC.prototype.register = function (method, params, callback) {
+ this._methods[method] = callback
+}
+
+RPC.prototype.send = function (method, params, callback) {
+ var message = {
+ method: method,
+ params: params,
+ }
+ if (callback) {
+ var id = ++this._id
+ this._waiting_callbacks[id] = callback
+ message.id = id
+ }
+ return this._transport.send(message)
+}
+
+function _is_request (message) {
+ return typeof message.method === 'string'
+}
+
+function _is_response (message) {
+ return message.hasOwnProperty('result')
+ || message.hasOwnProperty('error')
+}
+
+RPC.prototype.onmessage = function (message) {
+ console.log('RPC message:', message)
+ if (_is_request(message)) {
+ return this._on_request(message)
+ }
+ if (_is_response(message)) {
+ return this._on_response(message)
+ }
+ return this._on_bad_message(message)
+}
+
+RPC.prototype._on_request = function (request) {
+ var method = this._methods[request.method] || function(){
+ console.log('method not found', request.method)
+ }
+ var params = request.params
+ var id = request.id
+
+ var transport = this._transport
+
+ if (typeof id === 'string' || typeof id === 'number' || id === null) {
+ return method(params, function (error, result) {
+ var response = {
+ id: id,
+ }
+ if (error) {
+ response.error = error
+ } else {
+ response.result = result
+ }
+ console.log('request:', request, '->', response)
+ return transport.send(response)
+ })
+ } else {
+ return method(params, function (error, result) {
+ var response = {
+ id: id,
+ }
+ if (error) {
+ response.error = error
+ } else {
+ response.result = result
+ }
+ console.log('notification:', request, '->', response)
+ })
+ }
+}
+
+RPC.prototype._on_response = function (response) {
+ var result = response.result
+ var error = response.error
+ var id = response.id
+
+ var callback = this._waiting_callbacks[id]
+ delete this._waiting_callbacks[id]
+
+ return callback(result, error)
+}
diff --git a/webchat/public/sockjs_client_transport.js b/webchat/public/sockjs_client_transport.js
new file mode 100644
index 00000000..4e525d0d
--- /dev/null
+++ b/webchat/public/sockjs_client_transport.js
@@ -0,0 +1,27 @@
+
+function make_sockjs_client_transport (sock) {
+ var transport = {}
+
+ sock.onmessage = function (data) {
+ console.log('sockjs parse', data)
+ try {
+ var message = JSON.parse(data.data)
+ } catch (error) {
+ return console.log('error', error)
+ }
+ transport.onmessage(message)
+ }
+
+ transport.send = function (message) {
+ try {
+ var data = JSON.stringify(message)
+ } catch (error) {
+ return console.log('sockjs transport send error:', error)
+ }
+ sock.send(data)
+ }
+
+ return transport
+}
+
+
diff --git a/webchat/serverCommands.js b/webchat/serverCommands.js
deleted file mode 100644
index 303c4805..00000000
--- a/webchat/serverCommands.js
+++ /dev/null
@@ -1,26 +0,0 @@
-var serverCommands = {};
-
-serverCommands.coi = function (serverstate, settings, params, id) {
- return settings.conn.write({ result: { nick: settings.nick, addr: settings.addr}, id: id})
-}
-
-serverCommands.say = function (serverstate, settings, params, id) {
- var nick = settings.nick
- var message = params.msg
- params.nick = nick
- serverstate.irc_client.say("#krebs", nick + ' → ' + message);
- return serverstate.clients.notifyAll('message', params)
-}
-
-serverCommands.nick = function (serverstate, settings, params, id) {
- var oldnick = settings.nick
- var newnick = params.nick
- settings.nick = newnick
- return serverstate.clients.notifyAll('nickchange', { nick: oldnick, newnick: newnick });
-}
-
-serverCommands.badcommand = function (serversate, settings, params, id) {
- settings.conn.write(JSON.stringify({ method: 'usererror', params: { message: 'bad command' }}))
-}
-
-module.exports = serverCommands
diff --git a/webchat/sockjs_server_connection_transport.js b/webchat/sockjs_server_connection_transport.js
new file mode 100644
index 00000000..6f68b955
--- /dev/null
+++ b/webchat/sockjs_server_connection_transport.js
@@ -0,0 +1,26 @@
+
+module.exports = function make_sockjs_server_connection_transport (connection) {
+ var transport = {}
+
+ connection.on('data', function (data) {
+ try {
+ var message = JSON.parse(data)
+ } catch (error) {
+ return console.log('error', error)
+ }
+ transport.onmessage(message)
+ })
+ connection.on('close', function () {
+ })
+
+ transport.send = function (message) {
+ try {
+ var data = JSON.stringify(message)
+ } catch (error) {
+ return console.log('sockjs transport send error:', error)
+ }
+ connection.write(data)
+ }
+
+ return transport
+}