2010-03-09

[plack] response body を変更する Middleware の試作

Web アプリケーションが見事にコードリファレンス一つに抽象化されている PSGI では、あるアプリケーションに前処理・後処理を追加して、動作をちょっと変更する Middleware という仕組みがあって、それで色々と面白いことができそうなので、試しにくだらないものを作ってみました。

今回作ったのは、実用性皆無ですが、後処理を追加することで、出力をすべて大文字にしてくれる Middleware です。

必要なモジュール: Plack, Twiggy

使い方: plackup -s Twiggy app.psgi で動かして、http://localhost:5000/stream などを見てみる。

package Plack::Middleware::UpperCase;
use strict;
use warnings;
use parent qw(Plack::Middleware);

sub call {
    my ($self, $env) = @_;

    my $res = $self->app->($env);

    return $self->response_cb($res, sub {
        my $res = shift;

        return sub {
            my $body_chunk = shift;

            if (defined $body_chunk) {
                return uc $body_chunk;
            } else {
                return;
            }
        };
    });
}

package main;
use strict;
use warnings;
use AnyEvent;
use Plack::Builder;
use Plack::Response;

builder {
#    enable 'Plack::Middleware::UpperCase';
    enable sub { Plack::Middleware::UpperCase->wrap(@_) };

    mount '/static' => sub {
        my $env = shift;

        my $res = Plack::Response->new(200);
        $res->content_type('text/plain');
        $res->body("static response");

        return $res->finalize;
    };

    mount '/handle' => sub {
        my $env = shift;

        my $res = Plack::Response->new(200);
        $res->content_type('text/plain');

        open my $io, '<', \'body handle';
        $res->body($io);

        return $res->finalize;
    };

    mount '/stream' => sub {
        my $env = shift;

        return sub {
            my ($write) = @_;

            my $writer = $write->([
                200,
                [
                    'Content-Type' => 'text/plain',
                ],
            ]);

            my $count = 0;
            my $t; $t = AE::timer 0, 0.5, sub {
                $writer->write("date:@{[ scalar localtime ]}, count:@{[ $count++ ]}\n");

                if ($count > 10) {
                    undef $t;
                    $writer->close;
                }
            };
        };
    };
};

hiratara さんの記事 PSGI 1.03のMiddlewareを書いてみる を参考にしながら作ってみたのですが、Plack::Component::response_cb の定義をよく見ると、body_filter として返すコードリファレンスの内容は、return undef をすると $res->[2] に無駄な undef が入るので、return () の方を使うこのコードの方がよいと思います。