Skip to content

Commit

Permalink
added support for non-blocking name resolution with Net::DNS::Native
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Nov 7, 2014
1 parent 9a407b7 commit bbe3c42
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 58 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -9,7 +9,7 @@ perl:
env:
- "HARNESS_OPTIONS=j9 TEST_POD=1 TEST_EV=1 TEST_IPV6=1 TEST_SOCKS=1 TEST_TLS=1"
install:
- "cpanm -n Test::Pod Test::Pod::Coverage EV IO::Socket::IP IO::Socket::Socks IO::Socket::SSL"
- "cpanm -n Test::Pod Test::Pod::Coverage EV Net::DNS::Native IO::Socket::IP IO::Socket::Socks IO::Socket::SSL"
- "cpanm -n --installdeps ."
notifications:
email: false
16 changes: 6 additions & 10 deletions lib/Mojo/IOLoop.pm
Expand Up @@ -314,22 +314,18 @@ scalable C<select>.
LIBEV_FLAGS=4 # epoll (Linux)
LIBEV_FLAGS=8 # kqueue (*BSD, OS X)
Connections are established non-blocking, but name resolution needs to be
performed blocking, so you should reduce concurrency if resolver timeouts are
a possibility.
The event loop will be resilient to time jumps if a monotonic clock is
available through L<Time::HiRes>. A TLS certificate and key are also built
right in, to make writing test servers as easy as possible. Also note that for
convenience the C<PIPE> signal will be set to C<IGNORE> when L<Mojo::IOLoop>
is loaded.
For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
automatically if they are installed. Individual features can also be disabled
with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
variables.
For better scalability (epoll, kqueue) and to support IPv6, SOCKS5, TLS as
well as non-blocking name resolution, the optional modules L<EV> (4.0+),
L<IO::Socket::IP> (0.20+), L<IO::Socket::Socks> (0.64+), L<IO::Socket::SSL>
(1.84+) and L<Net::DNS::Native> (0.11+) will be used automatically if they are
installed. Individual features can also be disabled with the C<MOJO_NO_IPV6>,
C<MOJO_NO_SOCKS>, C<MOJO_NO_TLS> and C<MOJO_NO_NDN> environment variables.
See L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB"> for more.
Expand Down
58 changes: 44 additions & 14 deletions lib/Mojo/IOLoop/Client.pm
Expand Up @@ -12,6 +12,11 @@ use constant IPV6 => $ENV{MOJO_NO_IPV6}
? 0
: eval 'use IO::Socket::IP 0.20 (); 1';

# Non-blocking name resolution requires Net::DNS::Native and IO::Socket::IP
use constant NDN => $ENV{MOJO_NO_NDN} ? 0 : IPV6
&& eval 'use Net::DNS::Native 0.11 (); 1';
my $NDN = NDN ? Net::DNS::Native->new(pool => 5, extra_thread => 1) : undef;

# TLS support requires IO::Socket::SSL
use constant TLS => $ENV{MOJO_NO_TLS}
? 0
Expand All @@ -33,47 +38,71 @@ sub DESTROY { shift->_cleanup }
sub connect {
my $self = shift;
my $args = ref $_[0] ? $_[0] : {@_};

# Timeout
weaken $self;
$self->reactor->next_tick(sub { $self && $self->_connect($args) });
my $reactor = $self->reactor;
$self->{timer} = $reactor->timer($args->{timeout} || 10,
sub { $self->emit(error => 'Connect timeout') });

# Blocking name resolution
$_ && s/[[\]]//g for @$args{qw(address socks_address)};
my $address = $args->{socks_address} || ($args->{address} ||= 'localhost');
return $reactor->next_tick(sub { $self && $self->_connect($args) })
unless NDN && $address ne 'localhost' && !$args->{handle};

# Non-blocking name resolution
my $handle = $self->{dns}
= $NDN->getaddrinfo($address, _port($args), {protocol => IPPROTO_TCP});
$reactor->io(
$handle => sub {
my $reactor = shift;

$reactor->remove($self->{dns});
my ($err, @res) = $NDN->get_result(delete $self->{dns});
return $self->emit(error => "Can't resolve: $err") if $err;

$args->{addr_info} = \@res;
$self->_connect($args);
}
)->watch($handle, 1, 0);
}

sub _cleanup {
my $self = shift;
return $self unless my $reactor = $self->reactor;
$self->{$_} && $reactor->remove(delete $self->{$_}) for qw(timer handle);
$NDN->timedout($self->{dns}) if $self->{dns};
$self->{$_} && $reactor->remove(delete $self->{$_}) for qw(dns timer handle);
return $self;
}

