Skip to content

Commit

Permalink
added tests for SO_REUSEPORT and made servers easier to test
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Aug 25, 2013
1 parent f233adc commit 64c758c
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 9 deletions.
4 changes: 4 additions & 0 deletions Changes
@@ -1,7 +1,11 @@

4.27 2013-08-25
- Added acceptors acceptors to Mojo::Server::Daemon.
- Added handle method to Mojo::IOLoop::Server.
- Added reuse option to Mojo::IOLoop::Server::listen.
- Added reuse parameter to Mojo::Server::Daemon::listen.
- Fixed console message bug in Mojo::Server::Daemon.
- Fixed SNI bug in online tests.

4.26 2013-08-18
- Fixed support for Netscape cookies in Mojo::Cookie::Response.
Expand Down
8 changes: 8 additions & 0 deletions lib/Mojo/IOLoop/Server.pm
Expand Up @@ -103,6 +103,8 @@ sub generate_port {
->sockport;
}

sub handle { shift->{handle} }

sub start {
my $self = shift;
weaken $self;
Expand Down Expand Up @@ -292,6 +294,12 @@ TLS verification mode, defaults to C<0x03>.
Find a free TCP port, this is a utility function primarily used for tests.
=head2 handle
my $handle = $server->handle;
Get handle for server.
=head2 start
$server->start;
Expand Down
20 changes: 14 additions & 6 deletions lib/Mojo/Server/Daemon.pm
Expand Up @@ -9,6 +9,7 @@ use Scalar::Util 'weaken';

use constant DEBUG => $ENV{MOJO_DAEMON_DEBUG} || 0;

has acceptors => sub { [] };
has [qw(backlog group silent user)];
has inactivity_timeout => sub { $ENV{MOJO_INACTIVITY_TIMEOUT} // 15 };
has ioloop => sub { Mojo::IOLoop->singleton };
Expand All @@ -20,7 +21,7 @@ sub DESTROY {
my $self = shift;
return unless my $loop = $self->ioloop;
$self->_remove($_) for keys %{$self->{connections} || {}};
$loop->remove($_) for @{$self->{acceptors} || []};
$loop->remove($_) for @{$self->acceptors};
}

sub run {
Expand Down Expand Up @@ -54,9 +55,9 @@ sub start {

# Resume accepting connections
my $loop = $self->ioloop;
if (my $acceptors = $self->{acceptors}) {
push @$acceptors, $loop->acceptor(delete $self->{servers}{$_})
for keys %{$self->{servers}};
if (my $servers = $self->{servers}) {
push @{$self->acceptors}, $loop->acceptor(delete $servers->{$_})
for keys %$servers;
}

# Start listening
Expand All @@ -71,7 +72,7 @@ sub stop {

# Suspend accepting connections but keep listen sockets open
my $loop = $self->ioloop;
while (my $id = shift @{$self->{acceptors}}) {
while (my $id = shift @{$self->acceptors}) {
my $server = $self->{servers}{$id} = $loop->acceptor($id);
$loop->remove($id);
$server->stop;
Expand Down Expand Up @@ -176,7 +177,7 @@ sub _listen {
my $tls = $options->{tls} = $url->protocol eq 'https' ? 1 : undef;

weaken $self;
push @{$self->{acceptors} ||= []}, $self->ioloop->server(
push @{$self->acceptors}, $self->ioloop->server(
$options => sub {
my ($loop, $stream, $id) = @_;

Expand Down Expand Up @@ -314,6 +315,13 @@ L<Mojo::Server::Daemon> inherits all events from L<Mojo::Server>.
L<Mojo::Server::Daemon> inherits all attributes from L<Mojo::Server> and
implements the following new ones.
=head2 acceptors
my $acceptors = $daemon->acceptors;
$daemon = $daemon->acceptors([]);
Currently active acceptors.
=head2 backlog
my $backlog = $daemon->backlog;
Expand Down
26 changes: 26 additions & 0 deletions t/mojo/app.t
Expand Up @@ -12,6 +12,7 @@ use Mojo::Log;
use Mojo::Server::Daemon;
use Mojo::UserAgent;
use Mojolicious;
use Socket qw(SO_REUSEPORT SOL_SOCKET);

# Timeout
{
Expand Down Expand Up @@ -219,13 +220,16 @@ $daemon = Mojo::Server::Daemon->new(
listen => ["http://127.0.0.1:$port"],
silent => 1
);
is_deeply $daemon->acceptors, [], 'no active acceptors';
$daemon->start;
is scalar @{$daemon->acceptors}, 1, 'one active acceptor';
is $daemon->app->moniker, 'mojolicious', 'right moniker';
$tx = $ua->get("http://127.0.0.1:$port/throttle1" => {Connection => 'close'});
ok $tx->success, 'successful';
is $tx->res->code, 200, 'right status';
is $tx->res->body, 'Whatever!', 'right content';
$daemon->stop;
is_deeply $daemon->acceptors, [], 'no active acceptors';
$tx = $ua->inactivity_timeout(0.5)
->get("http://127.0.0.1:$port/throttle2" => {Connection => 'close'});
ok !$tx->success, 'not successful';
Expand All @@ -237,4 +241,26 @@ ok $tx->success, 'successful';
is $tx->res->code, 200, 'right status';
is $tx->res->body, 'Whatever!', 'right content';

# SO_REUSEPORT
SKIP: {
skip 'SO_REUSEPORT support required!', 2 unless eval {SO_REUSEPORT};

$port = Mojo::IOLoop->generate_port;
$daemon = Mojo::Server::Daemon->new(
listen => ["http://127.0.0.1:$port"],
silent => 1
)->start;
ok !Mojo::IOLoop->acceptor($daemon->acceptors->[0])
->handle->getsockopt(SOL_SOCKET, SO_REUSEPORT),
'no SO_REUSEPORT socket option';
$daemon = Mojo::Server::Daemon->new(
listen => ["http://127.0.0.1:$port?reuse=1"],
silent => 1
);
$daemon->start;
ok !!Mojo::IOLoop->acceptor($daemon->acceptors->[0])
->handle->getsockopt(SOL_SOCKET, SO_REUSEPORT),
'SO_REUSEPORT socket option';
}

done_testing();
12 changes: 9 additions & 3 deletions t/mojo/user_agent_online.t
Expand Up @@ -206,9 +206,15 @@ is $tx->req->url, 'https://ipv6.google.com', 'right url';
is $tx->res->code, 200, 'right status';

# HTTPS request that requires SNI
$tx = $ua->get('https://google.de');
like $ua->ioloop->stream($tx->connection)
->handle->peer_certificate('commonName'), qr/google\.de/, 'right name';
SKIP: {
skip 'SNI support required!', 1
unless IO::Socket::SSL->can('can_client_sni')
&& IO::Socket::SSL->can_client_sni;

$tx = $ua->get('https://google.de');
like $ua->ioloop->stream($tx->connection)
->handle->peer_certificate('commonName'), qr/google\.de/, 'right name';
}

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

2 comments on commit 64c758c

@OverlordQ
Copy link

Choose a reason for hiding this comment

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

This seems to break on my version of Perl/Linux (Debian).
Perl:

$perl -v

This is perl 5, version 14, subversion 2 (v5.14.2) built for x86_64-linux-gnu-thread-multi

Linux:
Linux lovecraft 3.2.0-4-amd64 #1 SMP Debian 3.2.46-1 x86_64 GNU/Linux

Error:

Can't create listen socket: IO::Socket::INET: Protocol not available at lib//Mojo/IOLoop.pm line 117
 at lib//Mojo/IOLoop/Server.pm line 75
        Mojo::IOLoop::Server::listen('Mojo::IOLoop::Server=HASH(0x2f66168)', 'HASH(0x2f064b8)') called at lib//Mojo/IOLoop.pm line 117
        Mojo::IOLoop::server('Mojo::IOLoop=HASH(0x266d188)', 'HASH(0x2f064b8)') called at lib//Mojo/Server/Daemon.pm line 200
        Mojo::Server::Daemon::_listen('Mojo::Server::Daemon=HASH(0x2fa2608)', 'http://127.0.0.1:34702?reuse=1') called at lib//Mojo/Server/Daemon.pm line 64
        Mojo::Server::Daemon::start('Mojo::Server::Daemon=HASH(0x2fa2608)') called at t/mojo/app.t line 260
# Tests were run but no plan was declared and done_testing() was not seen.

@kraih
Copy link
Member Author

@kraih kraih commented on 64c758c Sep 8, 2013

Choose a reason for hiding this comment

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

That's a kernel/libc header files version mismatch.

Please sign in to comment.