使超时适用于 LWP::UserAgent HTTPS

发布于 2025-01-08 02:44:01 字数 2535 浏览 1 评论 0原文

解决方案

正如 @limulus 在我接受的答案中所报告的,这是 Net::HTTPS 6.00 版中的一个错误。始终对新的 .0 版本保持警惕。以下是该模块的错误版本和修复版本之间的相关差异:

D:\Opt\Perl512.32 :: diff lib\Net\HTTPS.pm site\lib\Net\HTTPS.pm
6c6
< $VERSION = "6.00";
---
> $VERSION = "6.02";
75,78c75,80
< # The underlying SSLeay classes fails to work if the socket is
< # placed in non-blocking mode.  This override of the blocking
< # method makes sure it stays the way it was created.
< sub blocking { }  # noop
---
> if ($SSL_SOCKET_CLASS eq "Net::SSL") {
>     # The underlying SSLeay classes fails to work if the socket is
>     # placed in non-blocking mode.  This override of the blocking
>     # method makes sure it stays the way it was created.
>     *blocking = sub { };
> }

原始问题

相关性:看到 HTTPS 客户端无限期地阻止是很烦人的,因为连接端点不可靠。

这个实验很容易在家里设置和重播。您只需要两件事:一个用于捕获传入客户端的 tarpit 和一个 Perl 脚本。可以使用 netcat 设置 tarpit:

nc -k -l localhost 9999 # on Linux, for multiple requests
nc -l -p 9999 localhost # on Cygwin, for one request only

然后,将脚本指向此 tarpit:

use strict;
use LWP::UserAgent;
use HTTP::Request::Common;

print 'LWP::UserAgent::VERSION  ', $LWP::UserAgent::VERSION, "\n";
print 'IO::Socket::SSL::VERSION ', $IO::Socket::SSL::VERSION, "\n";

my $ua = LWP::UserAgent->new( timeout => 5, keep_alive => 1 );
$ua->ssl_opts( timeout => 5, Timeout => 5 ); # Yes - see note below!
my $rsp = $ua->request( GET 'https://localhost:9999' );
if ( $rsp->is_success ) {
  print $rsp->as_string;
} else {
  die $rsp->status_line;
}

这将做什么?好吧,连接到NetCat打开的端口,然后……挂掉。无限期。至少在开发时间方面是这样。我的意思是它可能会在十分钟或两个小时后超时,但我没有检查过;指定的超时不生效,在Linux上不生效,在Windows上也不生效(Win32,未检查Cygwin)。

使用的版本:

LWP::UserAgent::VERSION  6.02
IO::Socket::SSL::VERSION 1.44
# on Linux

LWP::UserAgent::VERSION  6.02
IO::Socket::SSL::VERSION 1.44
# on Win32

现在用于 timeoutTimeout 参数。前者是 LWP::UA 的参数名称,后者是名称对于 IO::Socket::SSL,通过 LWP::Protocol::https。 (顺便说一句,为什么metacpan是HTTPS?好吧,至少它不是tarpit。)我不知何故希望传递这些参数:)

只是让你知道,keep_alive没有任何事情可做由于超时不起作用,我凭经验验证了这一点。 :)

无论如何,在深入研究之前,有谁知道这里发生了什么以及如何使超时与 HTTPS 一起工作?很难相信我是第一个遇到这种情况的人。

Solution

As reported by @limulus in the answer I accepted, this was a bug in Net::HTTPS version 6.00. Always be wary of fresh .0 releases. Here's the relevant diff between the buggy and fixed version of that module:

D:\Opt\Perl512.32 :: diff lib\Net\HTTPS.pm site\lib\Net\HTTPS.pm
6c6
< $VERSION = "6.00";
---
> $VERSION = "6.02";
75,78c75,80
< # The underlying SSLeay classes fails to work if the socket is
< # placed in non-blocking mode.  This override of the blocking
< # method makes sure it stays the way it was created.
< sub blocking { }  # noop
---
> if ($SSL_SOCKET_CLASS eq "Net::SSL") {
>     # The underlying SSLeay classes fails to work if the socket is
>     # placed in non-blocking mode.  This override of the blocking
>     # method makes sure it stays the way it was created.
>     *blocking = sub { };
> }

Original question

Relevance: It is annoying to see your HTTPS client block indefinitely because the connection endpoint is unreliable.

This experiment is easy to set up and replay at home. You just need two things, a tarpit to trap an incoming client, and a Perl script. The tarpit can be set up using netcat:

nc -k -l localhost 9999 # on Linux, for multiple requests
nc -l -p 9999 localhost # on Cygwin, for one request only

Then, point the script to this tarpit:

use strict;
use LWP::UserAgent;
use HTTP::Request::Common;

print 'LWP::UserAgent::VERSION  ', $LWP::UserAgent::VERSION, "\n";
print 'IO::Socket::SSL::VERSION ', $IO::Socket::SSL::VERSION, "\n";

