@kyanny's blog

My thoughts, my life. Views/opinions are my own.

%エスケープの決定版みたいなやり方はあるのかな

本当にあった仕事の話シリーズ。かなりわかってないまま書いてるので、言葉の使い方とかすごく間違っているかもしれない。


http://kansai.pm.org/cgi-bin/wiki.cgi?page=%A5%A4%A5%D9%A5%F3%A5%C8%2F%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF%CA%D9%B6%AF%B2%F1%B9%F0%C3%CE

こういう URL が、 Sledge のコントローラで $self->r->param("link") とか受け取ると

http://kansai.pm.org/cgi-bin/wiki.cgi?page=イベント/フレームワーク勉強会告知

という文字列になる。 $self->r->param("link") で参照すると、 EUC-JP で符号化された文字列を含む文字列になる。 Perl で pack/unpack して n進数を変換する解説ページにならって?バイナリと呼ぼう。

DB には %エスケープされた状態の文字列、 ascii なものが入っていて、 $self->r->param("link") でうけとった文字列をキーにして、一度 DB から SELECT したい。そのときにバイナリを 16進数になおす、 %エスケープをする必要があるのでそういうふうにしたのだけど、

    my $link = $self->r->param("link");
    my ($path, $query_string) = split /\?/, $link;
    $path         =~ s/([^#;\/?:@&=+\$,A-Za-z0-9\-_.!~*'()])/'%'. unpack('H2', $1)/eg;
    $query_string =~ s/([^#;?@&=+\$,A-Za-z0-9\-_.!~*'()])/'%'. unpack('H2', $1)/eg if $query_string;

    my $escaped_link = $path;
    if ($query_string) {
        $escaped_link .= '?'. $query_string;
    }
    elsif ($link =~ /\?$/) {
        $escaped_link .= '?';
    }

こんなコードをかいて、 query_string のほうは slash をエスケープしないといけないのに path はしないから同じ正規表現をつかっていてエスケープされた結果が DB に入ってる文字列と違ってしまってうまく引けない、なんてのではまったりして。

わざわざ ? で区切ったりして、すごく面倒くさいことをしているよなあと感じる。もっと簡単な方法があるんじゃないかと思うんだけど、どうなんだろうか。昔ながらの CGI で配布されてるコードだと、 H2 じゃなくて C, hex を組み合わせたエスケープのコードがコピペで使われているけど、あれよりモダンな世界ではもっとエレガントな方法がありそうなものなんだけど。

こういうののテストを書きたいのだけど、バイナリになってしまうタイミングがどうもよくわからない。 Perl レベルじゃなくて apache のレベル? rewrite したりとか、そういう時点でそうなってしまっているんだろうか。いまいちどういう風にテストをかいたらいいものかアイデアがない。

URI->new() でバイナリのほうをいれて $uri->as_string したら望むものを返してくれたりするんだろうか?これはあとで試してみよう。