ローカルのNodeプログラムで外部Socket.IOサーバにWebSocketで接続する

socket.ioのtestではcommon.jsというところにwebsocketでhandshakeして接続するコードがあったので、そこから拝借。

var io = require('socket.io');
var http = require('http');
var WebSocket = require('websocket-client').WebSocket;
var parser = io.parser;

function HTTPClient (host, port) {
    this.host = host;
    this.port = port;
    this.agent = new http.Agent({
        host: host,
        port: port
    });
};
HTTPClient.prototype = {
    get: function (path, callback) {
        var buf = '';
        var req = http.request({
            host: this.host,
            port: this.port,
            path: path,
        }, function (res) {
            res.on('data', function (chunk) {
                buf += chunk;
            });
            res.on('end', function () {
                callback(null, res, buf);
            });
        });
        req.on('error', function (err) {
            console.log('error');
            callback(err);
        });
        req.end();
    },
    handshake: function (callback) {
        this.get('/socket.io/' + io.protocol, function (err, res, data) {
            if (err) { throw err; }
            callback(data.split(':')[0]);
        });
    }
};

function WSClient (host, port, sid) {
    this.sid = sid;
    this.port = port;
    WebSocket.call(
        this,
        'ws://' + host + ':' + port + '/socket.io/' + io.protocol + '/websocket/' + sid
    );
};
WSClient.prototype = {
    __proto__: WebSocket.prototype,
    emit: function (name) {
        var args = arguments;
        if (name === 'message' || name === 'data') {
            args[1] = parser.decodePacket(args[1].toString());
        }
        return WebSocket.prototype.emit.apply(this, arguments);
    }
};


function websocket (host, port, callback) {
    var client = new HTTPClient(host, port);
    client.handshake(function (sid) {
        callback(new WSClient(host, port, sid));
    });
}

websocket('127.0.0.1', '3000', function (socket) {
    socket.on('open', function () {
        console.log('open!');
    });
    socket.on('message', function (msg) {
        console.log(msg);
    });
});


こんなかんじで作っておくと、

var io = require('socket.io').listen(3000);
io.sockets.on('connection', function (socket) {
    setInterval(function () {
        console.log('emit');
        io.sockets.emit('hoge', 'fuga');
    }, 1000);
});

みたいなサーバに対し繋ぐとmessageを受け取れているのが確認できる。

$ node client.js
open!
{ type: 'connect', endpoint: '', qs: '' }
{ type: 'event',
  endpoint: '',
  name: 'hoge',
  args: [ 'fuga' ] }
{ type: 'event',
  endpoint: '',
  name: 'hoge',
  args: [ 'fuga' ] }
{ type: 'event',
  endpoint: '',
  name: 'hoge',
  args: [ 'fuga' ] }
{ type: 'event',
  endpoint: '',
  name: 'hoge',
  args: [ 'fuga' ] }
{ type: 'event',
  endpoint: '',
  name: 'hoge',
  args: [ 'fuga' ] }

hostとportを正しく指定すれば、外部サーバに対してでも(v0.7系で動作していて、WebSocket接続がサポートされていれば)繋げられるはず。

宿題

というmentionをいただいたのですが、イマイチやり方がよく分かりませんでした… orz
基本的に同一マシン内でサーバ/クライアント両方立ち上げているときにRedisを仲介して通知しあう、という方法なのだという理解ですが…