ホーム > perl

perlのアーカイブ

Plack::Middleware::Header

  • 投稿者: chiba
  • 2009/12/3 木曜日 23:19:23
  • perl

Plack::Middleware::Headerをgithubに上げました。

これは、Plack::Middlewareレイヤーでレスポンスヘッダを設定するもので、Apacheでいうところのmod_headersに相当します。

使い方はこんな感じです。

use Plack::Builder;
builder {
    enable 'Header',
        set => ['X-Plack-One' => '1'],
        append => ['X-Plack-Two' => '2'],
        unset => ['X-Plack-Three'];
    $app;
};

自分はPlack::Middleware::Staticと併用してExpiresをつけたりするのに使うために作りました。IEがExpires: -1とかやっておかないとしばらくキャッシュしてIf-Modifled-Sinceなリクエストも投げてくれなくなっちゃうという弊害があったので。

あと、IE関連では、If-Modified-Sinceの値に”;length=***”という余計なものが付いてきてしまい(ここここを参照)、Middleware::ConditionalGETを有効にしても304を吐けなかったりしていたので、それを取り除くためのMiddlewareも書いてみました。こちらは名前とかがきもいのでgistレベル。

追記(2009-12-13 21:27): cpanにアップしました。あと、If-Modified-SinceのIEバグの件はmiyagawaさんがConditionalGET側で対応してくれました

はてなブックマーク - Plack::Middleware::Header

patternがemptyな場合には前回成功時のpatternが使われるというハマりポイント

  • 投稿者: chiba
  • 2009/11/26 木曜日 2:42:23
  • perl
print "match1\n" if "baz" =~ m{};
"foo" =~ m{foo};
print "match2\n" if "baz" =~ m{};

の結果は

match1

である。なぜならperldoc perlrerefに書いてある通り、

If 'pattern' is an empty string, the last successfully matched regex is used. 

であるから。

patternを動的に生成してたりする時に空になっちゃうケースなんかがあると思うので、その場合はマッチ処理自体を回避するようにしたほうがよいかと思われます。

最初バグかと思いました。

はてなブックマーク - patternがemptyな場合には前回成功時のpatternが使われるというハマりポイント

Casual Talks#1でLTしてきました

  • 投稿者: chiba
  • 2009/11/21 土曜日 22:35:56
  • perl

昨日(11/20)にperl-casualのCasual Talks#1にて拙作のFormValidator::NestedについてLTしてきました。

slideをslideshareにあげておきました。

得られた教訓
1. 実際に話し始めると早口気味になるのでスライド大目ぐらいが自分にはちょうどいいかも
2. プロジェクターにつなぐときはねじをちゃんとしめておく
3. やっぱり笑いを一つぐらいとっといたほうが場は暖かくなる
(PAUSE ID requestの際にcatalyst-pluginを作りたいと書いていたと言ったときに若干嘲笑気味の笑いはありましたがw)

自分以外も発表が初めてだという方も多かったのに、発表が皆さんうまくて非常に面白かったです。またの機会があったら今回の教訓を踏まえまた発表できたら幸いです。

主催されたyusukebeさん、会場の用意や二次会の(実質)幹事をされたnobjasさんお疲れ様でした。

はてなブックマーク - Casual Talks#1でLTしてきました

JSocketを使ってPOSTもストリーミングするPlackアプリ

  • 投稿者: chiba
  • 2009/10/20 火曜日 3:58:37
  • perl

さて、前エントリJSocketというのを作ったと書いたのですが、これは実はjavascriptで動くtwitter streamクライアントを作るならばmultipart/mixedを使うべきというのを読んで、レスポンスがpollではなくてストリーミングできるというのを知って、リクエストもやりたいよというふうに思ったのがきっかけでした。

まず、XHRでできないかを試してみたのですが、xhr.send(data)を一回読んでしまうと少なくともjavascript側ではリクエストは完了したと思ってしまって、再度xhr.send(data)しても反応はありませんでした。

そこでJSocketの登場です。JSocketを使うとhttpリクエストを断続的に送信することができます。やったですね。

で、Plack::Server::Coroを使って試しに
リアルタイムチャット
を作ってみました。どうぞお試し下さい。レスポンスもJSocketを使っていますのでmultipartに比べてboundary分お得だったりもします。テキストエリアに入力された文字がストリーミングで送信され、また他人のデータもストリーミングで受信できていると思います。ちなみに自分の発言もサーバから持ってきてるので一人でも確認できます。

ちなみにこれを1つのコネクションでやってしまおうというのがWebsocketなんだろうという認識ですね。ただwebsocketはhttpの上に一応のってますがハンドシェイク以外の部分はhttp/1.0やhttp/1.1的には正しくないでしょうし、JSocketでの二重接続であれば正しいhttpとしてふるまえるはずで、現行のapacheやproxy等でも扱える可能性があったりします。

あ、チャットのサンプルコードもgithubにあげてあります。

plackup -s Coro --app chat.psgi 

という感じでPlack::Server::Coroをお使いください。

はてなブックマーク - JSocketを使ってPOSTもストリーミングするPlackアプリ

