2016/04/19

なんでも Furl

Webアプリやモジュールに HTTPクライアントを組み込む場合、安心と信頼とスピードの Furl でまかないたいというのは老若男女問わずもがなの夢だと思われる。Furl 自体は、そうした user agent の切り替えが考慮されていて、Furl 開発当時のデファクトであった LWP::UserAgent とは互換が取りやすく作られている。おおむねそのまま挿げ替えて動く。ちょっとうまくいかない場面と言えば、例えば Net::Twitter::Lite で ua を Furl に切り替えたら default_header メソッドが無いと言われる程度だ。それも以下のように Furl を継承したクラスを用意すれば良い。

package MyUA;
use strict;
use warnings;
use parent 'Furl';

sub default_header {
    my ($self, %args) = @_;

    push @{${$self}->{headers}}, each(%args);
}

1;

これはもしかしたら、Furl が取り込んでくれたりするとありがたい。

いずれにしろ、LWP::UserAgent から Furl へのスイッチはたやすいのは間違いない。

しれっと現れてた HTTP::Tiny

そんな折、HTTP::Tiny というHTTPクライアントモジュールが登場している。HTTP/1.1 を扱う Pure Perl モジュールで、Perl 5.14 からコアに入っている。CPAN.pm や cpanm の中の HTTP クライアントとしても知られているところだ。安心も信頼も厚いが、深遠なる理由により、HTTP::Tiny を Furl に挿げ替えたい場面もあるであろう。そんなときは、先ほどの MyUA を継承しつつ、主に request メソッドで差分を吸収してやれば良さそうである(get/post以外も必要に応じて同様に)。

package MyUA::HTTPTiny;
use strict;
use warnings;
use parent 'MyUA';

sub get {
    my $self = shift;

    my $res = $self->SUPER::get(@_);

    return $res;
}

sub post {
    my ($self, $url, $data, $args) = @_;

    my $res = $self->SUPER::post($url, $args, $data->{content});

    return $res;
}

sub timeout {}

sub request {
    my $self = shift;

    my ($method, $url, $tiny_args) = @_;

    my %args;

    $args{url}     = $url;
    $args{method}  = $method;
    $args{content} = $tiny_args->{content};
    $args{headers} = [ map { $_ => $tiny_args->{headers}{$_} } keys %{$tiny_args->{headers}} ];

    my (
        $res_minor_version,
        $res_status,
        $res_msg,
        $res_headers,
        $res_content,
        $captured_req_headers,
        $captured_req_content,
        $captured_res_headers,
        $captured_res_content,
        $request_info,
        ) = ${$self}->request(%args);

    my $res = Furl::Response->new($res_minor_version, $res_status, $res_msg, $res_headers, $res_content);
    $res->set_request_info(\%args, $captured_req_headers, $captured_req_content);

    $res->is_success or carp $res->status_line;

    return {
        success => $res->is_success ? 1 : 0,
        content => $res->content,
        !$res->is_success ? (reason => $res->status_line) : (),
        status  => $res->status,
        reason  => $res->status_line,
        headers => $res->headers,
    };
}

1;

ざっとこんな感じという以上の何物でもないが、いったんこんなんで動く様子である。特徴としては、HTTP::Tiny はオブジェクトでもろもろ扱わずハッシュでやりくりするので、Furl がメソッドで呼び出したりしているところではまるとけっこう面倒だが、幸いこの程度で動いてくれるのは、双方が HTTPクライアント実装の頂上決戦のようにだいたい似ているのが幸いしていると見ているが裏づけはないので各自ソースを追ってみて欲しい。

See Also

サイト内検索