Я пытаюсь написать несколько относительно простых библиотечных функций для эмуляции метода LWP::UserAgent
get
, потому что LWP и связанные библиотеки недоступны на некоторых наших хостингах. Все, на что я могу положиться - это основные функции Perl, и даже некоторые из них ограничены, но у меня, похоже, есть доступ к сокетам, разветвлениям, сигналам и т. П.
Мне уже удалось создать простой клиент и сервер (сервер только для тестирования), который может отправлять и получать данные. Проблема в том, что я хочу установить тайм-аут на всю операцию get
, как в LWP, но мои первоначальные попытки были безрезультатными. Следующее не работает, и я не верю, что это может работать, но я опубликую это только в случае, если это может быть исправлено:
sub grab {
my($addr, $port, $timeout) = @_;
my $it;
eval {
local $SIG{ALRM} = sub {
die "alarm\n";
};
alarm $timeout if $timeout;
my $iaddr = inet_aton($addr)
or die "client no host: $!";
my $paddr = sockaddr_in($port, $iaddr)
or die "client sockaddr_in: $!";
my $proto = getprotobyname("tcp");
socket(Client, PF_INET, SOCK_STREAM, $proto)
or die "Client socket: $!";
local $SIG{ALRM} = sub {
close(Client);
die "alarm\n";
};
connect(Client, $paddr)
or die "Client connect: $!";
while(my $line = <Client>) {
$it .= $line;
}
print alarm(0), " seconds left \n";
close(Client) or die "Client close: $!";
};
if($@) {
die unless $@ eq "alarm\n";
}
return $it;
}
Сигнал тревоги, кажется, игнорируется подобными соединениями, чтением и, возможно, некоторыми другими. Я прибег к чтению исходного кода LWP после того, как это не сработало - потому что я чувствовал, что я лаю не на том дереве - и обнаружил следующее, среди других драгоценных камней, в strawberry/perl/vendor/lib/LWP/Protocol/http.pm
:
sub sysread {
my $self = shift;
if (my $timeout = ${*$self}{io_socket_timeout}) {
die "read timeout" unless $self->can_read($timeout);
}
else {
# since we have made the socket non-blocking we
# use select to wait for some data to arrive
$self->can_read(undef) || die "Assert";
}
sysread($self, $_[0], $_[1], $_[2] || 0);
}
sub can_read {
my($self, $timeout) = @_;
my $fbits = '';
vec($fbits, fileno($self), 1) = 1;
SELECT:
{
my $before;
$before = time if $timeout;
my $nfound = select($fbits, undef, undef, $timeout);
if ($nfound < 0) {
if ($!{EINTR} || $!{EAGAIN}) {
# don't really think EAGAIN can happen here
if ($timeout) {
$timeout -= time - $before;
$timeout = 0 if $timeout < 0;
}
redo SELECT;
}
die "select failed: $!";
}
return $nfound > 0;
}
}
Так что похоже, что он работает вокруг некоторых ограничений других подпрограмм при использовании select? Он также, кажется, не разветвляется и не использует сигналы, и, строго говоря, время от времени блокирует, но пытается убедиться, что он не будет блокироваться долго? Я чувствую, что должен скопировать суть этого кода и создать упрощенную версию для своих конкретных нужд, но я начинаю довольно осторожно сталкиваться с минными полями. Также обратите внимание, что я занимаюсь разработкой под Windows, но в будущем буду развертывать на Linux / nix *, а также, возможно, на Windows.