diff options
| author | makefu <github@syntax-fehler.de> | 2013-11-12 14:43:50 +0100 | 
|---|---|---|
| committer | makefu <github@syntax-fehler.de> | 2013-11-12 14:43:50 +0100 | 
| commit | 8b60dff25e85921fe533f621a1b5d0f8149bf38f (patch) | |
| tree | b39eb0e15e630f5b5af624ec91b8f222a25170ae | |
| parent | 8ebba4db20b8127e57490eeea95d50d158dc69dd (diff) | |
| parent | b749c216ae84f02330da7396135be11259ec12d9 (diff) | |
Merge branch 'master' of github.com:krebscode/painload
| -rw-r--r-- | webchat/index.js | 152 | ||||
| -rw-r--r-- | webchat/proto_spec | 62 | ||||
| -rw-r--r-- | webchat/public/client.js | 123 | ||||
| -rw-r--r-- | webchat/public/functions.js | 54 | ||||
| -rw-r--r-- | webchat/public/krebs.png | bin | 956 -> 2583 bytes | |||
| -rw-r--r-- | webchat/public/reset.css | 23 | ||||
| -rw-r--r-- | webchat/public/rpc.js | 99 | ||||
| -rw-r--r-- | webchat/public/sockjs_client_transport.js | 25 | ||||
| -rw-r--r-- | webchat/sockjs_server_connection_transport.js | 26 | 
9 files changed, 432 insertions, 132 deletions
| diff --git a/webchat/index.js b/webchat/index.js index 8ef737fc..9569fc80 100644 --- a/webchat/index.js +++ b/webchat/index.js @@ -1,20 +1,30 @@ +'use strict';  var fs = require('fs');  var http = require('https');  var sockjs = require('sockjs');  var connect = require('connect');  var irc = require('irc'); -var Clients = []; +var make_sockjs_server_connection_transport = require('./sockjs_server_connection_transport.js') +var RPC = require('./public/rpc.js'); +var irc_nicks = [] -Clients.broadcast = function(object) { //broadcast to all clients -  Clients.forEach(function(client) { -    client.write(JSON.stringify(object)); -  }); +function pluck (key) { +  return function (object) { +    return object[key] +  } +} + +var clients = []; +clients.broadcast = function (method, params) { +  clients.map(pluck('rpc')).forEach(function (rpc) { +    rpc.send(method, params) +  })  }  var irc_reconnect = function() { //reconnt to irc -  console.log("reconnecting due to pingtimeout"); -  irc_client.disconnect(); -  irc_client.connect(); +  console.log("would reconnect now") +//  irc_client.disconnect() +//  irc_client.connect()  }  var pingTimeoutDelay = 3*60*1000 @@ -27,7 +37,7 @@ var irc_client = new irc.Client('irc.freenode.net', 'kweb', { //create irc_clien    userName: 'kweb', //todo: read from local_config    realName: 'kweb', //todo: read from local_config    password: fs.readFileSync(__dirname+'/local_config/irc.key').toString(), -  debug: false, +  debug: true,    showErrors: true,    floodProtection: true,    port: 6697, @@ -36,7 +46,6 @@ var irc_client = new irc.Client('irc.freenode.net', 'kweb', { //create irc_clien    stripColors: true  }); -  irc_client.on('ping', function(server) { //restart timer on server ping    console.log("got ping from server, renewing timeout for automatic reconnect");    clearTimeout(lastping); @@ -45,60 +54,97 @@ 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.broadcast({ from: from, message: message }); //broadcast irc messages to all connected clients +  clients.broadcast('msg', {nick: from, msg: message})    clearTimeout(lastping);  }); -var web_serv_options = { //certificates for https -  key: fs.readFileSync(__dirname+'/local_config/server_npw.key'), -  cert: fs.readFileSync(__dirname+'/local_config/server.crt'), -}; +irc_client.on('names#krebs', function(nicks) { +  Object.keys(nicks).forEach(function (nick) { +  irc_nicks.push(nick) +    clients.broadcast('join', {type: 'irc', nick: nick}) +  }) +}) -var echo = sockjs.createServer(); -echo.on('connection', function(conn) { -  var origin = conn.remoteAddress; -  Clients.push(conn); -  Clients.broadcast({from: 'system', message: origin + ' has joined'}) -//  irc_client.say("#krebs", origin + ' has joined'); -  conn.write(JSON.stringify({from: 'system', message: 'hello'})) //welcome message -  conn.on('data', function(data) { -    console.log('data:',data); -    try { -      var object = JSON.parse(data); -      if (object.message.length > 0) { //if message is not empty -        if (/^\/nick\s+(.+)$/.test(object.message)) { //if nick is send use nick instead of ip -          object.from = origin; -        } else if (typeof object.nick === 'string') { -          object.from = object.nick; -        } else { -          object.from = origin; -        }; -        console.log(object.message); -        irc_client.say("#krebs", object.from + ' → ' + object.message); -        Clients.broadcast(object); -      } - -    } catch (error) { -      console.log(error); -    } -  }); -  conn.on('close', function() { //propagate if client quits the page -  Clients.splice(Clients.indexOf(conn)); -  Clients.broadcast({from: 'system', message: origin + ' has quit'}) -//  irc_client.say("#krebs", origin + ' has quit'); -}); +irc_client.on('join#krebs', function(nick, msg) { +  if (nick !== 'kweb'){ +    irc_nicks.push(nick) +    clients.broadcast('join', {type: 'irc', nick: nick}) +  } +}) + +irc_client.on('part#krebs', function(nick, rs, msg) { +  clients.broadcast('part', {type: 'irc', nick: nick})  }); +irc_client.on('error', function (error) { +  irc_nicks.forEach( function(nick) { +    client.rpc.send('part', {type: 'irc', nick: nick}) +    irc_nicks.splice(irc_nicks.indexOf(nick)) +  }) +  console.log('irc-client error', error) +}) + +var echo = sockjs.createServer(); + +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.broadcast('msg', {type: 'web', nick: client.nick, msg: params.msg}) +    irc_client.say('#krebs', client.nick + ' → ' + 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.broadcast('nick', {type: 'web', newnick: client.nick, oldnick: oldnick}) +      irc_client.say('#krebs', oldnick + ' is now known as ' + client.nick) +    } +  }) +  connection.on('close', function() { //propagate if client quits the page +    clients.splice(clients.indexOf(client)); +    clients.broadcast('part', {type: 'web', nick: client.nick}) +    irc_client.say('#krebs', client.nick + ' has parted') +  }) +  //send the irc nicklist to the new joined client +  irc_nicks.forEach( function(nick) { +    client.rpc.send('join', {type: 'irc', nick: 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.broadcast('join', {type: 'web', nick: client.nick}) +  //send join to irc +  irc_client.say('#krebs', client.nick + ' has joined') +})  var app = connect()    .use(connect.logger('dev'))    .use(connect.static(__dirname+'/public'))    .use( function (req, res) {      res.writeHead(200, {'Content-Type': 'text/html'}); -    page_template='<!doctype html>\n'; +    var page_template='<!doctype html>\n';      page_template+='<link rel="stylesheet" type="text/css" href="reset.css">\n';      page_template+='<script src="sockjs-0.3.min.js"></script>\n';      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="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">'; @@ -106,7 +152,8 @@ var app = connect()      page_template+='hello, this is the official krebs support:<br>\n';      page_template+='<table id="chatbox"><tr id="foot"><td id="time"></td><td id="nick" class="chat_from"></td><td><input type="text" id="input"></td></tr></table>\n';      page_template+='</div>'; -    page_template+='<div id="sideboard"><div id="links">'; +    page_template+='<div id="sideboard"><div id=nicklist></div>'; +    page_template+='<div id="links">';      page_template+='<a href="http://gold.krebsco.de/">krebsgold browser plugin</a><br>';      page_template+='<a href="http://ire:1027/dashboard/">ire: Retiolum Dashboard</a><br>';      page_template+='<a href="http://pigstarter/">pigstarter: network graphs</a><br>'; @@ -114,6 +161,11 @@ var app = connect()      res.end(page_template);    }) + +var web_serv_options = { //certificates for https +  key: fs.readFileSync(__dirname+'/local_config/server_npw.key'), +  cert: fs.readFileSync(__dirname+'/local_config/server.crt'), +};  var server = http.createServer(web_serv_options, app);  echo.installHandlers(server, {prefix:'/echo'});  server.listen(1337, '0.0.0.0'); 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 ca71b537..54ccfe34 100644 --- a/webchat/public/client.js +++ b/webchat/public/client.js @@ -1,34 +1,50 @@ -function replaceURLWithHTMLLinks (text) { -  var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; -  return text.replace(exp,"<a href='$1'>$1</a>"); -} -function setMaybeNick (input) { -  var match = /^\/nick\s+(.+)$/.exec(input); -  if (match) { -    nick = match[1]; -    $('#nick').html(nick); -  } -} +var settings = {} +settings.sock = new SockJS('/echo'); +settings.waiting_callbacks = {} -function getCurTime () { -  date = new Date; -  h = date.getHours(); -  if(h<10) -  { -    h = "0"+h; -  } -  m = date.getMinutes(); -  if(m<10) -  { -    m = "0"+m; -  } -  s = date.getSeconds(); -  if(s<10) -  { -    s = "0"+s; +var transport = make_sockjs_client_transport(settings.sock) +settings.rpc = new RPC(transport) + +settings.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) +}) +settings.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(); +  if (safe_oldnick === settings.nick){ +    settings.nick = safe_newnick +    $('#nick').html(settings.nick)    } -  return ''+h+':'+m+':'+s; -}; +  $(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) +}) +settings.rpc.register('your_nick', {nick: 'string'}, function(params, callback) { +  var safe_nick = $('<div/>').text(params.nick).html(); +  settings.nick = safe_nick +  $('#nick').html(settings.nick) +  return callback(null) +}) +settings.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) +}) +settings.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 updateTime () {    $('#time').html(getCurTime()); @@ -36,59 +52,18 @@ $(function updateTime () {    return true;  }); -var nick; - -$(function connect() { -  sock = new SockJS('/echo'); - -  sock.onopen = function() { -    console.log('open'); -    sock.send('open'); -  }; -  sock.onmessage = function(e) { -    console.log('message', e.data); -    try { -      var object = JSON.parse(e.data); -      console.log(object.message); -      var safe_message = $('<div/>').text(object.message).html(); -      safe_message = replaceURLWithHTMLLinks(safe_message); -      var safe_from = $('<div/>').text(object.from).html(); -      $('<tr><td class="chat_date">'+getCurTime()+'</td><td class="chat_from">'+safe_from+'</td><td class="chat_msg">'+safe_message+'</td></tr>').insertBefore('#foot'); -      var elem = document.getElementById('chatter'); -      elem.scrollTop = elem.scrollHeight; - -    } catch (error) { -      console.log(error); -    } -  }; -  sock.onclose = function(event) { -    console.log('close'); -    switch (event.code) { -      case 1006: //abnormal closure -        return setTimeout(connect, 1000); -    }; -  }; - -});  $(function() {    $('#input').keydown(function(e) {      if (e.keyCode === 13) {        e.preventDefault();        e.stopPropagation();        e.stopImmediatePropagation(); -      setMaybeNick($('#input').val()); -      var sendObj = { -        message: $('#input').val(), -      }; - -      if (typeof nick === 'string') { -        sendObj.nick = nick; -      }; +      var input = ($('#input').val()); +      $('#input').val('') -      sock.send(JSON.stringify(sendObj)); -      $('#input').val(''); -      return; +      var command = inputParser(input) +      return (commands[command.method] || commands.badcommand)(settings, command.params)      }    }); diff --git a/webchat/public/functions.js b/webchat/public/functions.js new file mode 100644 index 00000000..781fafce --- /dev/null +++ b/webchat/public/functions.js @@ -0,0 +1,54 @@ +function inputParser (str) { +  var match = /^\/([a-z]+)(?:\s+(.*\S))?\s*$/.exec(str) +  if (match) { +    return { method: match[1], params: match[2] } +  } else { +    return { method: 'msg', params: str } +  } +} + +function replaceURLWithHTMLLinks (text) { +  var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; +  return text.replace(exp,"<a class=chat_link href='$1'>$1</a>"); +} + +function getNicklistElement(name, type) {  +  var el; +  $('.'+type+'_name').each(function (i,e) { +    if (e.innerHTML === name) { +      if (typeof el !== 'undefined') { +        throw new Error('duplicate name: ' + name); +      }; +      el = e; +    }; +  }); +  return el; +} + +function chatboxAppend (chat_from, chat_msg, type) { +  type = type||'msg' +  $('<tr><td class="date '+type+'_date">'+getCurTime()+'</td><td class="from '+type+'_from">'+chat_from+'</td><td class="msg '+type+'_msg">'+chat_msg+'</td></tr>').insertBefore('#foot'); + +  var elem = document.getElementById('chatter'); +  elem.scrollTop = elem.scrollHeight; +}; + +function getCurTime () { +  date = new Date; +  h = date.getHours(); +  if(h<10) +  { +    h = "0"+h; +  } +  m = date.getMinutes(); +  if(m<10) +  { +    m = "0"+m; +  } +  s = date.getSeconds(); +  if(s<10) +  { +    s = "0"+s; +  } +  return ''+h+':'+m+':'+s; +}; diff --git a/webchat/public/krebs.png b/webchat/public/krebs.pngBinary files differ index 263d5b1c..5762e7f4 100644 --- a/webchat/public/krebs.png +++ b/webchat/public/krebs.png diff --git a/webchat/public/reset.css b/webchat/public/reset.css index 65f68058..d369bc86 100644 --- a/webchat/public/reset.css +++ b/webchat/public/reset.css @@ -21,7 +21,6 @@ time, mark, audio, video {    border: 0;    font-size: 100%;    font: inherit; -  font-family: monospace;    vertical-align: baseline;  }  /* HTML5 display-role reset for older browsers */ @@ -33,6 +32,7 @@ body {    line-height: 1;    background-color: black;    color: white; +  font-family: monospace;  }  ol, ul {    list-style: none; @@ -54,17 +54,18 @@ q:before, q:after {  }  #input{    width: 100%; -  background-color: #555555; +  background-color: #221111;    border: 1px solid black;    color: white;  } -.chat_from { +.from {    color:grey;    font-weight: bold;    text-align: right;    font-size:12px; +  white-space: nowrap;  } -.chat_from:after { +.from:after {    content: ":";    padding-right: 6px;  } @@ -87,16 +88,16 @@ q:before, q:after {  }  .chat_date,.chat_from,.chat_msg{  } -.chat_msg{ +.msg{    width: 100%;  }  a {    color: red;  } -.chat_date { +.date {    color: green;  } -.chat_date:after { +.date:after {    content: "";    padding-right: 4px;  } @@ -116,4 +117,10 @@ a {    font-size: 14px;    position: absolute;    bottom: 5px; -}
\ No newline at end of file +} +.join_msg { +  color: #00FF00; +} +.quit_msg { +  color: #FF0000; +} 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..a7b76af3 --- /dev/null +++ b/webchat/public/sockjs_client_transport.js @@ -0,0 +1,25 @@ + +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/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 +} | 
