概要
LWP::UserAgent(バージョン6.06未満)でプロキシ越しにHTTPS通信をやろうとすると、何も考えずに設定すると
GET https://example.com/index.html
のように、CONNECTメソッドを使わずhttpsスキームのリクエストURLをそのままプロキシに投げてしまう。
このようなリクエストはSquidなどのプロキシではサポートされておらず、エラーになってしまう。
(Apache mod_proxyならイケるっぽい?)
CONNECTメソッドによる通信をやらせるには、いろいろと環境変数を設定する必要がある。
参考
- http://stackoverflow.com/questions/12116244/https-proxy-and-lwpuseragent
- http://d.hatena.ne.jp/ragtarou/20070302
- http://perl-users.jp/articles/advent-calendar/2009/casual/17.html
- https://metacpan.org/module/Net::HTTPS
- https://metacpan.org/module/Crypt::SSLeay#ENVIRONMENT-VARIABLES
- https://metacpan.org/module/LWP#ENVIRONMENT
割と以前から認識されている問題なのになぜかドハマりしてしまったのでここに書き留めておきます。
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
サンプルコード
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;
## ** This lets LWP::UserAgent to handle HTTPS proxying, but that's wrong.
$ua->env_proxy;
## ** Explicitly disables HTTPS proxy by LWP::UserAgent.
## ** Just let Crypt::SSLeay do the job.
$ua->proxy(https => undef);
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()
メソッドを呼んでもいいが、その後に必ず$ua->proxy(https => undef)
としてプロキシ設定を消す。 - Crypt::SSLeayにプロキシ設定を伝えるには環境変数を使う。
- その際、環境変数
HTTPS_CA_DIR
で認証局証明書ディレクトリを指定しないとサーバ証明書の検証でエラーになる。 - さらに、環境変数
PERL_NET_HTTPS_SSL_SOCKET_CLASS
をNet::SSL
に設定しないと、そもそもCrypt::SSLeayが使われないようだ。-
LWP::Protocol::https
がNet::HTTPS
を使っており、Net::HTTPS
はIO::Socket::SSL
を優先的に使い、環境変数などで指定された場合にNet::SSL
を使うから。
-
LWP::UserAgentを使う別のモジュールを使う場合
例えば、Net::TwitterなんかはLWP::UserAgentを内製して内部でenv_proxy()
を呼んでしまう。
Net::Twitterの場合は幸い、使用するUserAgentクラスをカスタマイズできるので、以下のようにenv_proxy()
を無力化するクラスを作って与えればいいと思う。
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自体を外から上書きするのかなあ・・・。うーむ。