LWP::UserAgentでHTTPSプロキシ越しに通信する (LWP::Protocol::connect編)

  • 5
    Like
  • 0
    Comment

概要

LWP::UserAgent(バージョン6.06未満)でプロキシ越しにHTTPS通信をやろうとすると、何も考えずに設定すると

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

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

「LWP::UserAgentでHTTPSプロキシ越しに通信する」の投稿では、環境変数を設定することでCONNECTメソッドを有効にした。

今回は、LWP::Protocol::connectモジュールを使ってCONNECTメソッドを使う。

検証環境

  • Ubuntu 12.04.2
  • perl 5.14.2
  • IO::Socket::INET6 2.71
  • IO::Socket::IP 0.22
  • IO::Socket::SSL 1.953
  • LWP::UserAgent 6.05
  • LWP::Protocol::connect 6.03
  • LWP::Protocol::https 6.02
  • Net::HTTPS 6.04

サンプルコード

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

my $ua = LWP::UserAgent->new;
$ua->proxy('https', 'connect://localhost:3128/');

## ** If you want to disable peer verification (NOT RECOMMENDED)
## $ua->ssl_opts(verify_hostname => 0);

## ** If you want to explicitly specify the directory containing CA certificates.
## $ua->ssl_opts(SSL_ca_path => '/etc/ssl/certs');

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

print $res->as_string, "\n";

ポイント

LWP::Protocol::connectをインストールした状態で、

$ua->proxy('https', 'connect://PROXY_HOSTNAME:PORT');

を設定する。

LWP::Protocol::connectconnect://という独自スキームを使うので、これだけだと一般的な環境変数の設定を解釈できないのが玉にキズではある。

ハマりどころ

上記のサンプルコードを実行すると、以下のようなエラーが出ることがある。

Bad arg length for Socket6::unpack_sockaddr_in6, length is 16, should be 28 at /usr/lib/perl5/Socket6.pm line 282.

これはIO::Socket::INET6のバグ: https://rt.cpan.org/Public/Bug/Display.html?id=68282
(さらに元をたどるとIO::Socketのバグらしい)

IO::Socket::IPをインストールすると直る。これは、IO::Socket::SSLIO::Socket::IPを優先して使うため。

参考: 各モジュールの使役関係

  • 同じレベルの->「いずれかを選択して使う」 という意味
  • 継承関係を表すわけではないので注意!
  • 間違いがあったらご指摘お願いします
LWP::UserAgent 
-> LWP::Protocol::connect
   -> LWP::Protocol::https::connect
      -> LWP::Protocol::https
         -> Net::HTTPS
            -> IO::Socket::SSL
               -> IO::Socket::IP
               -> IO::Socket::INET6
               -> IO::Socket::INET
            -> Net::SSL
               -> IO::Socket::INET + Crypt::SSLeay