sub _connect {
my ($self, $args) = @_;

my $handle;
my $reactor = $self->reactor;
my $address = $args->{socks_address} || ($args->{address} ||= 'localhost');
my $port = $args->{socks_port} || $args->{port} || ($args->{tls} ? 443 : 80);
my $address = $args->{socks_address} || $args->{address};
unless ($handle = $self->{handle} = $args->{handle}) {
my %options = (
Blocking => 0,
PeerAddr => $address eq 'localhost' ? '127.0.0.1' : $address,
PeerPort => $port
PeerPort => _port($args)
);
$options{LocalAddr} = $args->{local_address} if $args->{local_address};
$options{PeerAddr} =~ s/[\[\]]//g if $options{PeerAddr};
%options = (PeerAddrInfo => $args->{addr_info}) if $args->{addr_info};
$options{Blocking} = 0;
my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
return $self->emit(error => "Can't connect: $@")
unless $self->{handle} = $handle = $class->new(%options);
}
$handle->blocking(0);

# Timeout
$self->{timer} = $reactor->timer($args->{timeout} || 10,
sub { $self->emit(error => 'Connect timeout') });

# Wait for handle to become writable
weaken $self;
$reactor->io($handle => sub { $self->_ready($args) })->watch($handle, 0, 1);
$self->reactor->io($handle => sub { $self->_ready($args) })
->watch($handle, 0, 1);
}

sub _port { $_[0]->{socks_port} || $_[0]->{port} || ($_[0]->{tls} ? 443 : 80) }

