File Coverage

blib/lib/IO/Async/Resolver/LibAsyncNS.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2014 -- leonerd@leonerd.org.uk
5              
6             package IO::Async::Resolver::LibAsyncNS;
7              
8 3     3   248366 use strict;
  3         7  
  3         124  
9 3     3   16 use warnings;
  3         5  
  3         104  
10 3     3   26 use base qw( IO::Async::Resolver );
  3         4  
  3         3169  
11              
12             our $VERSION = '0.01';
13              
14 3     3   248324 use Carp;
  3         6  
  3         163  
15              
16 3     3   18 use Future;
  3         6  
  3         66  
17 3     3   17 use IO::Async::Handle;
  3         6  
  3         82  
18              
19 3     3   3756 use Net::LibAsyncNS;
  0            
  0            
20              
21             =head1 NAME
22              
23             C - use F for C resolver queries
24              
25             =head1 SYNOPSIS
26              
27             use IO::Async::Loop;
28             use IO::Async::Resolver::LibAsyncNS;
29              
30             my $loop = IO::Async::Loop->new;
31              
32             my $resolver = IO::Async::Resolver::LibAsyncNS->new;
33             $loop->add( $resolver );
34              
35             $resolver->getaddrinfo(
36             host => "metacpan.org",
37             service => "http",
38             socktype => "stream",
39             )->on_done( sub {
40             my @res = @_;
41             print "metacpan.org available at\n";
42             printf " family=%d addr=%v02x\n", $_->{family}, $_->{addr} for @res;
43             })->get;
44              
45             =head1 DESCRIPTION
46              
47             This subclass of L applies special handling to the
48             C and C resolvers to use a L
49             instance, rather than using the usual L wrapper around
50             the system resolver functions. This may lead to higher performance in some
51             applications.
52              
53             It provides no additional methods, configuration options or events besides
54             those supported by C itself. It exists purely to
55             implement the same behaviours in a more efficient manner.
56              
57             =cut
58              
59             sub new
60             {
61             my $class = shift;
62             my $self = $class->SUPER::new( @_ );
63              
64             my $asyncns = Net::LibAsyncNS->new( 4 ); # TODO: configurable
65             $self->{asyncns} = $asyncns;
66              
67             $self->add_child( IO::Async::Handle->new(
68             read_handle => $asyncns->new_handle_for_fd,
69             on_read_ready => $self->_replace_weakself( '_on_asyncns_read_ready' ),
70             ) );
71              
72             return $self;
73             }
74              
75             sub _on_asyncns_read_ready
76             {
77             my $self = shift;
78              
79             my $asyncns = $self->{asyncns};
80              
81             $asyncns->wait( 0 ); # perform some IO but don't block
82              
83             while( my $q = $asyncns->getnext ) {
84             my $code = delete $self->{on_query_ready}{"$q"} or next;
85              
86             $code->( $self, $q );
87             }
88             }
89              
90             sub resolve
91             {
92             my $self = shift;
93             my %args = @_;
94              
95             my $type = delete $args{type} or croak "Expected 'type'";
96              
97             my $f;
98             if( $type eq "getaddrinfo_hash" ) {
99             $f = $self->_getaddrinfo_via_asyncns( @{ $args{data} } );
100             }
101             elsif( $type eq "getnameinfo" ) {
102             $f = $self->_getnameinfo_via_asyncns( @{ $args{data} } );
103             }
104             else {
105             return $self->SUPER::resolve( @_ );
106             }
107              
108             $f = Future->wait_any(
109             $f,
110             $self->loop->timeout_future( after => $args{timeout} )
111             ) if defined $args{timeout};
112              
113             return $f;
114             }
115              
116             sub _getaddrinfo_via_asyncns
117             {
118             my $self = shift;
119             my %args = @_;
120              
121             my %hints;
122             defined $args{$_} and $hints{$_} = $args{$_} for qw( flags family socktype protocol );
123              
124             my $asyncns = $self->{asyncns};
125              
126             my $q = $asyncns->getaddrinfo( $args{host}, $args{service}, \%hints );
127              
128             my $f = $self->loop->new_future;
129             $f->on_cancel( sub { $asyncns->cancel( $q ) } );
130              
131             $self->{on_query_ready}{"$q"} = sub {
132             my ( $self, $q ) = @_;
133              
134             my ( $err, @res ) = $self->{asyncns}->getaddrinfo_done( $q );
135              
136             if( $err ) {
137             $f->fail( "$err\n", resolve => getaddrinfo => );
138             }
139             else {
140             $f->done( @res );
141             }
142             };
143              
144             return $f;
145             }
146              
147             sub _getnameinfo_via_asyncns
148             {
149             my $self = shift;
150             my ( $addr, $flags ) = @_;
151              
152             my $asyncns = $self->{asyncns};
153              
154             my $q = $asyncns->getnameinfo( $addr, $flags, 1, 1 );
155              
156             my $f = $self->loop->new_future;
157             $f->on_cancel( sub { $asyncns->cancel( $q ) } );
158              
159             $self->{on_query_ready}{"$q"} = sub {
160             my ( $self, $q ) = @_;
161              
162             my ( $err, $host, $service ) = $self->{asyncns}->getnameinfo_done( $q );
163              
164             if( $err ) {
165             $f->fail( "$err\n", resolve => getnameinfo => );
166             }
167             else {
168             $f->done( [ $host, $service ] );
169             }
170             };
171              
172             return $f;
173             }
174              
175             =head1 AUTHOR
176              
177             Paul Evans
178              
179             =cut
180              
181             0x55AA;