天然パーマです。

multipart/mixedなストリームをPlack/PSGIでpushする

元ネタがmattnさんがsinatraで作ったものかつ、 さらにmiyagawaさんにコアな部分を教えてもらったということで、 お二人に敬意を示しつつ紹介。

Big Sky :: javascriptで動くtwitter streamクライアントを作るならばmultipart/mixedを使うべき では、Twitter Stream API で取得したTwitterのつぶやきをmultipart/mixedで送出し、 それをdiggで使われているDUI.jsとStream.jsというJSのライブラリで受け取り、表示する方法を紹介しています。

sinatraで実装されているので、Plack/PSGIでも実装できないかと思って作ってみました。

$ plackup -a fast-twitter-stream.psgi --port 8080 -s Coro

で起動します。ServerにはCoroを指定します。 以下が fast-twitter-stream.psgi です。

 use Coro;
use Coro::Channel;
use Coro::AnyEvent;
use AnyEvent::Twitter::Stream;
use Plack::Request;
use Plack::Builder;
use IO::Handle::Util qw(io_from_getline);
use Encode;

my $username = $ENV{TWITTER_USERNAME};
my $password = $ENV{TWITTER_PASSWORD};
my $boundary = '|||';
my $app = sub {
    my $env = shift;
    my $req = Plack::Request->new($env);

    if ( $req->path eq '/push' ) {
        my $queue    = Coro::Channel->new;
        my $streamer = AnyEvent::Twitter::Stream->new(
            username => $username,
            password => $password,
            method   => 'filter',
            track => 'twitter',
            on_tweet => sub {
                $queue->put(@_);
            },
        );
        my $body = io_from_getline sub {
            my $tweet = $queue->get;
            if( $tweet->{text} ){
                return "--$boundary\nContent-Type: text/html\n" .
                    Encode::encode_utf8( $tweet->{text} );
            }else{
                return '';
            }
        };
        return [ 200, ['Content-Type' => qq{multipart/mixed; boundary="$boundary"} ], $body ];
    }
    if ( $req->path eq '/' ) {
        my $res = $req->new_response(200);
        $res->content_type('text/html');
        $res->body( html() );
        $res->finalize;
    }
};

builder {
    enable "Plack::Middleware::Static",
        path => qr{\.(?:png|jpg|gif|css|txt|js)$},
            root => './static/';
    $app;
};

sub html {
    my $html = <<'HTML';
<html><head>
<title>Server Push</title>
<script src="">http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js">
<script type="text/javascript" src="/js/DUI.js"></script>
<script type="text/javascript" src="/js/Stream.js"></script>
<script type="text/javascript">
$(function() {
var s = new DUI.Stream();
s.listen('text/html', function(payload) {
$('#content').prepend('<p>' + payload + '</p>');
});
s.load('/push');
});
</script>
</head>
<body>
<h1>Server Push</h1>
<div id="content"></div>
</body>
</html>
HTML
    return $html;
}

ブラウザから"http://localhost:8080/"にアクセスすると、 もの凄い勢いでつぶやきが追加されていくのが分かるかと思います。しかも切断していないのでかなり高速です。

お試しの効果には個人差があります。

multipart/mixedとこのDiggライブラリの組み合わせ、いいですね。 というわけで、元ネタになったmattnさん、Plackでpush配信の仕方を教えてくれたmiyagawaさんありがとうございました。 Enjoy!