理系学生日記

おまえはいつまで学生気分なのか

LWP::Authen::Wsse を使うとき,なぜ use LWP::Authen::Wsse しなくて良いのか

以下は SYNOPSIS を CPAN からパクってきただけなんですけど,なんで use LWP::Authen::Wsse してないのかがちょっと不思議だったりしました.ソースを見ても,別に LWP::UserAgent の名前空間を汚しているわけじゃないし,なんで LWP::UserAgent は,LWP::Authen::Wsse をインストールするだけで WSSE 認証に対応できるようになってんだ! わからん!! 調べなければ!!

    use LWP::UserAgent;
    use HTTP::Request::Common;
    my $url = 'http://www.example.org/protected_page.html';

    # Set up the WSSE client
    my $ua = LWP::UserAgent->new;
    $ua->credentials('example.org', '', 'username', 'password');

    $request = GET $url;
    print "--Performing request now...-----------\n";
    $response = $ua->request($request);
    print "--Done with request-------------------\n";

    if ($response->is_success) {
        print "It worked!->", $response->code, "\n";
    }
    else {
        print "It didn't work!->", $response->code, "\n";
    }

で,LWP::UserAgent のソースを眺めてみると分かったんですけど,LWP::UserAgent では HTTP サーバが返すヘッダによって,認証用のモジュールを動的に読み込むようになってるんですね.LWP::UserAgent 使うんなら知っとけよって話なのかもしれませんが,今知りました.

具体的には,LWP::UserAgent::request の中に,こんな部分がある.必要そうな部分だけ抜き出しますね.日本語のコメントは勝手に今ぼくがつけた.

    elsif ($code == &HTTP::Status::RC_UNAUTHORIZED ||
	     $code == &HTTP::Status::RC_PROXY_AUTHENTICATION_REQUIRED
	    )     # 認証を要求されたら:

	my $proxy = ($code == &HTTP::Status::RC_PROXY_AUTHENTICATION_REQUIRED);
	my $ch_header = $proxy ?  "Proxy-Authenticate" : "WWW-Authenticate";
	my @challenge = $response->header($ch_header);  # 認証用のヘッダを取り出して

	CHALLENGE: for my $challenge (@challenge) {
	    $challenge =~ tr/,/;/;  # "," is used to separate auth-params!!
	    ($challenge) = HTTP::Headers::Util::split_header_words($challenge);
	    my $scheme = lc(shift(@$challenge)); # 何の認証が使われるかを読み取って

	    my $class = "LWP::Authen::\u$scheme"; # 何のモジュールを使うのかを設定して
	    $class =~ s/-/_/g;

	    no strict 'refs';
	    unless (%{"$class\::"}) {
		# try to load it
		eval "require $class";  # モジュールをロードする

まぁこんな部分があって,LWP::UserAgent が勝手に適切な認証モジュールを読み込んでくれる仕様になってた.拡張性に関心した! スゴい! エラい!