Skip to content

Commit

Permalink
fixed IPv6 support and a bug that prevented sessions from working in …
Browse files Browse the repository at this point in the history
…embedded applications
  • Loading branch information
kraih committed Jun 20, 2012
1 parent 7600cf5 commit f925dbd
Show file tree
Hide file tree
Showing 13 changed files with 62 additions and 50 deletions.
3 changes: 2 additions & 1 deletion Changes
Expand Up @@ -9,14 +9,15 @@
- Switched from HMAC-MD5 to HMAC-SHA1 for signed cookies.
- Added j and r functions to ojo. (sharifulin, sri)
- Added idle_interval attribute to Mojo::IOLoop.
- Added support for MOJO_SECRET environment variable.
- Added support for new HTTP status code.
- Modernized ".perltidyrc".
- Improved Mojolicious::Plugin::Mount to automatically synchronize secrets.
- Improved to method in Mojolicious::Routes::Route to give easier access to
default parameters.
- Improved message parser performance slightly.
- Improved documentation. (ichesnokov, sri)
- Improved tests.
- Fixed bug that prevented sessions from working in embedded applications.
- Fixed JSON Pointer escaping.
- Fixed small JSON Pointer bug in get command. (avkhozov)

Expand Down
2 changes: 1 addition & 1 deletion lib/Mojo/IOLoop.pm
Expand Up @@ -341,7 +341,7 @@ L<Mojo::IOLoop> is a very minimalistic reactor based on L<Mojo::Reactor>, it
has been reduced to the absolute minimal feature set required to build solid
and scalable non-blocking TCP clients and servers.
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.12+) and
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and
L<IO::Socket::SSL> (1.75+) are supported transparently and used if installed.
Individual features can also be disabled with the C<MOJO_NO_IPV6> and
C<MOJO_NO_TLS> environment variables.
Expand Down
82 changes: 46 additions & 36 deletions lib/Mojo/IOLoop/Client.pm
@@ -1,14 +1,15 @@
package Mojo::IOLoop::Client;
use Mojo::Base 'Mojo::EventEmitter';

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

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

