8
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

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

概要

LWP::UserAgent(バージョン6.06未満)でプロキシ越しに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;

## ** 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_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自体を外から上書きするのかなあ・・・。うーむ。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
8
Help us understand the problem. What are the problem?