diff options
| author | lassulus <lassulus@googlemail.com> | 2013-11-12 02:01:29 +0100 | 
|---|---|---|
| committer | lassulus <lassulus@googlemail.com> | 2013-11-12 02:01:29 +0100 | 
| commit | 3531d46cc3644a64b990f904e40e4846b8242a07 (patch) | |
| tree | a0289317e14a8011d7a6615010b70e5d21eff02c /webchat | |
| parent | c6a7cd9027150fca42500b3bbc78be01993856f6 (diff) | |
webchat: refactor to rpc
Diffstat (limited to 'webchat')
| -rw-r--r-- | webchat/index.js | 129 | ||||
| -rw-r--r-- | webchat/proto_spec | 62 | ||||
| -rw-r--r-- | webchat/public/client.js | 75 | ||||
| -rw-r--r-- | webchat/public/commands.js | 11 | ||||
| -rw-r--r-- | webchat/public/functions.js | 20 | ||||
| -rw-r--r-- | webchat/public/handler.js | 35 | ||||
| -rw-r--r-- | webchat/public/rpc.js | 99 | ||||
| -rw-r--r-- | webchat/public/sockjs_client_transport.js | 27 | ||||
| -rw-r--r-- | webchat/serverCommands.js | 26 | ||||
| -rw-r--r-- | webchat/sockjs_server_connection_transport.js | 26 | 
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 +} | 
