Acme-Sort-Sleep
view release on metacpan or search on metacpan
local/lib/perl5/IO/Async/Resolver.pm view on Meta::CPAN
# You may distribute under the terms of either the GNU General Public License
# or the Artistic License (the same terms as Perl itself)
#
# (C) Paul Evans, 2007-2015 -- leonerd@leonerd.org.uk
package IO::Async::Resolver;
use strict;
use warnings;
use base qw( IO::Async::Function );
our $VERSION = '0.70';
# Socket 2.006 fails to getaddrinfo() AI_NUMERICHOST properly on MSWin32
use Socket 2.007 qw(
AI_NUMERICHOST AI_PASSIVE
NI_NUMERICHOST NI_NUMERICSERV NI_DGRAM
EAI_NONAME
);
use IO::Async::OS;
# Try to use HiRes alarm, but we don't strictly need it.
# MSWin32 doesn't implement it
BEGIN {
require Time::HiRes;
eval { Time::HiRes::alarm(0) } and Time::HiRes->import( qw( alarm ) );
}
use Carp;
my $started = 0;
my %METHODS;
=head1 NAME
C<IO::Async::Resolver> - performing name resolutions asynchronously
=head1 SYNOPSIS
This object is used indirectly via an L<IO::Async::Loop>:
use IO::Async::Loop;
my $loop = IO::Async::Loop->new;
$loop->resolver->getaddrinfo(
host => "www.example.com",
service => "http",
)->on_done( sub {
foreach my $addr ( @_ ) {
printf "http://www.example.com can be reached at " .
"socket(%d,%d,%d) + connect('%v02x')\n",
@{$addr}{qw( family socktype protocol addr )};
}
});
$loop->resolve( type => 'getpwuid', data => [ $< ] )
->on_done( sub {
print "My passwd ent: " . join( "|", @_ ) . "\n";
});
$loop->run;
=head1 DESCRIPTION
This module extends an L<IO::Async::Loop> to use the system's name resolver
functions asynchronously. It provides a number of named resolvers, each one
providing an asynchronous wrapper around a single resolver function.
Because the system may not provide asynchronous versions of its resolver
functions, this class is implemented using a L<IO::Async::Function> object
that wraps the normal (blocking) functions. In this case, name resolutions
will be performed asynchronously from the rest of the program, but will likely
be done by a single background worker process, so will be processed in the
order they were requested; a single slow lookup will hold up the queue of
other requests behind it. To mitigate this, multiple worker processes can be
used; see the C<workers> argument to the constructor.
The C<idle_timeout> parameter for the underlying L<IO::Async::Function> object
is set to a default of 30 seconds, and C<min_workers> is set to 0. This
ensures that there are no spare processes sitting idle during the common case
of no outstanding requests.
=cut
sub _init
{
my $self = shift;
my ( $params ) = @_;
$self->SUPER::_init( @_ );
$params->{code} = sub {
my ( $type, $timeout, @data ) = @_;
if( my $code = $METHODS{$type} ) {
local $SIG{ALRM} = sub { die "Timed out\n" };
alarm( $timeout );
my @ret = eval { $code->( @data ) };
alarm( 0 );
die $@ if $@;
return @ret;
}
else {
die "Unrecognised resolver request '$type'";
}
};
$params->{idle_timeout} = 30;
$params->{min_workers} = 0;
$started = 1;
}
=head1 METHODS
The following methods documented with a trailing call to C<< ->get >> return
L<Future> instances.
=cut
=head2 resolve
@result = $loop->resolve( %params )->get
Performs a single name resolution operation, as given by the keys in the hash.
The C<%params> hash keys the following keys:
=over 8
=item type => STRING
Name of the resolution operation to perform. See BUILT-IN RESOLVERS for the
list of available operations.
=item data => ARRAY
Arguments to pass to the resolver function. Exact meaning depends on the
specific function chosen by the C<type>; see BUILT-IN RESOLVERS.
=item timeout => NUMBER
Optional. Timeout in seconds, after which the resolver operation will abort
with a timeout exception. If not supplied, a default of 10 seconds will apply.
=back
On failure, the fail category name is C<resolve>; the details give the
individual resolver function name (e.g. C<getaddrinfo>), followed by other
error details specific to the resolver in question.
->fail( $message, resolve => $type => @details )
=head2 resolve (void)
$resolver->resolve( %params )
When not returning a future, additional parameters can be given containing the
continuations to invoke on success or failure:
=over 8
=item on_resolved => CODE
A continuation that is invoked when the resolver function returns a successful
result. It will be passed the array returned by the resolver function.
$on_resolved->( @result )
=item on_error => CODE
local/lib/perl5/IO/Async/Resolver.pm view on Meta::CPAN
else {
my $future = $self->loop->new_future->done( $host, $service );
$future->on_done( $args{on_resolved} ) if $args{on_resolved};
return $future;
}
}
my $future = $self->resolve(
type => "getnameinfo",
data => [ $args{addr}, $flags ],
timeout => $args{timeout},
)->transform(
done => sub { @{ $_[0] } }, # unpack the ARRAY ref
);
$future->on_done( $args{on_resolved} ) if $args{on_resolved};
$future->on_fail( $args{on_error} ) if $args{on_error};
return $future if defined wantarray;
# Caller is not going to keep hold of the Future, so we have to ensure it
# stays alive somehow
$self->adopt_future( $future->else( sub { Future->done } ) );
}
=head1 FUNCTIONS
=cut
=head2 register_resolver( $name, $code )
Registers a new named resolver function that can be called by the C<resolve>
method. All named resolvers must be registered before the object is
constructed.
=over 8
=item $name
The name of the resolver function; must be a plain string. This name will be
used by the C<type> argument to the C<resolve> method, to identify it.
=item $code
A CODE reference to the resolver function body. It will be called in list
context, being passed the list of arguments given in the C<data> argument to
the C<resolve> method. The returned list will be passed to the
C<on_resolved> callback. If the code throws an exception at call time, it will
be passed to the C<on_error> continuation. If it returns normally, the list of
values it returns will be passed to C<on_resolved>.
=back
=cut
# Plain function, not a method
sub register_resolver
{
my ( $name, $code ) = @_;
croak "Cannot register new resolver methods once the resolver has been started" if $started;
croak "Already have a resolver method called '$name'" if exists $METHODS{$name};
$METHODS{$name} = $code;
}
=head1 BUILT-IN RESOLVERS
The following resolver names are implemented by the same-named perl function,
taking and returning a list of values exactly as the perl function does:
getpwnam getpwuid
getgrnam getgrgid
getservbyname getservbyport
gethostbyname gethostbyaddr
getnetbyname getnetbyaddr
getprotobyname getprotobynumber
=cut
# Now register the inbuilt methods
register_resolver getpwnam => sub { my @r = getpwnam( $_[0] ) or die "$!\n"; @r };
register_resolver getpwuid => sub { my @r = getpwuid( $_[0] ) or die "$!\n"; @r };
register_resolver getgrnam => sub { my @r = getgrnam( $_[0] ) or die "$!\n"; @r };
register_resolver getgrgid => sub { my @r = getgrgid( $_[0] ) or die "$!\n"; @r };
register_resolver getservbyname => sub { my @r = getservbyname( $_[0], $_[1] ) or die "$!\n"; @r };
register_resolver getservbyport => sub { my @r = getservbyport( $_[0], $_[1] ) or die "$!\n"; @r };
register_resolver gethostbyname => sub { my @r = gethostbyname( $_[0] ) or die "$!\n"; @r };
register_resolver gethostbyaddr => sub { my @r = gethostbyaddr( $_[0], $_[1] ) or die "$!\n"; @r };
register_resolver getnetbyname => sub { my @r = getnetbyname( $_[0] ) or die "$!\n"; @r };
register_resolver getnetbyaddr => sub { my @r = getnetbyaddr( $_[0], $_[1] ) or die "$!\n"; @r };
register_resolver getprotobyname => sub { my @r = getprotobyname( $_[0] ) or die "$!\n"; @r };
register_resolver getprotobynumber => sub { my @r = getprotobynumber( $_[0] ) or die "$!\n"; @r };
=pod
The following three resolver names are implemented using the L<Socket> module.
getaddrinfo
getaddrinfo_array
getnameinfo
The C<getaddrinfo> resolver takes arguments in a hash of name/value pairs and
returns a list of hash structures, as the C<Socket::getaddrinfo> function
does. For neatness it takes all its arguments as named values; taking the host
and service names from arguments called C<host> and C<service> respectively;
all the remaining arguments are passed into the hints hash. This name is also
aliased as simply C<getaddrinfo>.
The C<getaddrinfo_array> resolver behaves more like the C<Socket6> version of
the function. It takes hints in a flat list, and mangles the result of the
function, so that the returned value is more useful to the caller. It splits
up the list of 5-tuples into a list of ARRAY refs, where each referenced array
contains one of the tuples of 5 values.
( run in 0.782 second using v1.01-cache-2.11-cpan-39bf76dae61 )