Socket.ioで一つのlisterで全てのイベントを受け取るHack

久々にSocket.io書いてて、イベント名を決めるのが面倒になって、もうこうなったら任意のイベントを受け取って勝手にブロードキャストするようにしてやれ、と思って書きました。

var app = require('http').createServer(handler)
  , sio = require('socket.io')
  , io = sio.listen(app)
  , fs = require('fs');

app.listen(8001);

function handler (req, res) {
  fs.readFile(__dirname + '/index.html',
              function (err, data) {
                if (err) {
                  res.writeHead(500);
                  return res.end('Error loading index.html');
                }

                res.writeHead(200);
                res.end(data);
              });
}

// $emitをoverrideする。
var _$emit = sio.Socket.prototype.$emit;

sio.Socket.prototype.$emit = function() {
  // オリジナルの$emitを呼び出す。
  _$emit.apply(this, arguments);
  if (arguments.length >= 2) {
    var eventname = arguments[0];
    var message = arguments[1];
    // 任意のイベントは * に補足させる。
    _$emit.apply(this, ['*', eventname, message]);
  }
};

io.sockets.on('connection', function (socket) {
  socket.on('*', function (eventname, message) {
    io.sockets.emit(eventname, message);
  });
});

みたいな。

後はindex.htmlを作って

<html>
  <head>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      var socket = io.connect();
      socket.emit('my favorite event', { my: 'data' });
      socket.on('my favorite event', function (data) {
        console.log(data);
      });
    </script>
  </head>
  <body>
    <h1>Welcome to Socket.io</h1>
  </body>
</html>


ブラウザからhttp://localhost:8001を開いてコンソールに "{ my: 'data' } " が出ていたら完了。イベントを登録しなくてもよくなった。

ただ、Socket.io側で現在、ワイルドカードでのイベントフックをサポートするように画策されているので、その対応ができたらこのHackは不要になります。

Add wildcard support for events · Issue #434 · LearnBoost/socket.io · GitHub

あと、このままだとdisconnectとかの内部で予約されているイベントも受け取ってブロードキャストしちゃうので、まじめにやるならそういう予約されているイベント名の場合はブロードキャストしないような対応が必要かも。

とか書いてたら sorah さんがほぼ同じコードを書いてた。
socket.io で任意のeventを一つのlistenerで受け取る - blog.sorah

こっちのほうがargsをちゃんと使っててエレガントかも。でも、"anything"イベントはsocket.ioのドキュメント見ると、イベント名として定義されていそうなので、回避したほうが良さげ。

Exposed events · LearnBoost/socket.io Wiki · GitHub

あと、単純にフックするだけならこのドキュメントに書かれている"anything"イベントだけでできそうだが、イベント名がないからそのまま同じイベント名でブロードキャストするのが難しそう。これはまだ試してない。