mod_psgi試してみた

  • 投稿者: chiba
  • 2009/10/16 金曜日 18:17:20
  • perl

mod_psgi をインストールしてみたをみて速くなるなら試してみようということでmod_psgiを試してみますた。

まずはこんな感じの簡単なPSGIアプリで。

my $app = sub {
    return ['200', [Content-Length => '5'], ['hello']];
};
ab -n 10000 -c 100
mod_psgi
 Requests per second:    1403.51 [#/sec] (mean)
mod_perl
 Requests per second:    668.33 [#/sec] (mean)
Standalone
 Requests per second:    702.46 [#/sec] (mean)
Standalone::Prefork
 Requests per second:    725.78 [#/sec] (mean)

おおおお。超絶早いですね!
(plackupの際に-E product(development以外ならなんでもおk)をつけないとだいぶ遅くてそれをそのまま載せてたのを修正しました)

で、お次は、Catalyst::Engine::PSGIを使ってCatalystのhello worldなアプリを。

catalyst.pl Hello

で作って、debugモードを切って、Root.pmのindexで

    $c->response->content_type('text/plain');
    $c->response->body( 'hello' );

こんな感じにしたもの。

ab -n 1000 -c 10
mod_psgi
 Requests per second:    207.87 [#/sec] (mean)
mod_perl
 Requests per second:    144.94 [#/sec] (mean)
cat-standalone
 Requests per second:    184.95 [#/sec] (mean)

おおおおお。早いですね!!これはこれだけでも乗り換える意味がありそうだ。と思ってたんですが現在実運用中のCatalystアプリでやってみたところ”child pid 32320 exit signal Segmentation fault (11)”とかいって死にました。うーむ。これは再現する最小構成を探ってid:spiritlooseさんにフィードバックしたいところですね。

casual-perlerな人たちは今持ってるCatalystアプリをmod_psgi+Catalyst::Engine::PSGIで動かしてどんどんフィードバックするといいんじゃないかな!

はてなブックマーク - mod_psgi試してみた

Acme::Coro::Sukeをリリースしました

  • 投稿者: chiba
  • 2009/10/12 月曜日 15:44:51
  • perl

多分、5人以上の人が思いついたけどあまりのくだらなさに作成を断念したといううわさのAcme::Coro::Sukeを先ほどCPANにリリースしました。(githubにもあげてあります)

これは、Coroのasyncと基本的には同じ動作をするbenzoというブロック定義ができるようになるもので、benzoブロックにCoroがスレッドを切り替えるたびに、「うわぁ…べんぞうさんの中…すごくあったかいナリぃ… 」とコロちゃんが囁やいてくれるモジュールです。
一番簡単な使い方は以下のようになります。

use Coro;
use Acme::Coro::Suke;
benzo {
};
cede;

enjoy Acme::Coro::Suke!

CPANに上げる三つめのモジュールがAcmeモジュールというのはこれはひどい。

はてなブックマーク - Acme::Coro::Sukeをリリースしました

Re: PSGI Implのsendfileについて

  • 投稿者: chiba
  • 2009/10/7 水曜日 15:29:06
  • perl

PSGI Implのsendfileについてですが、自分の今のところのイメージを書いておくです。

on Plack::Server

  • $res->[2]がGLOBだったら fileno($res->[2])して sendfile に fd を渡す
  • $res->[2]->can(‘fileno’) が生えてたら、$res->[2]->filenoからfdを取って使う
  • $res->[2]->can(‘path’) が生えてたら、$res->[2]->pathからファイルパスを取って使う

$env->{psgix.sendfile}を使わない以外は一緒

on app

realfileをserveしたい時は

  • GLOBを返す
  • ->pathを生やしたIO-Handle-likeなオブジェクトを返す

の二種類の方法があって、後者のほうがあらゆるサーバで最適化される可能性がある。かといって前者だからといってクライアントに届くresponseが変わることはないと期待される。後者において->filenoの実体のファイルと->pathが違うものだった場合の動作は未定義。

on middleware

  • Plack::Middleware::XSendfile
    responseから->pathが取れる場合にはそれをヘッダのX-Sendfileにセットするようなmiddleware。これのpsgix.sendfileサポートを無くしたやつのイメージ。
    X-Sendfileヘッダをセットする場合というのはbackendがlightyの場合とかだと思うけど、それをセットしていいかどうかはPlackは知り得ない。かといってappでセットしていて必要のない時に外にファイルPATHが出ていくものいくない。というわけでappでは->path付きオブジェクトを返すだけにして、このmiddlewareをbackendにあわせて付けたり外したりする。Plack::Server::*のsendfile(2)サポートとかとはまったく関係無い。
  • Plack::Middleware::GuessPath
    GLOBからファイルPathを推測して->path付きのオブジェクトに変換しちゃうMiddleware。マルチプラットフォーム対応が課題。Plack::Middleware::XSendfileより先に実行する。Plack::Server::*のsendfile(2)サポートとも関係する。


追記(10/7 17:24): 大体ここに書いてある感じで追加されたみたいです。詳しくは本家specのchangesetをご覧ください。Plack::Middleware::XSendfileも追加されたみたいです。miyagawaさん仕事ハヤス、、、。

はてなブックマーク - Re: PSGI Implのsendfileについて

ファイルPATHを覚えているfhを作成できるIO::File::WithPathをリリース

  • 投稿者: chiba
  • 1:49:30
  • perl

Plackのsendfileサポート関連を追いかけてて、Plack::Server::Apache2でsendfileサポートされてない理由がfhからファイル名を取るベストな方法がないというわけで、gist: 200797みたいなパッチを書いて#http-engineとかに張り付けたけどそれlinuxでしか動かないよねみたいなことで日本の恥を晒してしまったりしてたわけですが、その中でIO::File::WithPathみたいなのがあればいいよねと話がでていたので作ってみました。

CPANgithubにあげてあります。

使い方はこんな感じ。

    my $fh = IO::File::WithPath->new('/path/to/file');
    print $fh->path; # /path/to/fileがとれるよ
    print $fh->getline; # IO::Handleとしても扱える
    print <$fh>; # もちろんGLOBとしても

Plackのappでは

    return ['200', [], IO::File::WithPath->new('/path/to/file')];

みたいな使い方を想定していますが、Plack::Server側はこのモジュールを使うことが前提というわけではなくて、duck typingで->pathがあるオブジェクトであればそのpathを(使える条件であれば)使うようになる予定だと思います。

あと、純粋なfhからpathを取るマルチプラットフォーム対応なモジュールもできれば欲しいよねというところ。windowsはかなり無理っぽい。

はてなブックマーク - ファイルPATHを覚えているfhを作成できるIO::File::WithPathをリリース

Plack::Requestからのhostのとりかた

  • 投稿者: chiba
  • 2009/9/30 水曜日 21:42:21
  • perl

Plack::Requestにhost無いなぁ。と思って勢い余ってforkしてコミットまでしてしまったのですが、#perl-casual@freenodeで呟いたところyappoさんから

$req->uri->host

もしくはURIオブジェクトの作成コストが気になるなら($req->uriは遅延作成なので)

$req->env->{HTTP_HOST}

でいいじゃんと言われました。確かにその通りですね。

はてなブックマーク - Plack::Requestからのhostのとりかた

Wassrのyapcasia2009チャンネルの発言をIRCに投稿するボットをYAPC::Asiaの二日目向けに作った

  • 投稿者: chiba
  • 2009/9/10 木曜日 23:47:54
  • perl

twitterの#yapcasia2009ハッシュタグがスクリーンに晒されるのが個人的に非常にツボだったので、wassrのyapcasia2009チャンネルに発言するとirc.freenode.orgの#yapc.asia-jaに投稿するボットも作ってみました。
wassrにはstreaming APIはないようなので5秒ごとにpollingしちゃってます。いいんでしょうか。
参考文献(つうかほぼパクリ):
Twitterのハッシュタグ付き発言をIRCに投稿するボットをYAPC::Asia向けに作った

以下コード

#!/usr/bin/perl
use strict;
use warnings;

use AnyEvent::IRC::Client;
use Net::Wassr;
use Encode ();
use Storable;

my $channel = '#yapc.asia-ja';
my $interval = 5;
my $wassr_channel = 'yapcasia2009';

my $wassr = Net::Wassr->new(
    user   => 'nihen',
    passwd => '*****',
);



my $cv = AnyEvent->condvar;
my $pc = AnyEvent::IRC::Client->new;


my $send_message = -e 'send_message' ? retrieve('send_message') : {};
$SIG{INT} = $SIG{TERM} = sub {
    $pc->disconnect('bye');
    store $send_message, 'send_message';
    exit;
};

$pc->reg_cb(
    connect => sub {
        my ( $pc, $err ) = @_;
        if ( defined $err ) {
            warn $err;
            return;
        }
    },
    registered => sub {
        my ( $self ) = @_;
        print "registerd!\n";
        $pc->enable_ping(60);
    },
    disconnect => sub {
        print "disconnected: $_[1]!\n";
    }
);
$pc->send_srv('JOIN', $channel);
$pc->send_chan($channel, 'NOTICE', $channel, 'hi');
$pc->connect('irc.freenode.org', 6667, {
    nick => 'wassr_bot',
    user => 'wassr_bot',
    real => 'wassr_bot',
});

my $w = AnyEvent->timer(after => $interval, interval => $interval, cb => sub {
    my $time_line = $wassr->channel_timeline('name_en=' . $wassr_channel);
    if ( !$time_line || ref $time_line ne 'ARRAY' ) {
        return;
    }

    foreach my $message ( @{$time_line} ) {
        next if $send_message->{$message->{rid}};

        $send_message->{$message->{rid}} = 1;

        $pc->send_chan($channel, 'NOTICE', $channel, Encode::encode('utf-8', $message->{user}->{login_id} . ': ' . $message->{body}));
    }
});


$cv->wait;
はてなブックマーク - Wassrのyapcasia2009チャンネルの発言をIRCに投稿するボットをYAPC::Asiaの二日目向けに作った

1 2 3 4 5 6

ホーム > perl

検索
フィード
メタ情報

ページの上部に戻る