1. debug-ito
Changes in body
Source | HTML | Preview
@@ -1,99 +1,103 @@
# 概要
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編)](http://qiita.com/debug-ito@github/items/c621aecca275b241053f)
# 検証バージョン
- Ubuntu 12.04.1
- perl 5.14.2
- LWP::UserAgent 6.04
- Crypt::SSLeay 0.64
- Net::SSL 2.85
- Net::Twitter 4.00003
# サンプルコード
```perl: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;
+## ** 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()`メソッドも**呼んではいけない**
+- `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()`を無力化するクラスを作って与えればいいと思う。
```perl: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自体を外から上書きするのかなあ・・・。うーむ。