Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
improved Mojo::IOLoop::Server and Mojo::Server::Daemon to be able to …
…listen on random ports
  • Loading branch information
kraih committed May 11, 2014
1 parent cd1c39e commit 6a46010
Show file tree
Hide file tree
Showing 13 changed files with 81 additions and 93 deletions.
4 changes: 3 additions & 1 deletion Changes
@@ -1,5 +1,7 @@

4.99 2014-05-10
4.99 2014-05-11
- Improved Mojo::IOLoop::Server and Mojo::Server::Daemon to be able to
listen on random ports.

4.98 2014-05-09
- Removed deprecated get_line function from Mojo::Util.
Expand Down
17 changes: 9 additions & 8 deletions lib/Mojo/IOLoop/Server.pm
Expand Up @@ -33,7 +33,7 @@ has reactor => sub {

sub DESTROY {
my $self = shift;
$ENV{MOJO_REUSE} =~ s/(?:^|\,)\Q$self->{reuse}\E:\d+// if $self->{reuse};
$ENV{MOJO_REUSE} =~ s/(?:^|\,)\Q$self->{reuse}\E// if $self->{reuse};
return unless my $reactor = $self->reactor;
$self->stop if $self->{handle};
$reactor->remove($_) for values %{$self->{handles}};
Expand All @@ -51,11 +51,11 @@ sub listen {

# Look for reusable file descriptor
my $address = $args->{address} || '0.0.0.0';
my $port = $args->{port} || 3000;
my $reuse = $self->{reuse} = "$address:$port";
my ($port, $fd);
$ENV{MOJO_REUSE} ||= '';
my $fd;
if ($ENV{MOJO_REUSE} =~ /(?:^|\,)\Q$reuse\E:(\d+)/) { $fd = $1 }
$fd = $1
if ($port = $args->{port})
&& $ENV{MOJO_REUSE} =~ /(?:^|\,)\Q$address:$port\E:(\d+)/;

# Allow file descriptor inheritance
local $^F = 1000;
Expand All @@ -73,15 +73,16 @@ sub listen {
my %options = (
Listen => $args->{backlog} // SOMAXCONN,
LocalAddr => $address,
LocalPort => $port,
ReuseAddr => 1,
ReusePort => $args->{reuse},
Type => SOCK_STREAM
);
$options{LocalPort} = $port if $port;
$options{LocalAddr} =~ s/[\[\]]//g;
$handle = $class->new(%options) or croak "Can't create listen socket: $@";
$fd = fileno $handle;
$ENV{MOJO_REUSE} .= length $ENV{MOJO_REUSE} ? ",$reuse:$fd" : "$reuse:$fd";
my $reuse = $self->{reuse} = join ':', $address, $handle->sockport, $fd;
$ENV{MOJO_REUSE} .= length $ENV{MOJO_REUSE} ? ",$reuse" : "$reuse";
}
$handle->blocking(0);
$self->{handle} = $handle;
Expand Down Expand Up @@ -262,7 +263,7 @@ Maximum backlog size, defaults to C<SOMAXCONN>.
port => 80
Port to listen on.
Port to listen on, defaults to a random port.
=item reuse
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojo/Server/Daemon.pm
Expand Up @@ -145,9 +145,9 @@ sub _listen {
my $options = {
address => $url->host,
backlog => $self->backlog,
port => $url->port,
reuse => scalar $query->param('reuse'),
};
if (my $port = $url->port) { $options->{port} = $port }
$options->{"tls_$_"} = scalar $query->param($_) for qw(ca cert ciphers key);
my $verify = $query->param('verify');
$options->{tls_verify} = hex $verify if defined $verify;
Expand Down
10 changes: 5 additions & 5 deletions t/mojo/daemon.t
Expand Up @@ -186,10 +186,10 @@ ok $remote_address, 'has local address';
ok $remote_port > 0, 'has local port';

# Pipelined
$port = Mojo::IOLoop->generate_port;
my $daemon = Mojo::Server::Daemon->new(listen => ["http://127.0.0.1:$port"],
silent => 1);
my $daemon
= Mojo::Server::Daemon->new(listen => ['http://127.0.0.1'], silent => 1);
$daemon->start;
$port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->handle->sockport;
is $daemon->app->moniker, 'HelloWorld', 'right moniker';
my $buffer = '';
my $id;
Expand All @@ -214,16 +214,16 @@ Mojo::IOLoop->start;
like $buffer, qr/Mojo$/, 'transactions were pipelined';

# Throttling
$port = Mojo::IOLoop->generate_port;
$daemon = Mojo::Server::Daemon->new(
app => $app,
listen => ["http://127.0.0.1:$port"],
listen => ['http://127.0.0.1'],
silent => 1
);
is scalar @{$daemon->acceptors}, 0, 'no active acceptors';
$daemon->start;
is scalar @{$daemon->acceptors}, 1, 'one active acceptor';
is $daemon->app->moniker, 'mojolicious', 'right moniker';
$port = Mojo::IOLoop->acceptor($daemon->acceptors->[0])->handle->sockport;
$tx = $ua->get("http://127.0.0.1:$port/throttle1" => {Connection => 'close'});
ok $tx->success, 'successful';
is $tx->res->code, 200, 'right status';
Expand Down
31 changes: 15 additions & 16 deletions t/mojo/ioloop.t
Expand Up @@ -77,15 +77,15 @@ ok $count > 1, 'more than one recurring event';
ok $count < 10, 'less than ten recurring events';

# Handle
my $port = Mojo::IOLoop->generate_port;
my ($handle, $handle2);
$id = Mojo::IOLoop->server(
(address => '127.0.0.1', port => $port) => sub {
(address => '127.0.0.1') => sub {
my ($loop, $stream) = @_;
$handle = $stream->handle;
Mojo::IOLoop->stop;
}
);
my $port = Mojo::IOLoop->acceptor($id)->handle->sockport;
Mojo::IOLoop->acceptor($id)->on(accept => sub { $handle2 = pop });
$id2
= Mojo::IOLoop->client((address => 'localhost', port => $port) => sub { });
Expand All @@ -102,10 +102,9 @@ Mojo::IOLoop->one_tick;
ok time < ($time + 10), 'stopped automatically';

# Stream
$port = Mojo::IOLoop->generate_port;
my $buffer = '';
Mojo::IOLoop->server(
(address => '127.0.0.1', port => $port) => sub {
$id = Mojo::IOLoop->server(
(address => '127.0.0.1') => sub {
my ($loop, $stream) = @_;
$buffer .= 'accepted';
$stream->on(
Expand All @@ -118,6 +117,7 @@ Mojo::IOLoop->server(
);
}
);
$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
my $delay = Mojo::IOLoop->delay;
my $end = $delay->begin(0);
Mojo::IOLoop->client(
Expand Down Expand Up @@ -145,8 +145,8 @@ ok !Mojo::IOLoop->stream($id), 'stream does not exist anymore';
is $buffer, 'acceptedhelloworld', 'right result';

# Removed listen socket
$port = Mojo::IOLoop->generate_port;
$id = $loop->server({address => '127.0.0.1', port => $port} => sub { });
$id = $loop->server({address => '127.0.0.1'} => sub { });
$port = $loop->acceptor($id)->handle->sockport;
my $connected;
$loop->client(
{port => $port} => sub {
Expand All @@ -167,14 +167,14 @@ ok !$loop->acceptor($id), 'acceptor has been removed';
# Removed connection (with delay)
my $removed;
$delay = Mojo::IOLoop->delay(sub { $removed++ });
$port = Mojo::IOLoop->generate_port;
$end = $delay->begin;
Mojo::IOLoop->server(
(address => '127.0.0.1', port => $port) => sub {
$id = Mojo::IOLoop->server(
(address => '127.0.0.1') => sub {
my ($loop, $stream) = @_;
$stream->on(close => $end);
}
);
$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
my $end2 = $delay->begin;
$id = Mojo::IOLoop->client(
(port => $port) => sub {
Expand All @@ -187,10 +187,9 @@ $delay->wait;
is $removed, 1, 'connection has been removed';

# Stream throttling
$port = Mojo::IOLoop->generate_port;
my ($client, $server, $client_after, $server_before, $server_after);
Mojo::IOLoop->server(
{address => '127.0.0.1', port => $port} => sub {
$id = Mojo::IOLoop->server(
{address => '127.0.0.1'} => sub {
my ($loop, $stream) = @_;
$stream->timeout(0)->on(
read => sub {
Expand All @@ -215,6 +214,7 @@ Mojo::IOLoop->server(
);
}
);
$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
Mojo::IOLoop->client(
{port => $port} => sub {
my ($loop, $err, $stream) = @_;
Expand Down Expand Up @@ -242,9 +242,8 @@ is $loop->max_connections, 0, 'right value';
# Graceful shutdown (max_accepts)
$err = '';
$loop = Mojo::IOLoop->new(max_accepts => 1);
$port = $loop->generate_port;
$loop->server(
{address => '127.0.0.1', port => $port} => sub { shift; shift->close });
$id = $loop->server({address => '127.0.0.1'} => sub { shift; shift->close });
$port = $loop->acceptor($id)->handle->sockport;
$loop->client({port => $port} => sub { });
$loop->timer(3 => sub { shift->stop; $err = 'failed' });
$loop->start;
Expand Down
6 changes: 3 additions & 3 deletions t/mojo/ioloop_ipv6.t
Expand Up @@ -14,17 +14,17 @@ use Mojo::IOLoop;

# IPv6 roundtrip
my $delay = Mojo::IOLoop->delay;
my $port = Mojo::IOLoop->generate_port;
my ($server, $client);
my $end = $delay->begin;
Mojo::IOLoop->server(
{address => '[::1]', port => $port} => sub {
my $id = Mojo::IOLoop->server(
{address => '[::1]'} => sub {
my ($loop, $stream) = @_;
$stream->write('test' => sub { shift->write('321') });
$stream->on(close => $end);
$stream->on(read => sub { $server .= pop });
}
);
my $port = Mojo::IOLoop->acceptor($id)->handle->sockport;
my $end2 = $delay->begin;
Mojo::IOLoop->client(
{address => '[::1]', port => $port} => sub {
Expand Down
41 changes: 17 additions & 24 deletions t/mojo/ioloop_tls.t
Expand Up @@ -38,17 +38,17 @@ use Mojo::IOLoop;
# Built-in certificate
my $loop = Mojo::IOLoop->new;
my $delay = $loop->delay;
my $port = Mojo::IOLoop->generate_port;
my ($server, $client);
my $end = $delay->begin;
$loop->server(
{address => '127.0.0.1', port => $port, tls => 1} => sub {
my $id = $loop->server(
{address => '127.0.0.1', tls => 1} => sub {
my ($loop, $stream) = @_;
$stream->write('test' => sub { shift->write('321') });
$stream->on(close => $end);
$stream->on(read => sub { $server .= pop });
}
);
my $port = $loop->acceptor($id)->handle->sockport;
my $end2 = $delay->begin;
$loop->client(
{port => $port, tls => 1} => sub {
Expand All @@ -65,14 +65,12 @@ is $client, 'test321', 'right content';

# Valid client certificate
$delay = Mojo::IOLoop->delay;
$port = Mojo::IOLoop->generate_port;
($server, $client) = ();
my ($remove, $running, $timeout, $server_err, $server_close, $client_close);
Mojo::IOLoop->remove(Mojo::IOLoop->recurring(0 => sub { $remove++ }));
$end = $delay->begin;
Mojo::IOLoop->server(
$id = Mojo::IOLoop->server(
address => '127.0.0.1',
port => $port,
tls => 1,
tls_ca => 't/mojo/certs/ca.crt',
tls_cert => 't/mojo/certs/server.crt',
Expand All @@ -93,6 +91,7 @@ Mojo::IOLoop->server(
$stream->timeout(0.5);
}
);
$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
$end2 = $delay->begin;
Mojo::IOLoop->client(
port => $port,
Expand Down Expand Up @@ -150,17 +149,16 @@ ok $client_err, 'has error';

# Invalid certificate authority (server)
$loop = Mojo::IOLoop->new;
$port = Mojo::IOLoop->generate_port;
($server_err, $client_err) = ();
$loop->server(
$id = $loop->server(
address => '127.0.0.1',
port => $port,
tls => 1,
tls_ca => 'no cert',
tls_cert => 't/mojo/certs/server.crt',
tls_key => 't/mojo/certs/server.key',
sub { $server_err = 'accepted' }
);
$port = $loop->acceptor($id)->handle->sockport;
$loop->client(
port => $port,
tls => 1,
Expand All @@ -177,13 +175,11 @@ ok $client_err, 'has error';

# Valid client and server certificates
$delay = Mojo::IOLoop->delay;
$port = Mojo::IOLoop->generate_port;
($running, $timeout, $server, $server_err, $server_close) = ();
($client, $client_close) = ();
$end = $delay->begin;
Mojo::IOLoop->server(
$id = Mojo::IOLoop->server(
address => '127.0.0.1',
port => $port,
tls => 1,
tls_ca => 't/mojo/certs/ca.crt',
tls_cert => 't/mojo/certs/server.crt',
Expand All @@ -202,6 +198,7 @@ Mojo::IOLoop->server(
$stream->on(read => sub { $server .= pop });
}
);
$port = Mojo::IOLoop->acceptor($id)->handle->sockport;
$end2 = $delay->begin;
Mojo::IOLoop->client(
port => $port,
Expand Down Expand Up @@ -234,16 +231,15 @@ ok !$server_err, 'no error';

# Invalid server certificate (unsigned)
$loop = Mojo::IOLoop->new;
$port = Mojo::IOLoop->generate_port;
($server_err, $client_err) = ();
$loop->server(
$id = $loop->server(
address => '127.0.0.1',
port => $port,
tls => 1,
tls_cert => 't/mojo/certs/badclient.crt',
tls_key => 't/mojo/certs/badclient.key',
sub { $server_err = 'accepted' }
);
$port = $loop->acceptor($id)->handle->sockport;
$loop->client(
port => $port,
tls => 1,
Expand All @@ -259,16 +255,15 @@ ok $client_err, 'has error';

# Invalid server certificate (hostname)
$loop = Mojo::IOLoop->new;
$port = Mojo::IOLoop->generate_port;
($server_err, $client_err) = ();
$loop->server(
$id = $loop->server(
address => '127.0.0.1',
port => $port,
tls => 1,
tls_cert => 't/mojo/certs/server.crt',
tls_key => 't/mojo/certs/server.key',
sub { $server_err = 'accepted' }
);
$port = $loop->acceptor($id)->handle->sockport;
$loop->client(
address => '127.0.0.1',
port => $port,
Expand All @@ -285,16 +280,15 @@ ok $client_err, 'has error';

# Invalid certificate authority (client)
$loop = Mojo::IOLoop->new;
$port = Mojo::IOLoop->generate_port;
($server_err, $client_err) = ();
$loop->server(
$id = $loop->server(
address => '127.0.0.1',
port => $port,
tls => 1,
tls_cert => 't/mojo/certs/badclient.crt',
tls_key => 't/mojo/certs/badclient.key',
sub { $server_err = 'accepted' }
);
$port = $loop->acceptor($id)->handle->sockport;
$loop->client(
port => $port,
tls => 1,
Expand All @@ -310,12 +304,10 @@ ok $client_err, 'has error';

# Ignore invalid client certificate
$loop = Mojo::IOLoop->new;
$port = Mojo::IOLoop->generate_port;
my $cipher;
($server, $client, $client_err) = ();
$loop->server(
$id = $loop->server(
address => '127.0.0.1',
port => $port,
tls => 1,
tls_ca => 't/mojo/certs/ca.crt',
tls_cert => 't/mojo/certs/server.crt',
Expand All @@ -328,6 +320,7 @@ $loop->server(
$server = 'accepted';
}
);
$port = $loop->acceptor($id)->handle->sockport;
$loop->client(
port => $port,
tls => 1,
Expand Down

0 comments on commit 6a46010

Please sign in to comment.