Skip to content

Commit

Permalink
added support for non-blocking name resolution with Net::DNS::Native (c…
Browse files Browse the repository at this point in the history
…loses #700)
  • Loading branch information
kraih committed Nov 7, 2014
1 parent 687905b commit 5180ae3
Show file tree
Hide file tree
Showing 59 changed files with 132 additions and 218 deletions.
1 change: 1 addition & 0 deletions Changes
@@ -1,5 +1,6 @@

5.59 2014-11-07
- Added support for non-blocking name resolution with Net::DNS::Native.

5.58 2014-11-06
- Improved error handling in Mojo::IOLoop::Client.
Expand Down
7 changes: 6 additions & 1 deletion Makefile.PL
Expand Up @@ -7,6 +7,7 @@ use ExtUtils::MakeMaker;

# Pod::Simple 3.09 first shipped with Perl 5.11.2
# Time::Local 1.2 first shipped with Perl 5.13.9
# IO::Socket::IP 0.26 first shipped with Perl 5.19.8
WriteMakefile(
NAME => 'Mojolicious',
VERSION_FROM => 'lib/Mojolicious.pm',
Expand All @@ -24,7 +25,11 @@ WriteMakefile(
},
no_index => {directory => ['t']}
},
PREREQ_PM => {'Pod::Simple' => '3.09', 'Time::Local' => '1.2'},
PREREQ_PM => {
'IO::Socket::IP' => '0.26',
'Pod::Simple' => '3.09',
'Time::Local' => '1.2'
},
EXE_FILES => ['script/hypnotoad', 'script/mojo', 'script/morbo'],
test => {TESTS => 't/*.t t/*/*.t'}
);
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -18,7 +18,7 @@
testing framework, static file server, first class Unicode support and
much more for you to discover.
* Very clean, portable and object-oriented pure-Perl API with no hidden
magic and no requirements besides Perl 5.18.0 (versions as old as 5.10.1
magic and no requirements besides Perl 5.20.0 (versions as old as 5.10.1
can be used too, but may require additional CPAN modules to be installed)
* Full stack HTTP and WebSocket client/server implementation with IPv6, TLS,
SNI, IDNA, HTTP/SOCKS5 proxy, Comet (long polling), keep-alive, connection
Expand Down
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 provide non-blocking name
resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
L<Net::DNS::Native> (0.11+), 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_NDN>,
C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
See L<Mojolicious::Guides::Cookbook/"REAL-TIME WEB"> for more.
Expand Down
67 changes: 46 additions & 21 deletions lib/Mojo/IOLoop/Client.pm
Expand Up @@ -2,15 +2,16 @@ package Mojo::IOLoop::Client;
use Mojo::Base 'Mojo::EventEmitter';

use Errno 'EINPROGRESS';
use IO::Socket::INET;
use IO::Socket::IP;
use Mojo::IOLoop;
use Scalar::Util 'weaken';
use Socket qw(IPPROTO_TCP TCP_NODELAY);

# IPv6 support requires IO::Socket::IP
use constant IPV6 => $ENV{MOJO_NO_IPV6}
# Non-blocking name resolution requires Net::DNS::Native
use constant NDN => $ENV{MOJO_NO_NDN}
? 0
: eval 'use IO::Socket::IP 0.20 (); 1';
: 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}
Expand All @@ -33,45 +34,66 @@ 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 = (PeerAddrInfo => $args->{addr_info}) if $args->{addr_info};
$options{Blocking} = 0;
$options{LocalAddr} = $args->{local_address} if $args->{local_address};
$options{PeerAddr} =~ s/[\[\]]//g if $options{PeerAddr};
my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
return $self->emit(error => "Can't connect: $@")
unless $self->{handle} = $handle = $class->new(%options);
unless $self->{handle} = $handle = IO::Socket::IP->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 _ready {
Expand All @@ -89,6 +111,8 @@ sub _ready {
$self->_try_socks($args);
}

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

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

Expand Down Expand Up @@ -243,8 +267,9 @@ 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+).
Open a socket connection to a remote host. Note that non-blocking name
resolution depends on L<Net::DNS::Native> (0.11+) and TLS support on
L<IO::Socket::SSL> (1.84+).
These options are currently available:
Expand Down
17 changes: 6 additions & 11 deletions lib/Mojo/IOLoop/Server.pm
Expand Up @@ -4,16 +4,11 @@ use Mojo::Base 'Mojo::EventEmitter';
use Carp 'croak';
use File::Basename 'dirname';
use File::Spec::Functions 'catfile';
use IO::Socket::INET;
use IO::Socket::IP;
use Mojo::IOLoop;
use Scalar::Util 'weaken';
use Socket qw(IPPROTO_TCP TCP_NODELAY);