sub _ready {
my ($self, $args) = @_;

Expand Down Expand Up @@ -244,7 +273,8 @@ implements the following new ones.
$client->connect(address => '127.0.0.1', port => 3000);
Open a socket connection to a remote host. Note that TLS support depends on
L<IO::Socket::SSL> (1.84+) and IPv6 support on L<IO::Socket::IP> (0.20+).
L<IO::Socket::SSL> (1.84+), IPv6 support on L<IO::Socket::IP> (0.20+) and
non-blocking name resolution on L<Net::DNS::Native> (0.11+).
These options are currently available:
Expand Down
12 changes: 6 additions & 6 deletions lib/Mojo/Server/Daemon.pm
Expand Up @@ -264,12 +264,12 @@ L<Mojo::Server::Daemon> is a full featured, highly portable non-blocking I/O
HTTP and WebSocket server, with IPv6, TLS, Comet (long polling), keep-alive
and multiple event loop support.
For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
automatically if they are installed. Individual features can also be disabled
with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
variables.
For better scalability (epoll, kqueue) and to support IPv6, SOCKS5, TLS as
well as non-blocking name resolution, the optional modules L<EV> (4.0+),
L<IO::Socket::IP> (0.20+), L<IO::Socket::Socks> (0.64+), L<IO::Socket::SSL>
(1.84+) and L<Net::DNS::Native> (0.11+) will be used automatically if they are
installed. Individual features can also be disabled with the C<MOJO_NO_IPV6>,
C<MOJO_NO_SOCKS>, C<MOJO_NO_TLS> and C<MOJO_NO_NDN> environment variables.
See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
Expand Down
12 changes: 6 additions & 6 deletions lib/Mojo/Server/Hypnotoad.pm
Expand Up @@ -171,12 +171,12 @@ You can run the same command again for automatic hot deployment.
This second invocation will load the application again, detect the process id
file with it, and send a L</"USR2"> signal to the already running server.
For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
automatically if they are installed. Individual features can also be disabled
with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
variables.
For better scalability (epoll, kqueue) and to support IPv6, SOCKS5, TLS as
well as non-blocking name resolution, the optional modules L<EV> (4.0+),
L<IO::Socket::IP> (0.20+), L<IO::Socket::Socks> (0.64+), L<IO::Socket::SSL>
(1.84+) and L<Net::DNS::Native> (0.11+) will be used automatically if they are
installed. Individual features can also be disabled with the C<MOJO_NO_IPV6>,
C<MOJO_NO_SOCKS>, C<MOJO_NO_TLS> and C<MOJO_NO_NDN> environment variables.
See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
Expand Down
12 changes: 6 additions & 6 deletions lib/Mojo/Server/Morbo.pm
Expand Up @@ -130,12 +130,12 @@ To start applications with it you can use the L<morbo> script.
$ morbo ./myapp.pl
Server available at http://127.0.0.1:3000.
For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
automatically if they are installed. Individual features can also be disabled
with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
variables.
For better scalability (epoll, kqueue) and to support IPv6, SOCKS5, TLS as
well as non-blocking name resolution, the optional modules L<EV> (4.0+),
L<IO::Socket::IP> (0.20+), L<IO::Socket::Socks> (0.64+), L<IO::Socket::SSL>
(1.84+) and L<Net::DNS::Native> (0.11+) will be used automatically if they are
installed. Individual features can also be disabled with the C<MOJO_NO_IPV6>,
C<MOJO_NO_SOCKS>, C<MOJO_NO_TLS> and C<MOJO_NO_NDN> environment variables.
See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
Expand Down
12 changes: 6 additions & 6 deletions lib/Mojo/Server/Prefork.pm
Expand Up @@ -261,12 +261,12 @@ keep-alive and multiple event loop support. Note that the server uses signals
for process management, so you should avoid modifying signal handlers in your
applications.
For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
automatically if they are installed. Individual features can also be disabled
with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
variables.
For better scalability (epoll, kqueue) and to support IPv6, SOCKS5, TLS as
well as non-blocking name resolution, the optional modules L<EV> (4.0+),
L<IO::Socket::IP> (0.20+), L<IO::Socket::Socks> (0.64+), L<IO::Socket::SSL>
(1.84+) and L<Net::DNS::Native> (0.11+) will be used automatically if they are
installed. Individual features can also be disabled with the C<MOJO_NO_IPV6>,
C<MOJO_NO_SOCKS>, C<MOJO_NO_TLS> and C<MOJO_NO_NDN> environment variables.
See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
Expand Down
12 changes: 6 additions & 6 deletions lib/Mojo/UserAgent.pm
Expand Up @@ -440,12 +440,12 @@ All connections will be reset automatically if a new process has been forked,
this allows multiple processes to share the same L<Mojo::UserAgent> object
safely.
For better scalability (epoll, kqueue) and to provide IPv6, SOCKS5 as well as
TLS support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.20+),
L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL> (1.84+) will be used
automatically if they are installed. Individual features can also be disabled
with the C<MOJO_NO_IPV6>, C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment
variables.
For better scalability (epoll, kqueue) and to support IPv6, SOCKS5, TLS as
well as non-blocking name resolution, the optional modules L<EV> (4.0+),
L<IO::Socket::IP> (0.20+), L<IO::Socket::Socks> (0.64+), L<IO::Socket::SSL>
(1.84+) and L<Net::DNS::Native> (0.11+) will be used automatically if they are
installed. Individual features can also be disabled with the C<MOJO_NO_IPV6>,
C<MOJO_NO_SOCKS>, C<MOJO_NO_TLS> and C<MOJO_NO_NDN> environment variables.
See L<Mojolicious::Guides::Cookbook/"USER AGENT"> for more.
Expand Down
2 changes: 2 additions & 0 deletions lib/Mojolicious/Command/version.pm
Expand Up @@ -16,6 +16,7 @@ sub run {
my $ipv6 = $class->IPV6 ? $IO::Socket::IP::VERSION : 'not installed';
my $socks = $class->SOCKS ? $IO::Socket::Socks::VERSION : 'not installed';
my $tls = $class->TLS ? $IO::Socket::SSL::VERSION : 'not installed';
my $ndn = $class->NDN ? $Net::DNS::Native::VERSION : 'not installed';

print <<EOF;
CORE
Expand All @@ -27,6 +28,7 @@ OPTIONAL
IO::Socket::IP 0.20+ ($ipv6)
IO::Socket::Socks 0.64+ ($socks)
IO::Socket::SSL 1.84+ ($tls)
Net::DNS::Native 0.11+ ($ndn)
EOF

Expand Down
4 changes: 2 additions & 2 deletions lib/Mojolicious/Guides/FAQ.pod
Expand Up @@ -35,8 +35,8 @@ L<Mojolicious::Guides::Contributing> that forbid dependencies, we do currently
discourage adding non-optional ones in favor of a faster and more painless
installation process. And we do in fact already use several optional CPAN
modules such as L<EV>, L<IO::Socket::IP>, L<IO::Socket::Socks>,
L<IO::Socket::SSL> and L<Plack> to provide advanced functionality if they are
installed.
L<IO::Socket::SSL>, L<Net::DNS::Native> and L<Plack> to provide advanced
functionality if they are installed.

=head2 Why reinvent wheels?

Expand Down
2 changes: 1 addition & 1 deletion t/mojo/user_agent_online.t
Expand Up @@ -87,7 +87,7 @@ ok $tx->error, 'has error';
$tx = $ua->build_tx(GET => 'http://cdeabcdeffoobarnonexisting.com');
$ua->start($tx);
ok $tx->is_finished, 'transaction is finished';
like $tx->error->{message}, qr/^Can't connect/, 'right error';
ok $tx->error, 'has error';

# Fresh user agent again
$ua = Mojo::UserAgent->new;
Expand Down

2 comments on commit bbe3c42

@olegwtf
Copy link
Contributor

@olegwtf olegwtf commented on bbe3c42 Nov 7, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to call getaddrinfo() when !IPV6

@olegwtf
Copy link
Contributor

@olegwtf olegwtf commented on bbe3c42 Nov 7, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see, sorry

Please sign in to comment.