EV-cares
view release on metacpan or search on metacpan
lib/EV/cares.pm view on Meta::CPAN
return;
}
sub reverse_all {
my ($self, $ips, $cb) = @_;
require Carp;
Carp::croak("reverse_all: first argument must be an arrayref")
unless ref $ips eq 'ARRAY';
Carp::croak("reverse_all: callback must be a CODE reference")
unless ref $cb eq 'CODE';
Carp::croak("reverse_all: resolver is destroyed") if $self->is_destroyed;
return $cb->({}) unless @$ips;
my %seen;
my @unique = grep { !$seen{$_}++ } @$ips;
# validate every IP upfront. reverse() croaks on invalid input; if we
# discovered that mid-loop we would have already dispatched queries 1..k
# whose inner callbacks hold $cb and decrement $pending, but $pending
# would never reach 0 (we never dispatched k+1..N) -- the completion
# callback would be silently orphaned.
require Socket;
for my $ip (@unique) {
Carp::croak("reverse_all: invalid IP '$ip'")
unless Socket::inet_pton(Socket::AF_INET(), $ip)
|| Socket::inet_pton(Socket::AF_INET6(), $ip);
}
my %res;
my $pending = @unique;
for my $ip (@unique) {
$self->reverse($ip, sub {
my ($status, @hosts) = @_;
$res{$ip} = { status => $status, hosts => \@hosts };
$cb->(\%res) if --$pending == 0;
});
}
return;
}
sub getaddrinfo_all {
my ($self, $nodes, $service, $hints, $cb) = @_;
require Carp;
Carp::croak("getaddrinfo_all: first argument must be an arrayref")
unless ref $nodes eq 'ARRAY';
Carp::croak("getaddrinfo_all: callback must be a CODE reference")
unless ref $cb eq 'CODE';
Carp::croak("getaddrinfo_all: resolver is destroyed") if $self->is_destroyed;
return $cb->({}) unless @$nodes;
my %seen;
my @unique = grep { !$seen{$_}++ } @$nodes;
my %res;
my $pending = @unique;
for my $node (@unique) {
$self->getaddrinfo($node, $service, $hints, sub {
my ($status, @addrs) = @_;
$res{$node} = { status => $status, addrs => \@addrs };
$cb->(\%res) if --$pending == 0;
});
}
return;
}
# is_busy: true iff there are pending queries on this resolver. Cheap
# wrapper for the most common active_queries comparison.
sub is_busy { $_[0]->active_queries > 0 }
# wait_idle($timeout_seconds): pump the EV loop until either all of this
# resolver's pending queries complete or the timeout elapses. Returns
# true if the channel drained, false on timeout. Useful in mostly-
# synchronous scripts that want to ensure callbacks have run before
# proceeding. Returns immediately if the resolver is already idle.
#
# Picks up a custom EV::Loop passed to new(loop => $loop): the timer and
# the run() call are dispatched on the same loop the resolver's watchers
# are armed on. Without this, custom-loop resolvers would hang because
# EV::run/EV::timer always target the default loop.
sub wait_idle {
my ($self, $timeout) = @_;
require Carp;
Carp::croak("wait_idle: resolver is destroyed") if $self->is_destroyed;
return 1 unless $self->active_queries;
$timeout //= 30;
my $expired;
my $loop = $self->loop // EV::default_loop;
my $timer = $loop->timer($timeout, 0, sub { $expired = 1 });
while ($self->active_queries && !$expired) {
$loop->run(EV::RUN_ONCE);
}
# Look at the resolver's state rather than $expired: if the timer and
# the last query callback both fire in the same RUN_ONCE iteration,
# $expired is set but the channel did drain. Don't lie about that.
return $self->active_queries ? 0 : 1;
}
sub search_all {
my ($self, $names, $type, $class_or_cb, $cb) = @_;
require Carp;
# search_all($names, $type, $cb) -> 4 args (class default C_IN)
# search_all($names, $type, $class, $cb) -> 5 args
my $class;
if (@_ == 4) {
$cb = $class_or_cb;
} elsif (@_ == 5) {
$class = $class_or_cb;
require Scalar::Util;
Carp::croak("search_all: class must be an integer (C_IN, C_CHAOS, ...)")
unless Scalar::Util::looks_like_number($class);
} else {
Carp::croak("search_all: usage: \$r->search_all(\\\@names, \$type, [\$class,] \$cb)");
}
Carp::croak("search_all: first argument must be an arrayref")
unless ref $names eq 'ARRAY';
Carp::croak("search_all: callback must be a CODE reference")
unless ref $cb eq 'CODE';
Carp::croak("search_all: resolver is destroyed") if $self->is_destroyed;
return $cb->({}) unless @$names;
my %seen;
my @unique = grep { !$seen{$_}++ } @$names;
my %res;
my $pending = @unique;
for my $name (@unique) {
my $inner = sub {
my ($status, @records) = @_;
$res{$name} = { status => $status, records => \@records };
$cb->(\%res) if --$pending == 0;
lib/EV/cares.pm view on Meta::CPAN
{ host => '1.1.1.1' },
{ host => '8.8.8.8', port => 53 },
]);
Replace the DNS server list. Accepts a flat list, an arrayref of
strings (each may be C<"host:port">), or an arrayref of
C<< { host => ..., port => ... } >> hashrefs. Croaks if no server is
given.
=head2 set_sortlist
$r->set_sortlist('192.168.0.0/255.255.0.0 ::1/128');
Set the address-sortlist for ordering returned addresses. See c-ares'
C<ares_set_sortlist> for the format (CIDR / netmask pairs separated by
whitespace). Croaks on parse error.
=head2 servers
my $csv = $r->servers; # "8.8.8.8,1.1.1.1"
Returns the current server list as a comma-separated string.
=head2 set_local_dev
$r->set_local_dev('eth0');
Bind outgoing queries to a network device.
=head2 set_local_ip4
$r->set_local_ip4('192.168.1.100');
Bind outgoing queries to a local IPv4 address.
=head2 set_local_ip6
$r->set_local_ip6('::1');
Bind outgoing queries to a local IPv6 address.
=head2 loop
my $loop = $r->loop; # EV::Loop ref, or undef for default loop
Returns the C<EV::Loop> the resolver's watchers are armed on, as passed
to C<new(loop =E<gt> ...)>. Returns C<undef> when the resolver runs on
EV's default loop, and also C<undef> after C<destroy> (the loop
reference is released as part of cleanup). Read-only.
=head2 active_queries
my $n = $r->active_queries;
Returns the number of outstanding queries. Remains callable after
C<destroy>; returns C<0> in that case (during interpreter global
destruction the count may reflect whatever was pending, since
C<ares_destroy> is intentionally skipped on the global-destruction
path).
=head2 is_busy
if ($r->is_busy) { ... }
Convenience wrapper for C<< $r->active_queries > 0 >>.
=head2 wait_idle
my $drained = $r->wait_idle; # default 30s timeout
my $drained = $r->wait_idle($seconds);
Pumps the EV loop until every pending query on this resolver has fired
its callback, or until the timeout elapses. Returns true if the
channel drained, false on timeout. Returns immediately when the
resolver is already idle. Croaks on a destroyed resolver.
Intended for mostly-synchronous scripts that issue a batch of queries
and want to ensure their callbacks have run before continuing. Inside
a long-running event-driven program you generally don't need this -- let
the existing C<EV::run> drive callbacks.
Must not be called recursively on the same resolver: invoking
C<wait_idle> from inside a query callback whose enclosing C<wait_idle>
is still pumping the loop can let the outer timeout timer fire during
the nested pump and report a spurious timeout.
=head2 is_destroyed
if ($r->is_destroyed) { ... }
Returns 1 if C<destroy> has been called on this resolver, 0 otherwise.
Useful in long-running daemons that want to skip work without croaking
on a torn-down channel. Remains callable after C<destroy>.
=head2 next_timeout
my $secs = $r->next_timeout;
Returns the seconds until c-ares' next internal timer (e.g. retry
window for an in-flight query), or C<-1> if no timer is pending.
Useful for wiring EV::cares into custom scheduling or for diagnosing
a slow upstream. Croaks on a destroyed resolver.
=head2 last_query_timeouts
my $n = $r->last_query_timeouts;
Returns the c-ares retry/timeout count of the most recently completed
callback. Useful for tuning per-server timeouts; note that with
multiple in-flight queries this is whichever callback fired most
recently and races accordingly. Remains callable after C<destroy>.
=head2 reinit
$r->reinit;
Re-read system DNS configuration (resolv.conf, hosts file) without
destroying the channel. Useful for long-running daemons where the
resolver configuration may change at runtime.
=head2 destroy
$r->destroy;
( run in 0.463 second using v1.01-cache-2.11-cpan-995e09ba956 )