my $ua = LWP::UserAgent->new( timeout => 5, keep_alive => 1 );
$ua->ssl_opts( timeout => 5, Timeout => 5 ); # Yes - see note below!
my $rsp = $ua->request( GET 'https://localhost:9999' );
if ( $rsp->is_success ) {
  print $rsp->as_string;
} else {
  die $rsp->status_line;
}

What is this going to do? Well, connect to the port opened by NetCat, and then ... hang. Indefinitely. At least in terms of developer time. I mean it might time out after ten minutes or two hours, but I haven't checked; the specified timeout doesn't take effect, not on Linux, and not on Windows (Win32, haven't checked Cygwin).

Versions used:

LWP::UserAgent::VERSION  6.02
IO::Socket::SSL::VERSION 1.44
# on Linux

LWP::UserAgent::VERSION  6.02
IO::Socket::SSL::VERSION 1.44
# on Win32

Now for the timeout and Timeout parameters. The former is the name of the parameter for LWP::UA, the latter is the name for IO::Socket::SSL, used via LWP::Protocol::https. (Incidentally, why is metacpan HTTPS? Well, at least it's not a tarpit.) I am somehow hoping to have these parameters passed along :)

Just so you know, keep_alive doesn't have anything to do with the timeout not working, I verified that empirically. :)

Anyway, before digging deeper, does anyone know what's going on here and how to make the timeout work with HTTPS? Hard to believe I'm the first person running into this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

朕就是辣么酷 2025-01-15 02:44:01

这是 Net::HTTPS 模块使用 noop 覆盖 IO::Socketblocking 方法的结果。升级到最新的Net::HTTP软件包 应该解决这个问题。

This is a result of the Net::HTTPS module overriding the blocking method of IO::Socket with a noop. Upgrading to the latest Net::HTTP package should fix this.

欲拥i 2025-01-15 02:44:01

timeout(和Timeout)选项仅适用于连接 - 连接时 LWP::UserAgent 等待多少秒 - 它们是不是为整个事务设置超时。

您需要使用 Perl 的 alarm$SIG{ALRM} 处理程序来使整个事务超时。请参阅 perldoc -f Alarmperlipc

local $SIG{ALRM} = sub { die "SSL timeout\n" };
my $ua = LWP::UserAgent->new( timeout => 5, keep_alive => 1 );
$ua->ssl_opts( timeout => 5, Timeout => 5 );

eval {
    alarm(10);
    my $rsp = $ua->request( GET 'https://localhost:9999' );
    if ( $rsp->is_success ) {
      print $rsp->as_string;
    } else {
      die $rsp->status_line;
    }
 };
 alarm(0);
 if ($@) {
     if ($@ =~ /SSL timeout/) {
         warn "request timed out";
     } else {
         die "error in request: $@";
     }
 }

(在 Linux 上测试。Windows/Cygwin 中的警报可能会更烦人

The timeout (and Timeout) options apply only to the connection -- how many seconds will LWP::UserAgent wait while connecting -- they are not for setting a timeout on the whole transaction.

You'll want to use Perl's alarm with a $SIG{ALRM} handler to timeout the whole transaction. See perldoc -f alarm or perlipc.

local $SIG{ALRM} = sub { die "SSL timeout\n" };
my $ua = LWP::UserAgent->new( timeout => 5, keep_alive => 1 );
$ua->ssl_opts( timeout => 5, Timeout => 5 );

eval {
    alarm(10);
    my $rsp = $ua->request( GET 'https://localhost:9999' );
    if ( $rsp->is_success ) {
      print $rsp->as_string;
    } else {
      die $rsp->status_line;
    }
 };
 alarm(0);
 if ($@) {
     if ($@ =~ /SSL timeout/) {
         warn "request timed out";
     } else {
         die "error in request: $@";
     }
 }

(tested on Linux. Alarms can be a bit more cantankerous in Windows/Cygwin)

胡渣熟男 2025-01-15 02:44:01

我在 PerlMonks 上问了这个问题收到答复,大意是:

底层 IO::Socket::INET 不支持非阻塞套接字
在 Win32 上,因此 Win32 上不支持非阻塞 IO::Socket::SSL,
这也意味着超时不起作用(因为它们基于
非阻塞)。另请参阅 http://www.perlmonks.org/?node_id=378675

http://cpansearch.perl.org/src/SULLR/IO-Socket-SSL-1.60/README.Win32

PerlMonks 帖子指出的是 2004 年。不确定该信息是否仍然适用;毕竟,我已经看到超时在 Windows 上确实有效,只是不是通过 SSL。

I asked this question on PerlMonks, and received an answer to the effect that:

The underlying IO::Socket::INET does not support non-blocking sockets
on Win32, thus non-blocking IO::Socket::SSL is not supported on Win32,
which means also, that timeouts don't work (because they are based on
non-blocking). See also http://www.perlmonks.org/?node_id=378675

http://cpansearch.perl.org/src/SULLR/IO-Socket-SSL-1.60/README.Win32

The PerlMonks post pointed to is from 2004. Not sure the information still applies; after all, I've seen the timeout does work on Windows, just not via SSL.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文