# TLS support requires IO::Socket::SSL
use constant TLS => $ENV{MOJO_NO_TLS} ? 0
Expand All @@ -35,10 +36,10 @@ sub connect {

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

sub _connect {
Expand All @@ -48,7 +49,7 @@ sub _connect {
my $handle;
my $reactor = $self->reactor;
my $address = $args->{address} ||= 'localhost';
unless ($handle = $args->{handle}) {
unless ($handle = $self->{handle} = $args->{handle}) {
my %options = (
Blocking => 0,
PeerAddr => $address eq 'localhost' ? '127.0.0.1' : $address,
Expand All @@ -59,19 +60,36 @@ sub _connect {
$options{PeerAddr} =~ s/[\[\]]//g if $options{PeerAddr};
my $class = IPV6 ? 'IO::Socket::IP' : 'IO::Socket::INET';
return $self->emit_safe(error => "Couldn't connect")
unless $handle = $class->new(%options);
unless $self->{handle} = $handle = $class->new(%options);

# Timer
# Timeout
$self->{timer} = $reactor->timer($args->{timeout} || 10,
sub { $self->emit_safe(error => 'Connect timeout') });
}
$handle->blocking(0);

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

# "Have you ever seen that Blue Man Group? Total ripoff of the Smurfs.
# And the Smurfs, well, they SUCK."
sub _connecting {
my ($self, $args) = @_;

# Retry or handle exceptions
my $handle = $self->{handle};
return $! == EINPROGRESS ? undef : $self->emit_safe(error => $!)
if IPV6 && !$handle->connect;
return $self->emit_safe(error => $! = $handle->sockopt(SO_ERROR))
if !IPV6 && !$handle->connected;

# Disable Nagle's algorithm
setsockopt $handle, IPPROTO_TCP, TCP_NODELAY, 1;

# TLS
weaken $self;
if ($args->{tls} && !$handle->isa('IO::Socket::SSL')) {

# No TLS support
Expand All @@ -80,53 +98,45 @@ sub _connect {
unless TLS;

# Upgrade
weaken $self;
my %options = (
SSL_ca_file => $args->{tls_ca}
&& -T $args->{tls_ca} ? $args->{tls_ca} : undef,
SSL_cert_file => $args->{tls_cert},
SSL_error_trap => sub {
$self->_cleanup;
$self->emit_safe(error => $_[1]);
},
SSL_hostname => $args->{address},
SSL_key_file => $args->{tls_key},
SSL_startHandshake => 0,
SSL_verify_mode => $args->{tls_ca} ? 0x01 : 0x00,
SSL_verifycn_name => $args->{address},
SSL_cert_file => $args->{tls_cert},
SSL_error_trap => sub { $self->_cleanup->emit_safe(error => $_[1]) },
SSL_hostname => $args->{address},
SSL_key_file => $args->{tls_key},
SSL_startHandshake => 0,
SSL_verify_mode => $args->{tls_ca} ? 0x01 : 0x00,
SSL_verifycn_name => $args->{address},
SSL_verifycn_scheme => $args->{tls_ca} ? 'http' : undef
);
$self->{tls} = 1;
my $reactor = $self->reactor;
$reactor->remove($handle);
return $self->emit_safe(error => 'TLS upgrade failed')
unless $handle = IO::Socket::SSL->start_SSL($handle, %options);
return $reactor->io($handle => sub { $self->_tls })->watch($handle, 0, 1);
}

# Wait for handle to become writable
$self->{handle} = $handle;
$reactor->io($handle => sub { $self->_connecting })->watch($handle, 0, 1);
# Connected
$self->_cleanup->emit_safe(connect => $handle);
}

# "Have you ever seen that Blue Man Group? Total ripoff of the Smurfs.
# And the Smurfs, well, they SUCK."
sub _connecting {
sub _tls {
my $self = shift;

# Switch between reading and writing
my $handle = $self->{handle};
my $reactor = $self->reactor;
my $handle = $self->{handle};
if ($self->{tls} && !$handle->connect_SSL) {
my $err = $IO::Socket::SSL::SSL_ERROR;
if ($err == TLS_READ) { $reactor->watch($handle, 1, 0) }
elsif ($err == TLS_WRITE) { $reactor->watch($handle, 1, 1) }
if ($err == TLS_READ) { $self->reactor->watch($handle, 1, 0) }
elsif ($err == TLS_WRITE) { $self->reactor->watch($handle, 1, 1) }
return;
}

# Check for errors
return $self->emit_safe(error => $! = $handle->sockopt(SO_ERROR))
unless $handle->connected;

# Connected
$self->_cleanup;
$self->emit_safe(connect => $handle);
$self->_cleanup->emit_safe(connect => $handle);
}

1;
Expand Down Expand Up @@ -199,7 +209,7 @@ 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.75+) and IPv6 support on L<IO::Socket::IP> (0.12+).
L<IO::Socket::SSL> (1.75+) and IPv6 support on L<IO::Socket::IP> (0.16+).
These options are currently available:
Expand Down
4 changes: 2 additions & 2 deletions lib/Mojo/IOLoop/Server.pm
Expand Up @@ -11,7 +11,7 @@ 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.12 (); 1';
: eval 'use IO::Socket::IP 0.16 (); 1';

# TLS support requires IO::Socket::SSL
use constant TLS => $ENV{MOJO_NO_TLS} ? 0
Expand Down Expand Up @@ -227,7 +227,7 @@ implements the following new ones.
$server->listen(port => 3000);
Create a new listen socket. Note that TLS support depends on
L<IO::Socket::SSL> (1.75+) and IPv6 support on L<IO::Socket::IP> (0.12+).
L<IO::Socket::SSL> (1.75+) and IPv6 support on L<IO::Socket::IP> (0.16+).
These options are currently available:
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojo/Server/Daemon.pm
Expand Up @@ -298,7 +298,7 @@ Mojo::Server::Daemon - Non-blocking I/O HTTP 1.1 and WebSocket server
L<Mojo::Server::Daemon> is a full featured non-blocking I/O HTTP 1.1 and
WebSocket server with C<IPv6>, C<TLS> and C<libev> support.
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.12+) and
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and
L<IO::Socket::SSL> (1.75+) are supported transparently and used if installed.
Individual features can also be disabled with the C<MOJO_NO_IPV6> and
C<MOJO_NO_TLS> environment variables.
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojo/Server/Hypnotoad.pm
Expand Up @@ -382,7 +382,7 @@ You can run the exact same command again for automatic hot deployment.
For L<Mojolicious> and L<Mojolicious::Lite> applications it will default to
C<production> mode.
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.12+) and
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and
L<IO::Socket::SSL> (1.75+) are supported transparently and used if installed.
Individual features can also be disabled with the C<MOJO_NO_IPV6> and
C<MOJO_NO_TLS> environment variables.
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojo/Server/Morbo.pm
Expand Up @@ -130,7 +130,7 @@ To start applications with it you can use the L<morbo> script.
$ morbo myapp.pl
Server available at http://127.0.0.1:3000.
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.12+) and
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and
L<IO::Socket::SSL> (1.75+) are supported transparently and used if installed.
Individual features can also be disabled with the C<MOJO_NO_IPV6> and
C<MOJO_NO_TLS> environment variables.
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojo/UserAgent.pm
Expand Up @@ -603,7 +603,7 @@ Mojo::UserAgent - Non-blocking I/O HTTP 1.1 and WebSocket user agent
L<Mojo::UserAgent> is a full featured non-blocking I/O HTTP 1.1 and WebSocket
user agent with C<IPv6>, C<TLS> and C<libev> support.
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.12+) and
Optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and
L<IO::Socket::SSL> (1.75+) are supported transparently and used if installed.
Individual features can also be disabled with the C<MOJO_NO_IPV6> and
C<MOJO_NO_TLS> environment variables.
Expand Down
1 change: 1 addition & 0 deletions lib/Mojolicious.pm
Expand Up @@ -144,6 +144,7 @@ sub handler {
# Embedded application
my $stash = {};
if (my $sub = $tx->can('stash')) { ($stash, $tx) = ($tx->$sub, $tx->tx) }
$stash->{'mojo.secret'} //= $self->secret;

# Build default controller
my $defaults = $self->defaults;
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Controller.pm
Expand Up @@ -407,7 +407,7 @@ sub signed_cookie {
my ($self, $name, $value, $options) = @_;

# Response cookie
my $secret = $self->app->secret;
my $secret = $self->stash->{'mojo.secret'};
return $self->cookie($name,
"$value--" . Mojo::Util::hmac_sha1_sum($value, $secret), $options)
if defined $value;
Expand Down
6 changes: 3 additions & 3 deletions lib/Mojolicious/Plugin/Mount.pm
Expand Up @@ -9,8 +9,8 @@ sub register {
my ($self, $app, $conf) = @_;

# Load application
my $path = (keys %$conf)[0];
my $e = Mojo::Server->new->load_app($conf->{$path})->secret($app->secret);
my $path = (keys %$conf)[0];
my $embed = Mojo::Server->new->load_app($conf->{$path});

# Extract host
my $host;
Expand All @@ -20,7 +20,7 @@ sub register {
}

# Generate route
my $route = $app->routes->route($path)->detour(app => $e);
my $route = $app->routes->route($path)->detour(app => $embed);
$route->over(host => $host) if $host;

return $route;
Expand Down
2 changes: 1 addition & 1 deletion t/mojo/ioloop_ipv6.t
Expand Up @@ -7,7 +7,7 @@ use Test::More;
use Mojo::IOLoop::Server;
plan skip_all => 'set TEST_IPV6 to enable this test (developer only!)'
unless $ENV{TEST_IPV6};
plan skip_all => 'IO::Socket::IP 0.12 required for this test!'
plan skip_all => 'IO::Socket::IP 0.16 required for this test!'
unless Mojo::IOLoop::Server::IPV6;
plan tests => 2;

Expand Down
2 changes: 1 addition & 1 deletion t/mojo/user_agent_online.t
Expand Up @@ -10,7 +10,7 @@ use Test::More;
use Mojo::IOLoop::Server;
plan skip_all => 'set TEST_ONLINE to enable this test (developer only!)'
unless $ENV{TEST_ONLINE};
plan skip_all => 'IO::Socket::IP 0.12 required for this test!'
plan skip_all => 'IO::Socket::IP 0.16 required for this test!'
unless Mojo::IOLoop::Server::IPV6;
plan skip_all => 'IO::Socket::SSL 1.75 required for this test!'
unless Mojo::IOLoop::Server::TLS;
Expand Down

0 comments on commit f925dbd

Please sign in to comment.