[Perl] PSGI入門第二章 CGI::SessionをPSGIで無理やり使う

#この記事には問題があります。
[こちら](https://qiita.com/worthmine/items/78b7c782a3c442318d54)をお読みください。

# 本日のお題：CGI::SessionをPSGIで無理やり使う
こ## おさらい

- 前章　[PSGI入門 第一章 環境変数の一覧を出す](http://qiita.com/worthmine/items/96f36332e620aef34001)

## 背景
1. 新しいウェブアプリケーションを書くにあたって
2. [PCGI](http://plackperl.org/)を使うべきか [Mojolicious](http://www.mojolicious.org/)を使うべきか悩んだ
3. 調べてみたらセッションに大きく違いがあった
4. Plackのセッションの実装は非常に簡素[^1]
5. Mojoliciousのセッションはちょっと特殊[^2]
6. 中間が欲しいんだよね。ってか[CGI::Session](http://search.cpan.org/~markstos/CGI-Session-4.48/lib/CGI/Session.pm)をそのまま移植したようなのが使いたい

## やってみた。
結論から先に書くとこうなります。

```perl:Myapp.pl
use strict;
use warnings;
use Carp;

use Plack::App::Path::Router::PSGI;
use Path::Router;
use Plack::Request;

use CGI::Session;
my $session = CGI::Session->new( undef, undef, { Directory => './sessions' } ); # ダミーのセッション
my $sid = $session->param('CGISESSID') || undef;
$session->delete(); # ダミーはすぐ消す
$session = CGI::Session->new( undef, $sid, { Directory => './sessions' } ); # 本セッション
if ( $session->is_expired() ) { # 期限切れを消して再発行
    $session->delete();
    $session = CGI::Session->new( undef, undef, { Directory => './sessions' } );
}else{
    $session->expire('+1d');
}

my $router = Path::Router->new;
$router->add_route( '/' => target => \&root );
$router->add_route( '/env' => target => \&env );

# now create the Plack app
my $app = Plack::App::Path::Router::PSGI->new( router => $router );
$app->to_app();

sub root {
    my $env = shift;
    my $req = Plack::Request->new($env);
    my %param = %{ $req->body_parameters };
    if( exists $param{'action'} and $param{'action'} eq 'forget' ) {
        $session->param( 'str', '' );
    }elsif( exists $param{'str'} ){
        $session->param( 'str', $param{'str'} );
    }

    my $str = $session->param('str') || '';
    return response( $env, <<"END");
<html><body>
    <h1>セッション管理してみる</h1>
    <form method="POST" action="/" enctype="multipart/form-data">
        <input type="text" name="str" value="$str">
        <button type="submit" name="action" value="remember">記憶</button>
        <button type="submit" name="action" value="forget">消去</button>
    </form>
</body></html>
END
}

sub env {
    my $env = shift;
    my @str;
    while ( my ( $key, $value ) = each %$env ) {
        push @str, "$key = $value" unless ref $value;
    }
    return response( $env,
        join( "<br>\n", '<html><body><h1>環境変数一覧</h1>', sort(@str), '</body></html>')
    );
}

# サブルーチン
sub response {
    my $env = shift;
    my $body = shift || croak 'empty body!';
    my %ARG = @_ if @_;
    my $status = $ARG{'-status'} || 200;
    croak "unvalid status: $status" if $status !~ /^\d{3}$/s;
    my $mime = $ARG{'-MIME'} || 'text/html; charset=utf-8';
    my $headers = $ARG{'-headers'} || {};
    my $req = Plack::Request->new($env);
    my $res = $req->new_response($status);
    $res->content_type($mime);
    $res->header(
        'Set-Cookie' => $session->cookie,
        %$headers
    );
    $res->body($body);
    $res->finalize;
}
```

結論、Set-Cookieヘッダに`$session->cookie`渡すだけ[^3]なんですよね。全然、過去の資産引き継げます。前回紹介した[環境変数一覧がこっそり/envにルーティングされてる](http://localhost:5000/env)点も注目ですよ！
**ね、簡単でしょう？**あなたも始めよう！PSGI！

## 次章
- [PSGI入門第三章 動的ルーティングを実装する](http://qiita.com/worthmine/items/9662ac8f30a30b167123)

# 宣伝
本記事は自前で有料WebサービスをPerlで書いてリリースするに辺り、つまづいた点や気づいたことを共有する目的で書き始めました。

![worthmine-qiita.png](https://qiita-image-store.s3.amazonaws.com/0/10766/370df067-1108-c7b8-2ea3-59ab11155660.png)こちらのQRコードをスマホで読み取り、
興味を持った方は<https://qrown.me>までぜひお立ち寄りください！

---
[^1]: [Plack::Middleware::Session](http://search.cpan.org/~miyagawa/Plack-Middleware-Session/lib/Plack/Session.pm)によると、`expire()`すら単なる`delete`でしかない。
[^2]: [GitHub](https://github.com/yuki-kimoto/mojolicious-guides-japanese/wiki/Mojolicious::Sessions)に丁寧な日本語訳があります。
[^3]: [Plack::Response]()には`cookies`メソッドがあるが、どちらかというと生cookie派の仕様で、セッション管理には向かない。