Hello community,
here is the log from the commit of package perl-Mojolicious for openSUSE:Factory checked in at 2016-09-05 21:18:13
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Mojolicious (Old)
and /work/SRC/openSUSE:Factory/.perl-Mojolicious.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "perl-Mojolicious"
Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-Mojolicious/perl-Mojolicious.changes 2016-08-22 10:49:21.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.perl-Mojolicious.new/perl-Mojolicious.changes 2016-09-05 21:18:16.000000000 +0200
@@ -1,0 +2,20 @@
+Wed Aug 31 05:58:50 UTC 2016 - coolo@suse.com
+
+- updated to 7.05
+ see /usr/share/doc/packages/perl-Mojolicious/Changes
+
+ 7.05 2016-08-29
+ - Fixed bug in Mojo::IOLoop::Subprocess where EV would steal the subprocess
+ exit status.
+
+ 7.04 2016-08-28
+ - Added EXPERIMENTAL support for performing computationally expensive
+ operations in subprocesses, without blocking the event loop. (jberger, sri)
+ - Added EXPERIMENTAL module Mojo::IOLoop::Subprocess. (jberger, sri)
+ - Added EXPERIMENTAL subprocess method to Mojo::IOLoop. (jberger, sri)
+ - Improved many methods in Mojolicious::Controller to die more gracefully if
+ the connection has already been closed.
+ - Fixed bug where Mojo::UserAgent would try to follow redirects for CONNECT
+ requests.
+
+-------------------------------------------------------------------
Old:
----
Mojolicious-7.03.tar.gz
New:
----
Mojolicious-7.05.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ perl-Mojolicious.spec ++++++
--- /var/tmp/diff_new_pack.lMxJpM/_old 2016-09-05 21:18:18.000000000 +0200
+++ /var/tmp/diff_new_pack.lMxJpM/_new 2016-09-05 21:18:18.000000000 +0200
@@ -17,7 +17,7 @@
Name: perl-Mojolicious
-Version: 7.03
+Version: 7.05
Release: 0
%define cpan_name Mojolicious
Summary: Real-time web framework
++++++ Mojolicious-7.03.tar.gz -> Mojolicious-7.05.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/Changes new/Mojolicious-7.05/Changes
--- old/Mojolicious-7.03/Changes 2016-08-17 18:37:48.000000000 +0200
+++ new/Mojolicious-7.05/Changes 2016-08-29 17:10:48.000000000 +0200
@@ -1,4 +1,18 @@
+7.05 2016-08-29
+ - Fixed bug in Mojo::IOLoop::Subprocess where EV would steal the subprocess
+ exit status.
+
+7.04 2016-08-28
+ - Added EXPERIMENTAL support for performing computationally expensive
+ operations in subprocesses, without blocking the event loop. (jberger, sri)
+ - Added EXPERIMENTAL module Mojo::IOLoop::Subprocess. (jberger, sri)
+ - Added EXPERIMENTAL subprocess method to Mojo::IOLoop. (jberger, sri)
+ - Improved many methods in Mojolicious::Controller to die more gracefully if
+ the connection has already been closed.
+ - Fixed bug where Mojo::UserAgent would try to follow redirects for CONNECT
+ requests.
+
7.03 2016-08-17
- Fixed packaging errors.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/MANIFEST new/Mojolicious-7.05/MANIFEST
--- old/Mojolicious-7.03/MANIFEST 2016-08-17 18:39:29.000000000 +0200
+++ new/Mojolicious-7.05/MANIFEST 2016-08-29 18:43:03.000000000 +0200
@@ -37,6 +37,7 @@
lib/Mojo/IOLoop/resources/server.key
lib/Mojo/IOLoop/Server.pm
lib/Mojo/IOLoop/Stream.pm
+lib/Mojo/IOLoop/Subprocess.pm
lib/Mojo/JSON.pm
lib/Mojo/JSON/Pointer.pm
lib/Mojo/Loader.pm
@@ -206,6 +207,7 @@
t/mojo/request.t
t/mojo/request_cgi.t
t/mojo/response.t
+t/mojo/subprocess.t
t/mojo/template.t
t/mojo/templates/exception.mt
t/mojo/templates/test.mt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/META.json new/Mojolicious-7.05/META.json
--- old/Mojolicious-7.03/META.json 2016-08-17 18:39:28.000000000 +0200
+++ new/Mojolicious-7.05/META.json 2016-08-29 18:43:03.000000000 +0200
@@ -4,7 +4,7 @@
"Sebastian Riedel "
],
"dynamic_config" : 0,
- "generated_by" : "ExtUtils::MakeMaker version 7.22, CPAN::Meta::Converter version 2.150005",
+ "generated_by" : "ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010",
"license" : [
"artistic_2"
],
@@ -58,6 +58,6 @@
},
"x_IRC" : "irc://irc.perl.org/#mojo"
},
- "version" : "7.03",
+ "version" : "7.05",
"x_serialization_backend" : "JSON::PP version 2.27400"
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/META.yml new/Mojolicious-7.05/META.yml
--- old/Mojolicious-7.03/META.yml 2016-08-17 18:39:28.000000000 +0200
+++ new/Mojolicious-7.05/META.yml 2016-08-29 18:43:03.000000000 +0200
@@ -7,7 +7,7 @@
configure_requires:
ExtUtils::MakeMaker: '0'
dynamic_config: 0
-generated_by: 'ExtUtils::MakeMaker version 7.22, CPAN::Meta::Converter version 2.150005'
+generated_by: 'ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010'
license: artistic_2
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -31,5 +31,5 @@
homepage: http://mojolicious.org
license: http://www.opensource.org/licenses/artistic-license-2.0
repository: https://github.com/kraih/mojo.git
-version: '7.03'
+version: '7.05'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/lib/Mojo/IOLoop/Stream.pm new/Mojolicious-7.05/lib/Mojo/IOLoop/Stream.pm
--- old/Mojolicious-7.03/lib/Mojo/IOLoop/Stream.pm 2016-07-19 02:38:18.000000000 +0200
+++ new/Mojolicious-7.05/lib/Mojo/IOLoop/Stream.pm 2016-08-27 04:52:25.000000000 +0200
@@ -8,8 +8,6 @@
has reactor => sub { Mojo::IOLoop->singleton->reactor };
-sub DESTROY { Mojo::Util::_global_destruction() or shift->close }
-
sub close {
my $self = shift;
return unless my $reactor = $self->reactor;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/lib/Mojo/IOLoop/Subprocess.pm new/Mojolicious-7.05/lib/Mojo/IOLoop/Subprocess.pm
--- old/Mojolicious-7.03/lib/Mojo/IOLoop/Subprocess.pm 1970-01-01 01:00:00.000000000 +0100
+++ new/Mojolicious-7.05/lib/Mojo/IOLoop/Subprocess.pm 2016-08-29 17:32:08.000000000 +0200
@@ -0,0 +1,148 @@
+package Mojo::IOLoop::Subprocess;
+use Mojo::Base -base;
+
+use Carp 'croak';
+use Config;
+use Mojo::IOLoop;
+use Mojo::IOLoop::Stream;
+use POSIX ();
+use Storable;
+
+has deserialize => sub { \&Storable::thaw };
+has ioloop => sub { Mojo::IOLoop->singleton };
+has serialize => sub { \&Storable::freeze };
+
+sub pid { shift->{pid} }
+
+sub run {
+ my ($self, $child, $parent) = @_;
+
+ # No fork emulation support
+ croak 'Subprocesses do not support fork emulation' if $Config{d_pseudofork};
+
+ # Pipe for subprocess communication
+ pipe(my $reader, my $writer) or croak "Can't create pipe: $!";
+ $writer->autoflush(1);
+
+ # Child
+ croak "Can't fork: $!" unless defined(my $pid = $self->{pid} = fork);
+ unless ($pid) {
+ $self->ioloop->reset;
+ my $results = eval { [$self->$child] } || [];
+ print $writer $self->serialize->([$@, @$results]);
+ POSIX::_exit(0);
+ }
+
+ # Parent
+ my $stream = Mojo::IOLoop::Stream->new($reader);
+ $self->ioloop->stream($stream);
+ my $buffer = '';
+ $stream->on(read => sub { $buffer .= pop });
+ $stream->on(
+ close => sub {
+ waitpid $pid, 0;
+ my $results = eval { $self->deserialize->($buffer) } || [];
+ $self->$parent(shift(@$results) // $@, @$results);
+ }
+ );
+ return $self;
+}
+
+1;
+
+=encoding utf8
+
+=head1 NAME
+
+Mojo::IOLoop::Subprocess - Subprocesses
+
+=head1 SYNOPSIS
+
+ use Mojo::IOLoop::Subprocess;
+
+ # Operation that would block the event loop for 5 seconds
+ my $subprocess = Mojo::IOLoop::Subprocess->new;
+ $subprocess->run(
+ sub {
+ my $subprocess = shift;
+ sleep 5;
+ return '♥', 'Mojolicious';
+ },
+ sub {
+ my ($subprocess, $err, @results) = @_;
+ say "I $results[0] $results[1]!";
+ }
+ );
+
+ # Start event loop if necessary
+ $subprocess->ioloop->start unless $subprocess->ioloop->is_running;
+
+=head1 DESCRIPTION
+
+LMojo::IOLoop::Subprocess allows LMojo::IOLoop to perform computationally
+expensive operations in subprocesses, without blocking the event loop. Note that
+this module is EXPERIMENTAL and might change without warning!
+
+=head1 ATTRIBUTES
+
+LMojo::IOLoop::Subprocess implements the following attributes.
+
+=head2 deserialize
+
+ my $cb = $subprocess->deserialize;
+ $subprocess = $subprocess->deserialize(sub {...});
+
+A callback used to deserialize subprocess return values, defaults to using
+L<Storable>.
+
+ $subprocess->deserialize(sub {
+ my $bytes = shift;
+ return [];
+ });
+
+=head2 ioloop
+
+ my $loop = $subprocess->ioloop;
+ $subprocess = $subprocess->ioloop(Mojo::IOLoop->new);
+
+Event loop object to control, defaults to the global LMojo::IOLoop singleton.
+
+=head2 serialize
+
+ my $cb = $subprocess->serialize;
+ $subprocess = $subprocess->serialize(sub {...});
+
+A callback used to serialize subprocess return values, defaults to using
+L<Storable>.
+
+ $subprocess->serialize(sub {
+ my $array = shift;
+ return '';
+ });
+
+=head1 METHODS
+
+LMojo::IOLoop::Subprocess inherits all methods from LMojo::Base and
+implements the following new ones.
+
+=head2 pid
+
+ my $pid = $subprocess->pid;
+
+Process id of the spawned subprocess if available.
+
+=head2 run
+
+ $subprocess = $subprocess->run(sub {...}, sub {...});
+
+Execute the first callback in a child process and wait for it to return one or
+more values, without blocking L"ioloop"> in the parent process. Then execute
+the second callback in the parent process with the results. The return values of
+the first callback and exceptions thrown by it, will be serialized with
+L<Storable>, so they can be shared between processes.
+
+=head1 SEE ALSO
+
+L<Mojolicious>, LMojolicious::Guides, Lhttp://mojolicious.org.
+
+=cut
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/lib/Mojo/IOLoop.pm new/Mojolicious-7.05/lib/Mojo/IOLoop.pm
--- old/Mojolicious-7.03/lib/Mojo/IOLoop.pm 2016-07-19 02:38:18.000000000 +0200
+++ new/Mojolicious-7.05/lib/Mojo/IOLoop.pm 2016-08-27 14:46:02.000000000 +0200
@@ -8,6 +8,7 @@
use Mojo::IOLoop::Delay;
use Mojo::IOLoop::Server;
use Mojo::IOLoop::Stream;
+use Mojo::IOLoop::Subprocess;
use Mojo::Reactor::Poll;
use Mojo::Util qw(md5_sum steady_time);
use Scalar::Util qw(blessed weaken);
@@ -145,6 +146,12 @@
return $c->{stream};
}
+sub subprocess {
+ my $subprocess = Mojo::IOLoop::Subprocess->new;
+ weaken $subprocess->ioloop(_instance(shift))->{ioloop};
+ return $subprocess->run(@_);
+}
+
sub timer { shift->_timer(timer => @_) }
sub _id {
@@ -595,6 +602,29 @@
# Increase inactivity timeout for connection to 300 seconds
Mojo::IOLoop->stream($id)->timeout(300);
+=head2 subprocess
+
+ my $subprocess = Mojo::IOLoop->subprocess(sub {...}, sub {...});
+ my $subprocess = $loop->subprocess(sub {...}, sub {...});
+
+Build LMojo::IOLoop::Subprocess object to perform computationally expensive
+operations in subprocesses, without blocking the event loop. Callbacks will be
+passed along to LMojo::IOLoop::Subprocess/"run". Note that this method is
+EXPERIMENTAL and might change without warning!
+
+ # Operation that would block the event loop for 5 seconds
+ Mojo::IOLoop->subprocess(
+ sub {
+ my $subprocess = shift;
+ sleep 5;
+ return '♥', 'Mojolicious';
+ },
+ sub {
+ my ($subprocess, $err, @results) = @_;
+ say "I $results[0] $results[1]!";
+ }
+ );
+
=head2 timer
my $id = Mojo::IOLoop->timer(3 => sub {...});
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/lib/Mojo/Server/Daemon.pm new/Mojolicious-7.05/lib/Mojo/Server/Daemon.pm
--- old/Mojolicious-7.03/lib/Mojo/Server/Daemon.pm 2016-08-05 16:07:06.000000000 +0200
+++ new/Mojolicious-7.05/lib/Mojo/Server/Daemon.pm 2016-08-27 04:08:11.000000000 +0200
@@ -21,9 +21,8 @@
sub DESTROY {
return if Mojo::Util::_global_destruction();
my $self = shift;
- $self->_remove($_) for keys %{$self->{connections} || {}};
my $loop = $self->ioloop;
- $loop->remove($_) for @{$self->acceptors};
+ $loop->remove($_) for keys %{$self->{connections} || {}}, @{$self->acceptors};
}
sub run {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/lib/Mojo/Transaction/HTTP.pm new/Mojolicious-7.05/lib/Mojo/Transaction/HTTP.pm
--- old/Mojolicious-7.03/lib/Mojo/Transaction/HTTP.pm 2016-07-19 02:38:18.000000000 +0200
+++ new/Mojolicious-7.05/lib/Mojo/Transaction/HTTP.pm 2016-08-26 22:02:30.000000000 +0200
@@ -93,16 +93,13 @@
# Switch to body
if ($self->{write} <= 0) {
- $self->{offset} = 0;
+ @$self{qw(http_state offset)} = ('body', 0);
# Response without body
if ($head && $self->is_empty) { $self->completed }
# Body
- else {
- $self->{http_state} = 'body';
- $self->{write} = $msg->content->is_dynamic ? 1 : $msg->body_size;
- }
+ else { $self->{write} = $msg->content->is_dynamic ? 1 : $msg->body_size }
}
return $buffer;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/lib/Mojo/UserAgent/Transactor.pm new/Mojolicious-7.05/lib/Mojo/UserAgent/Transactor.pm
--- old/Mojolicious-7.03/lib/Mojo/UserAgent/Transactor.pm 2016-07-26 00:26:00.000000000 +0200
+++ new/Mojolicious-7.05/lib/Mojo/UserAgent/Transactor.pm 2016-08-21 18:02:29.000000000 +0200
@@ -71,16 +71,19 @@
my $code = $res->code // 0;
return undef unless grep { $_ == $code } 301, 302, 303, 307, 308;
+ # CONNECT requests cannot be redirected
+ my $req = $old->req;
+ return undef if uc $req->method eq 'CONNECT';
+
# Fix location without authority and/or scheme
return undef unless my $location = $res->headers->location;
$location = Mojo::URL->new($location);
- $location = $location->base($old->req->url)->to_abs unless $location->is_abs;
+ $location = $location->base($req->url)->to_abs unless $location->is_abs;
my $proto = $location->protocol;
return undef if ($proto ne 'http' && $proto ne 'https') || !$location->host;
# Clone request if necessary
my $new = Mojo::Transaction::HTTP->new;
- my $req = $old->req;
if ($code == 307 || $code == 308) {
return undef unless my $clone = $req->clone;
$new->req($clone);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/lib/Mojolicious/Controller.pm new/Mojolicious-7.05/lib/Mojolicious/Controller.pm
--- old/Mojolicious-7.03/lib/Mojolicious/Controller.pm 2016-07-19 02:38:18.000000000 +0200
+++ new/Mojolicious-7.05/lib/Mojolicious/Controller.pm 2016-08-27 14:19:33.000000000 +0200
@@ -106,7 +106,7 @@
my $self = shift;
# WebSocket
- my $tx = $self->tx;
+ my $tx = $self->tx || Carp::croak 'Connection already closed';
$tx->finish(@_) and return $tx->established ? $self : $self->rendered(101)
if $tx->is_websocket;
@@ -137,7 +137,7 @@
sub on {
my ($self, $name, $cb) = @_;
- my $tx = $self->tx;
+ my $tx = $self->tx || Carp::croak 'Connection already closed';
$self->rendered(101) if $tx->is_websocket && !$tx->established;
return $tx->on($name => sub { shift; $self->$cb(@_) });
}
@@ -218,8 +218,8 @@
return $self;
}
-sub req { shift->tx->req }
-sub res { shift->tx->res }
+sub req { (shift->tx || Carp::croak 'Connection already closed')->req }
+sub res { (shift->tx || Carp::croak 'Connection already closed')->res }
sub respond_to {
my ($self, $args) = (shift, ref $_[0] ? $_[0] : {@_});
@@ -248,7 +248,7 @@
sub send {
my ($self, $msg, $cb) = @_;
- my $tx = $self->tx;
+ my $tx = $self->tx || Carp::croak 'Connection already closed';
Carp::croak 'No WebSocket connection to send message to'
unless $tx->is_websocket;
$tx->send($msg, $cb ? sub { shift; $self->$cb(@_) } : ());
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/lib/Mojolicious/Guides/FAQ.pod new/Mojolicious-7.05/lib/Mojolicious/Guides/FAQ.pod
--- old/Mojolicious-7.03/lib/Mojolicious/Guides/FAQ.pod 2016-08-01 02:04:12.000000000 +0200
+++ new/Mojolicious-7.05/lib/Mojolicious/Guides/FAQ.pod 2016-08-27 14:23:36.000000000 +0200
@@ -259,6 +259,17 @@
defaults to C<20> seconds and can be extended with the attribute
LMojo::Server::Prefork/"heartbeat_timeout" if your application requires it.
+=head2 What does "Connection already closed" mean?
+
+This error message usually appears after waiting for the results of a
+non-blocking operation for longer periods of time, because the underlying
+connection has been closed in the meantime and the value of the attribute
+LMojolicious::Controller/"tx" is no longer available. While there might not be
+a way to prevent the connection from getting closed, you can also avoid this
+error message by keeping a reference to the transaction object that is not
+weakened. The helper LMojolicious::Plugin::DefaultHelpers/"delay" will do this
+automatically for you.
+
=head1 MORE
You can continue with LMojolicious::Guides now or take a look at the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/lib/Mojolicious/Guides.pod new/Mojolicious-7.05/lib/Mojolicious/Guides.pod
--- old/Mojolicious-7.03/lib/Mojolicious/Guides.pod 2016-07-19 02:38:18.000000000 +0200
+++ new/Mojolicious-7.05/lib/Mojolicious/Guides.pod 2016-08-26 19:36:13.000000000 +0200
@@ -323,6 +323,8 @@
=item * LMojo::Home
+=item * LMojo::IOLoop::Subprocess
+
=item * LMojo::JSON::Pointer
=item * LMojo::Parameters
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/lib/Mojolicious/Validator/Validation.pm new/Mojolicious-7.05/lib/Mojolicious/Validator/Validation.pm
--- old/Mojolicious-7.03/lib/Mojolicious/Validator/Validation.pm 2016-07-19 02:38:18.000000000 +0200
+++ new/Mojolicious-7.05/lib/Mojolicious/Validator/Validation.pm 2016-08-23 13:24:27.000000000 +0200
@@ -257,7 +257,6 @@
Change validation L"topic">, apply filters, and make sure a value is present
and not an empty string. All filters from LMojolicious::Validator/"FILTERS"
-are supported.All filters from LMojolicious::Validator/"FILTERS"
are supported.
# Trim value and check size
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/lib/Mojolicious.pm new/Mojolicious-7.05/lib/Mojolicious.pm
--- old/Mojolicious-7.03/lib/Mojolicious.pm 2016-08-17 18:34:19.000000000 +0200
+++ new/Mojolicious-7.05/lib/Mojolicious.pm 2016-08-29 16:00:05.000000000 +0200
@@ -43,7 +43,7 @@
has validator => sub { Mojolicious::Validator->new };
our $CODENAME = 'Doughnut';
-our $VERSION = '7.03';
+our $VERSION = '7.05';
sub AUTOLOAD {
my $self = shift;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/t/mojo/subprocess.t new/Mojolicious-7.05/t/mojo/subprocess.t
--- old/Mojolicious-7.03/t/mojo/subprocess.t 1970-01-01 01:00:00.000000000 +0100
+++ new/Mojolicious-7.05/t/mojo/subprocess.t 2016-08-29 17:43:27.000000000 +0200
@@ -0,0 +1,148 @@
+use Mojo::Base -strict;
+
+BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' }
+
+use Test::More;
+
+plan skip_all => 'set TEST_SUBPROCESS to enable this test (developer only!)'
+ unless $ENV{TEST_SUBPROCESS};
+
+use Mojo::IOLoop;
+use Mojo::IOLoop::Subprocess;
+
+# Huge result
+my ($fail, $result);
+my $subprocess = Mojo::IOLoop::Subprocess->new;
+$subprocess->run(
+ sub { shift->pid . $$ . ('x' x 100000) },
+ sub {
+ my ($subprocess, $err, $two) = @_;
+ $fail = $err;
+ $result .= $two;
+ }
+);
+$result = $$;
+Mojo::IOLoop->start;
+ok !$fail, 'no error';
+is $result, $$ . 0 . $subprocess->pid . ('x' x 100000), 'right result';
+
+# Custom event loop
+($fail, $result) = ();
+my $loop = Mojo::IOLoop->new;
+$loop->subprocess(
+ sub {'♥'},
+ sub {
+ my ($subprocess, $err, @results) = @_;
+ $fail = $err;
+ $result = \@results;
+ }
+);
+$loop->start;
+ok !$fail, 'no error';
+is_deeply $result, ['♥'], 'right structure';
+
+# Multiple return values
+($fail, $result) = ();
+$subprocess = Mojo::IOLoop::Subprocess->new;
+$subprocess->run(
+ sub { return '♥', [{two => 2}], 3 },
+ sub {
+ my ($subprocess, $err, @results) = @_;
+ $fail = $err;
+ $result = \@results;
+ }
+);
+Mojo::IOLoop->start;
+ok !$fail, 'no error';
+is_deeply $result, ['♥', [{two => 2}], 3], 'right structure';
+
+# Event loop in subprocess
+($fail, $result) = ();
+$subprocess = Mojo::IOLoop::Subprocess->new;
+$subprocess->run(
+ sub {
+ my $result;
+ Mojo::IOLoop->next_tick(sub { $result = 23 });
+ Mojo::IOLoop->start;
+ return $result;
+ },
+ sub {
+ my ($subprocess, $err, $twenty_three) = @_;
+ $fail = $err;
+ $result = $twenty_three;
+ }
+);
+Mojo::IOLoop->start;
+ok !$fail, 'no error';
+is $result, 23, 'right result';
+
+# Concurrent subprocesses
+($fail, $result) = ();
+Mojo::IOLoop->delay(
+ sub {
+ my $delay = shift;
+ Mojo::IOLoop->subprocess(sub {1}, $delay->begin);
+ Mojo::IOLoop->subprocess(sub {2}, $delay->begin);
+ },
+ sub {
+ my ($delay, $err1, $result1, $err2, $result2) = @_;
+ $fail = $err1 || $err2;
+ $result = [$result1, $result2];
+ }
+)->wait;
+ok !$fail, 'no error';
+is_deeply $result, [1, 2], 'right structure';
+
+# No result
+($fail, $result) = ();
+Mojo::IOLoop::Subprocess->new->run(
+ sub {return},
+ sub {
+ my ($subprocess, $err, @results) = @_;
+ $fail = $err;
+ $result = \@results;
+ }
+);
+Mojo::IOLoop->start;
+ok !$fail, 'no error';
+is_deeply $result, [], 'right structure';
+
+# Exception
+$fail = undef;
+Mojo::IOLoop::Subprocess->new->run(
+ sub { die 'Whatever' },
+ sub {
+ my ($subprocess, $err) = @_;
+ $fail = $err;
+ }
+);
+Mojo::IOLoop->start;
+like $fail, qr/Whatever/, 'right error';
+
+# Non-zero exit status
+$fail = undef;
+Mojo::IOLoop::Subprocess->new->run(
+ sub { exit 3 },
+ sub {
+ my ($subprocess, $err) = @_;
+ $fail = $err;
+ }
+);
+Mojo::IOLoop->start;
+like $fail, qr/Storable/, 'right error';
+
+# Serialization error
+$fail = undef;
+$subprocess = Mojo::IOLoop::Subprocess->new;
+$subprocess->deserialize(sub { die 'Whatever' });
+$subprocess->run(
+ sub { 1 + 1 },
+ sub {
+ my ($subprocess, $err) = @_;
+ $fail = $err;
+ }
+);
+Mojo::IOLoop->start;
+like $fail, qr/Whatever/, 'right error';
+
+done_testing();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/t/mojo/transactor.t new/Mojolicious-7.05/t/mojo/transactor.t
--- old/Mojolicious-7.03/t/mojo/transactor.t 2016-07-19 02:38:18.000000000 +0200
+++ new/Mojolicious-7.05/t/mojo/transactor.t 2016-08-21 17:49:10.000000000 +0200
@@ -927,6 +927,12 @@
is $tx->res->code, undef, 'no status';
is $tx->res->headers->location, undef, 'no "Location" value';
+# 302 redirect for CONNECT request
+$tx = $t->tx(CONNECT => 'http://mojolicious.org');
+$tx->res->code(302);
+$tx->res->headers->location('http://example.com/bar');
+is $t->redirect($tx), undef, 'unsupported redirect';
+
# Abstract methods
eval { Mojo::Transaction->client_read };
like $@, qr/Method "client_read" not implemented by subclass/, 'right error';
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Mojolicious-7.03/t/mojolicious/app.t new/Mojolicious-7.05/t/mojolicious/app.t
--- old/Mojolicious-7.03/t/mojolicious/app.t 2016-07-19 02:38:18.000000000 +0200
+++ new/Mojolicious-7.05/t/mojolicious/app.t 2016-08-27 05:40:16.000000000 +0200
@@ -16,6 +16,7 @@
use Mojo::Date;
use Mojo::IOLoop;
use Mojolicious;
+use Mojolicious::Controller;
use Test::Mojo;
# Missing config file
@@ -598,6 +599,20 @@
$t->get_ok('/rss.xml')->status_is(200)->content_type_is('application/rss+xml')
->content_like(qr!<\?xml version="1.0" encoding="UTF-8"\?><rss />!);
+# Connection already closed
+eval { Mojolicious::Controller->new->finish };
+like $@, qr/Connection already closed/, 'right error';
+eval {
+ Mojolicious::Controller->new->on(finish => sub { });
+};
+like $@, qr/Connection already closed/, 'right error';
+eval { Mojolicious::Controller->new->req };
+like $@, qr/Connection already closed/, 'right error';
+eval { Mojolicious::Controller->new->res };
+like $@, qr/Connection already closed/, 'right error';
+eval { Mojolicious::Controller->new->send('whatever') };
+like $@, qr/Connection already closed/, 'right error';
+
# Abstract methods
eval { Mojolicious::Plugin->register };
like $@, qr/Method "register" not implemented by subclass/, 'right error';