LWP::UserAgentでHTTPSプロキシ越しに通信する

  • 8
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

概要

LWP::UserAgentでプロキシ越しにHTTPS通信をやろうとすると、何も考えずに設定すると

GET https://example.com/index.html

のように、CONNECTメソッドを使わずhttpsスキームのリクエストURLをそのままプロキシに投げてしまう。
このようなリクエストはSquidなどのプロキシではサポートされておらず、エラーになってしまう。
(Apache mod_proxyならイケるっぽい?)

CONNECTメソッドによる通信をやらせるには、いろいろと環境変数を設定する必要がある。

参考

割と以前から認識されている問題なのになぜかドハマりしてしまったのでここに書き留めておきます。

LWP::Protocol::connectを使う解決策はこちら → LWP::UserAgentでHTTPSプロキシ越しに通信する (LWP::Protocol::connect編)

検証バージョン

  • Ubuntu 12.04.1
  • perl 5.14.2
  • LWP::UserAgent 6.04
  • Crypt::SSLeay 0.64
  • Net::SSL 2.85
  • Net::Twitter 4.00003

サンプルコード

lwp_https_proxy_sample.pl
use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request;

## ** See https://metacpan.org/module/Net::HTTPS
## Force use of Net::SSL instead of IO::Socket::SSL
$ENV{PERL_NET_HTTPS_SSL_SOCKET_CLASS} = 'Net::SSL';

## ** See https://metacpan.org/module/Crypt::SSLeay#ENVIRONMENT-VARIABLES
$ENV{HTTPS_PROXY} = 'http://your-https-proxy:8080';
$ENV{HTTPS_CA_DIR} = '/etc/ssl/certs';
## $ENV{HTTPS_DEBUG} = 1; ## ** Uncomment this for debug output


## ** See https://metacpan.org/module/LWP#ENVIRONMENT
## ** Uncomment this to disable server verification.
## ** You usually shouldn't do this, though.
## $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;

my $ua = LWP::UserAgent->new;

## ** Do NOT call env_proxy(). This enables LWP to handle HTTPS requests.
## $ua->env_proxy;

my $req = HTTP::Request->new('GET', 'https://metacpan.org/module/LWP::UserAgent');
my $res = $ua->request($req);

print $res->as_string, "\n";
  • CONNECTメソッドを使うためには、プロキシ処理をLWP::UserAgentではなくCrypt::SSLeayに任せる必要がある。
  • そのために、LWP::UserAgentではプロキシ設定を行わない
  • env_proxy()メソッドも呼んではいけない
  • Crypt::SSLeayにプロキシ設定を伝えるには環境変数を使う。
  • その際、環境変数HTTPS_CA_DIRで認証局証明書ディレクトリを指定しないとサーバ証明書の検証でエラーになる。
  • さらに、環境変数PERL_NET_HTTPS_SSL_SOCKET_CLASSNet::SSLに設定しないと、そもそもCrypt::SSLeayが使われないようだ。
    • LWP::Protocol::httpsNet::HTTPSを使っており、Net::HTTPSIO::Socket::SSLを優先的に使い、環境変数などで指定された場合にNet::SSLを使うから。

LWP::UserAgentを使う別のモジュールを使う場合

例えば、Net::TwitterなんかはLWP::UserAgentを内製して内部でenv_proxy()を呼んでしまう。

Net::Twitterの場合は幸い、使用するUserAgentクラスをカスタマイズできるので、以下のようにenv_proxy()を無力化するクラスを作って与えればいいと思う。

lwp_useragent_noenv.pl
package LWP::UserAgent::NoEnv;
use strict;
use warnings;
use base ('LWP::UserAgent');

sub env_proxy {
    print STDERR "Fake env_proxy called.\n";
}
1;

そういうカスタマイズのポイントがないモジュールの場合は・・・、LWP::UserAgent自体を外から上書きするのかなあ・・・。うーむ。