Plack::Middlewareでリクエスト終了後になにがしかのか処理をしたい場合
sub call {
my ($self, $env) = @_;
my $t0 = [gettimeofday];
my $res = $self->app->($env);
my $ela = Time::HiRes::tv_interval($t0);
};
と書きそうになりますが、これだと $res が CodeRef になるStreaming形式のレスポンスでは正しく処理ができません。 Streaming形式のレスポンスは多くないだろうとか思ってると、Catalystがstreaming形式のレスポンスを返したりするので注意が必要です
そこで Plack::Util::response_cb を使うと楽です
sub call {
my ($self, $env) = @_;
my $t0 = [gettimeofday];
my $res = $self->app->($env);
Plack::Util::response_cb($res, sub {
my $res = shift;
sub {
my $chunk = shift;
if ( ! defined $chunk ) {
my $ela = Time::HiRes::tv_interval($t0);
}
return $chunk;
}
});
};
リクエスト処理が終わると、$chunk が undefined になるのでそこで、ごにょごにょ処理します。
ただ response_cb はレスポンスのbodyにフィルタをかける用に作られているためか、Content-Length ヘッダが消されてしまいます。レアケースでContent-Typeを消したくない時、またresponse_cbをスキップして処理を高速化(ベンチマーク未実施)したい時は、responseが配列かどうか確認して処理してしまうのが良いでしょう
sub call {
my ($self, $env) = @_;
my $t0 = [gettimeofday];
my $res = $self->app->($env);
if ( ref($res) && ref($res) eq 'ARRAY' ) {
my $ela = Time::HiRes::tv_interval($t0);
return $res;
}
Plack::Util::response_cb($res, sub {
my $res = shift;
sub {
my $chunk = shift;
if ( ! defined $chunk ) {
my $ela = Time::HiRes::tv_interval($t0);
}
return $chunk;
}
});
};
以上、Plack::Middleware::AxsLogやServerStatus::Liteはこんな実装になってますよという話でした