dotcloudでsocket.io 0.7系のxhr-pollingを動かしてみる

nginxを挟んでsocket.ioのxhr-pollingを動かすには - すぎゃーんメモ
で調べた通り、現在 nginx+socket.io 0.7系のhttp-pollingが繋がらないようになっていて、DotCloudがまさにそういう環境なので、試してみた。
ちなみに後から知ったのだけど、このあたりについては随分前から既に議論されていたようで。
0.7 doesn't work on Dotcloud, but 0.6.18 does. · Issue #301 · socketio/socket.io · GitHub

動かないパターン

http://docs.dotcloud.com/services/nodejs/を読みつつ、プロジェクトを作って設定ファイルなど作成。
依存モジュールごと全部アップロードして使うことも出来てしまうようだけど、ここでは敢えてそれをせずにpackage.jsonによる自動依存解決に任せる。

{
    "name": "sugyan",
    "version": "0.0.1",
    "private": true,
    "dependencies": {
        "express": ">=2.4.4",
        "socket.io": ">=0.7.9",
        "ejs": ">=0.4.3"
    }
}

こんなかんじで書いておくと、dependenciesに書いたものがdeploy時に自動的にインストールされて起動される。
で、こんなかんじの普通のWebAPP+Socket.IOのサーバを書いて動かしてみる。

var express = require('express');
var sio = require('socket.io');

var app = express.createServer();
app.set('view engine', 'ejs');
app.use(express['static'](__dirname + '/public'));

app.get('/', function (req, res) {
    res.render('index');
});

app.listen(8080);

var io = sio.listen(app);
io.set('transports', ['xhr-polling']);
io.sockets.on('connection', function (socket) {
    setInterval(function () {
        socket.send('hoge');
    }, 1000);
});

deployしてアクセスしてみても、やはり繋がらない。前述の記事と同じ問題が起きている模様。

無理矢理動かす

ということで、以下のように無理矢理xhr-pollingのdoWriteメソッドの部分だけ書き換えてみる。headersに"Keep-Alive"を書かないようにするだけ。

var express = require('express');
var sio = require('socket.io');

var app = express.createServer();
app.set('view engine', 'ejs');
app.use(express['static'](__dirname + '/public'));

app.get('/', function (req, res) {
    res.render('index');
});

app.listen(8080);

var io = sio.listen(app);
io.set('transports', ['xhr-polling']);
io.sockets.on('connection', function (socket) {
    setInterval(function () {
        socket.send('hoge');
    }, 1000);
});

var path = require('path');
var HTTPPolling = require(path.join(path.dirname(require.resolve('socket.io')), 'lib', 'transports', 'http-polling'));
var XHRPolling  = require(path.join(path.dirname(require.resolve('socket.io')), 'lib', 'transports', 'xhr-polling'));
XHRPolling.prototype.doWrite = function (data) {
    HTTPPolling.prototype.doWrite.call(this);

    var origin = this.req.headers.origin,
        headers = {
            'Content-Type': 'text/plain; charset=UTF-8',
            'Content-Length': data === undefined ? 0 : Buffer.byteLength(data)
        };
    
    if (origin) {
        // https://developer.mozilla.org/En/HTTP_Access_Control
        headers['Access-Control-Allow-Origin'] = '*';
        if (this.req.headers.cookie) {
            headers['Access-Control-Allow-Credentials'] = 'true';
        }
    }
    
    this.response.writeHead(200, headers);
    this.response.write(data);
    this.log.debug(this.name + ' writing', data);
};

こうすることで、一応0.7系のSocket.IOでもDotCloud上でxhr-polling使って繋げられるようになる。
全然良い解決策とは思えないけど。

余談

今回検証するにあたって色々試そうとしてたらpackage.jsonからの依存解決がうまく動いていなかったりして、拙い英語でsupport forumに投げてみたりした。
https://dotcloud.zendesk.com/entries/20347516-how-run-npm-install-in-node-js-application
色々調べて丁寧に対応していただき、無事に直ったようです。とりあえず言ってみるもんですね!