# IPv6 support requires IO::Socket::IP
use constant IPV6 => $ENV{MOJO_NO_IPV6}
? 0
: eval 'use IO::Socket::IP 0.20 (); 1';

# TLS support requires IO::Socket::SSL
use constant TLS => $ENV{MOJO_NO_TLS}
? 0
Expand All @@ -38,7 +33,7 @@ sub DESTROY {
}

sub generate_port {
IO::Socket::INET->new(Listen => 5, LocalAddr => '127.0.0.1')->sockport;
IO::Socket::IP->new(Listen => 5, LocalAddr => '127.0.0.1')->sockport;
}

sub handle { shift->{handle} }
Expand All @@ -59,9 +54,8 @@ sub listen {

# Reuse file descriptor
my $handle;
my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
if (defined $fd) {
$handle = $class->new_from_fd($fd, 'r')
$handle = IO::Socket::IP->new_from_fd($fd, 'r')
or croak "Can't open file descriptor $fd: $!";
}

Expand All @@ -76,7 +70,8 @@ sub listen {
);
$options{LocalPort} = $port if $port;
$options{LocalAddr} =~ s/[\[\]]//g;
$handle = $class->new(%options) or croak "Can't create listen socket: $@";
$handle = IO::Socket::IP->new(%options)
or croak "Can't create listen socket: $@";
$fd = fileno $handle;
my $reuse = $self->{reuse} = join ':', $address, $handle->sockport, $fd;
$ENV{MOJO_REUSE} .= length $ENV{MOJO_REUSE} ? ",$reuse" : "$reuse";
Expand Down Expand Up @@ -238,7 +233,7 @@ Get handle for server.
$server->listen(port => 3000);
Create a new listen socket. 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+).
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 provide non-blocking name
resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
L<Net::DNS::Native> (0.11+), 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_NDN>,
C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> 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 provide non-blocking name
resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
L<Net::DNS::Native> (0.11+), 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_NDN>,
C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> 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 provide non-blocking name
resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
L<Net::DNS::Native> (0.11+), 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_NDN>,
C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> 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 provide non-blocking name
resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
L<Net::DNS::Native> (0.11+), 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_NDN>,
C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> 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 provide non-blocking name
resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
L<Net::DNS::Native> (0.11+), 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_NDN>,
C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
See L<Mojolicious::Guides::Cookbook/"USER AGENT"> for more.
Expand Down
4 changes: 2 additions & 2 deletions lib/Mojolicious/Command/version.pm
Expand Up @@ -13,9 +13,9 @@ sub run {

my $ev = eval 'use Mojo::Reactor::EV; 1' ? $EV::VERSION : 'not installed';
my $class = 'Mojo::IOLoop::Client';
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 @@ -24,9 +24,9 @@ CORE
OPTIONAL
EV 4.0+ ($ev)
IO::Socket::IP 0.20+ ($ipv6)
IO::Socket::Socks 0.64+ ($socks)
IO::Socket::SSL 1.84+ ($tls)
Net::DNS::Native ($ndn)
EOF

Expand Down
4 changes: 2 additions & 2 deletions lib/Mojolicious/Guides/FAQ.pod
Expand Up @@ -34,8 +34,8 @@ without compromises. While there are no rules in
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
modules such as L<EV>, L<IO::Socket::Socks>, 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
5 changes: 1 addition & 4 deletions t/mojo/daemon.t
@@ -1,9 +1,6 @@
use Mojo::Base -strict;

BEGIN {
$ENV{MOJO_NO_IPV6} = 1;
$ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
}
BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }

use Test::More;
use Cwd 'abs_path';
Expand Down
5 changes: 1 addition & 4 deletions t/mojo/delay.t
@@ -1,9 +1,6 @@
use Mojo::Base -strict;

BEGIN {
$ENV{MOJO_NO_IPV6} = 1;
$ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll';
}
BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }

use Test::More;
use Mojo::IOLoop;
Expand Down

0 comments on commit 5180ae3

Please sign in to comment.