本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーはbayashiこと岡林大さんで、
本稿のサンプルコードは、
今も熱いPlack::Middleware
PSGIは
PSGI/
今回は、
Plack::Middlewareとは
Plack::Middlewareは、
 
何も処理をしないシンプルなPlack::Middlewareの実装例は、
package Plack::Middleware::Foo;
use parent qw( Plack::Middleware );
sub call {
    my ($self, $env) = @_;
    # 1. ミドルウェアの処理
    my $res = $self->app->($env); # アプリケーションの処理
    # 2. ミドルウェアの処理
    return $res;
}リクエストごとに呼ばれるcallメソッドで$envを受け取り、$res$self->appがラップされるアプリケーションの処理で、
つまり、
- アプリケーションの処理の前に仕事をするもの
- アプリケーションの処理のあとに仕事をするもの
- アプリケーションの処理の前後両方で仕事をするもの
という3パターンがあります。
以降ではこの3パターンについて、
PSGIアプリケーションの入り口で処理
まずは、REMOTE_HTTP_SERVER_
ソースコードは次のようになっています
sub call {
    my $self = shift;
    my $env = shift;
    $env->{HTTPS} = $env->{'HTTP_X_FORWARDED_HTTPS'}
        if $env->{'HTTP_X_FORWARDED_HTTPS'};
    $env->{HTTPS} = 'ON'
        if $env->{'HTTP_X_FORWARDED_PROTO'}
        && $env->{'HTTP_X_FORWARDED_PROTO'} eq 'https';
    $env->{'psgi.url_scheme'} = 'https'
        if $env->{HTTPS} && uc $env->{HTTPS} eq 'ON';
    (省略)
    $self->app->($env);
}Plack::Middleware::ReverseProxyによってアプリケーションではリバースプロキシによる環境変数の書き換えを意識せずにコーディングできます。もしこのミドルウェアがなければ、
このように、
PSGIアプリケーションの出口で処理
続いては、
次のコードでは、$self->appのあとにContent-Typeを確認しつつ、
sub call {
    my $self = shift;
    my $res = $self->app->(@_);
    $self->response_cb($res, sub {
        my $res = shift;
        my $h = Plack::Util::headers($res->[1]);
        return unless $h->get('Content-Type');
        if ($h->get('Content-Type') =~ m!^text/!) {
            return sub {
                my $chunk = shift;
                return unless defined $chunk;
                local $_ = $chunk;
                $self->filter->();
                return $_;
            };
        }
    });
}$self->response_やPlack::Utilに属する関数が登場していますが、$self->appのあとに処理をしているという構造に注目しておきましょう。
PSGIアプリケーションの入り口と出口で処理
さらに、
次のコードでは、$self->appの前に時間を取っておき、
sub call {
    my($self, $env) = @_;
    my $start = [ Time::HiRes::gettimeofday ];
    my $res = $self->app->($env);
    my $header = $self->header_name || 'X-Runtime';
    $self->response_cb($res, sub {
        my $res = shift;
        my $req_time
            = sprintf '%.6f',
                Time::HiRes::tv_interval($start);
        Plack::Util::header_set(
            $res->[1],
            $header,
            $req_time
        );
    });
}このような処理は、$self->appをラップしているという設計であればこそです。もしアプリケーションの前後に処理が分離した設計だったら、
<続きの
本誌最新号をチェック!
WEB+DB PRESS Vol.133
2023年2月24日発売
B5判/176ページ
定価1,628円
(本体1,480円+税10%)
ISBN978-4-297-13370-2
- 特集1
 識者がたどり着いた最適解
 TypeScript最新活用
 リンタ、バンドラ、ランタイム、エッジ
- 特集2
 コミッター直伝!
 速習Ruby 3.2
 Wasm対応、ReDoS対策、性能改善
- 特集3
 Tailwind CSS実践入門
 まず作ってから、あとで共通化する


