使超时适用于 LWP::UserAgent HTTPS
解决方案
正如 @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
现在用于 timeout
和 Timeout
参数。前者是 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是
Net::HTTPS
模块使用 noop 覆盖IO::Socket
的blocking
方法的结果。升级到最新的Net::HTTP
软件包 应该解决这个问题。This is a result of the
Net::HTTPS
module overriding theblocking
method ofIO::Socket
with a noop. Upgrading to the latestNet::HTTP
package should fix this.timeout
(和Timeout
)选项仅适用于连接 - 连接时LWP::UserAgent
等待多少秒 - 它们是不是为整个事务设置超时。您需要使用 Perl 的
alarm
和$SIG{ALRM}
处理程序来使整个事务超时。请参阅perldoc -f Alarm
或perlipc
。(在 Linux 上测试。Windows/Cygwin 中的警报可能会更烦人)
The
timeout
(andTimeout
) options apply only to the connection -- how many seconds willLWP::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. Seeperldoc -f alarm
orperlipc
.(tested on Linux. Alarms can be a bit more cantankerous in Windows/Cygwin)
我在 PerlMonks 上问了这个问题,收到答复,大意是:
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:
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.