Hello community,
here is the log from the commit of package perl-Mojolicious for
openSUSE:Factory checked in at 2020-09-29 19:01:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Mojolicious (Old)
and /work/SRC/openSUSE:Factory/.perl-Mojolicious.new.4249 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "perl-Mojolicious"
Tue Sep 29 19:01:58 2020 rev:142 rq:838283 version:8.60
Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-Mojolicious/perl-Mojolicious.changes
2020-09-15 16:34:47.798970545 +0200
+++
/work/SRC/openSUSE:Factory/.perl-Mojolicious.new.4249/perl-Mojolicious.changes
2020-09-29 19:02:16.693808096 +0200
@@ -1,0 +2,10 @@
+Mon Sep 28 03:14:25 UTC 2020 - Tina Müller <[email protected]>
+
+- updated to 8.60
+ see /usr/share/doc/packages/perl-Mojolicious/Changes
+
+ 8.60 2020-09-27
+ - Improved reset method in Mojo::IOLoop to prevent close event to be
emitted in affected streams. (kiwiroy)
+ - Improved cookbook with Envoy deployment recipe. (zakame)
+
+-------------------------------------------------------------------
Old:
----
Mojolicious-8.59.tar.gz
New:
----
Mojolicious-8.60.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ perl-Mojolicious.spec ++++++
--- /var/tmp/diff_new_pack.WD5PBR/_old 2020-09-29 19:02:17.393808946 +0200
+++ /var/tmp/diff_new_pack.WD5PBR/_new 2020-09-29 19:02:17.397808951 +0200
@@ -17,7 +17,7 @@
Name: perl-Mojolicious
-Version: 8.59
+Version: 8.60
Release: 0
%define cpan_name Mojolicious
Summary: Real-time web framework
++++++ Mojolicious-8.59.tar.gz -> Mojolicious-8.60.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.59/Changes new/Mojolicious-8.60/Changes
--- old/Mojolicious-8.59/Changes 2020-09-05 14:16:59.000000000 +0200
+++ new/Mojolicious-8.60/Changes 2020-09-27 17:46:20.000000000 +0200
@@ -1,4 +1,8 @@
+8.60 2020-09-27
+ - Improved reset method in Mojo::IOLoop to prevent close event to be emitted
in affected streams. (kiwiroy)
+ - Improved cookbook with Envoy deployment recipe. (zakame)
+
8.59 2020-09-05
- Added l function to ojo. (kiwiroy)
- Added MOJO_PROMISE_DEBUG environment variable to Mojo::Promise.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.59/META.json
new/Mojolicious-8.60/META.json
--- old/Mojolicious-8.59/META.json 2020-09-13 18:04:04.000000000 +0200
+++ new/Mojolicious-8.60/META.json 2020-09-27 17:49:37.000000000 +0200
@@ -63,6 +63,6 @@
"web" : "https://webchat.freenode.net/#mojo"
}
},
- "version" : "8.59",
+ "version" : "8.60",
"x_serialization_backend" : "JSON::PP version 4.05"
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.59/META.yml
new/Mojolicious-8.60/META.yml
--- old/Mojolicious-8.59/META.yml 2020-09-13 18:04:04.000000000 +0200
+++ new/Mojolicious-8.60/META.yml 2020-09-27 17:49:37.000000000 +0200
@@ -34,5 +34,5 @@
homepage: https://mojolicious.org
license: http://www.opensource.org/licenses/artistic-license-2.0
repository: https://github.com/mojolicious/mojo.git
-version: '8.59'
+version: '8.60'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.59/lib/Mojo/IOLoop.pm
new/Mojolicious-8.60/lib/Mojo/IOLoop.pm
--- old/Mojolicious-8.59/lib/Mojo/IOLoop.pm 2020-09-07 17:42:33.000000000
+0200
+++ new/Mojolicious-8.60/lib/Mojo/IOLoop.pm 2020-09-27 02:33:25.000000000
+0200
@@ -95,6 +95,7 @@
sub reset {
my $self = _instance(shift)->emit('reset');
+ $self->stream($_)->unsubscribe('close') for (keys %{$self->{in}}, keys
%{$self->{out}});
delete @$self{qw(accepting acceptors events in out stop)};
$self->reactor->reset;
$self->stop;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.59/lib/Mojolicious/Guides/Cookbook.pod
new/Mojolicious-8.60/lib/Mojolicious/Guides/Cookbook.pod
--- old/Mojolicious-8.59/lib/Mojolicious/Guides/Cookbook.pod 2020-09-04
00:57:31.000000000 +0200
+++ new/Mojolicious-8.60/lib/Mojolicious/Guides/Cookbook.pod 2020-09-27
01:01:58.000000000 +0200
@@ -342,6 +342,56 @@
ScriptAlias / /home/sri/my_app/script/my_app/
+=head2 Envoy
+
+L<Mojolicious> applications can be deployed on "cloud-native" environments
that use L<Envoy|https://www.envoyproxy.io>,
+such as with this reverse proxy configuration similar to the Apache and Nginx
ones above.
+
+ static_resources:
+ listeners:
+ - name: listener_0
+ address:
+ socket_address: { address: 0.0.0.0, port_value: 80 }
+ filter_chains:
+ - filters:
+ - name: envoy.filters.network.http_connection_manager
+ typed_config:
+ "@type":
type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ codec_type: auto
+ stat_prefix: index_http
+ route_config:
+ name: local_route
+ virtual_hosts:
+ - name: service
+ domains: ["*"]
+ routes:
+ - match:
+ prefix: "/"
+ route:
+ cluster: local_service
+ upgrade_configs:
+ - upgrade_type: websocket
+ http_filters:
+ - name: envoy.filters.http.router
+ typed_config:
+ clusters:
+ - name: local_service
+ connect_timeout: 0.25s
+ type: strict_dns
+ lb_policy: round_robin
+ load_assignment:
+ cluster_name: local_service
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address: { address: mojo, port_value: 8080 }
+
+While this configuration works for simple applications, Envoy's typical use
case is for implementing proxies of
+applications as a "service mesh" providing advanced filtering, load balancing,
and observability features, such as
+seen in L<Istio|https://istio.io/latest/docs/ops/deployment/architecture/>.
For more examples, visit the
+L<Envoy documentation|https://www.envoyproxy.io/docs/envoy/latest/start/start>.
+
=head2 PSGI/Plack
L<PSGI> is an interface between Perl web frameworks and web servers, and
L<Plack> is a Perl module and toolkit that
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.59/lib/Mojolicious/Guides/Routing.pod
new/Mojolicious-8.60/lib/Mojolicious/Guides/Routing.pod
--- old/Mojolicious-8.59/lib/Mojolicious/Guides/Routing.pod 2020-08-19
22:42:00.000000000 +0200
+++ new/Mojolicious-8.60/lib/Mojolicious/Guides/Routing.pod 2020-09-27
17:47:44.000000000 +0200
@@ -376,7 +376,7 @@
# /foo/marcus -> {controller => 'foo', action => 'bar', user => 'marcus'}
$r->get('/foo/:user')->to('foo#bar')->name('baz');
- # Generate URL "/foo/marcus" for route "baz"
+ # Generate URL "/foo/marcus" for route "baz" (in previous request context)
my $url = $c->url_for('baz');
# Generate URL "/foo/jan" for route "baz"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.59/lib/Mojolicious.pm
new/Mojolicious-8.60/lib/Mojolicious.pm
--- old/Mojolicious-8.59/lib/Mojolicious.pm 2020-09-07 17:42:23.000000000
+0200
+++ new/Mojolicious-8.60/lib/Mojolicious.pm 2020-09-19 20:54:34.000000000
+0200
@@ -58,7 +58,7 @@
has validator => sub { Mojolicious::Validator->new };
our $CODENAME = 'Supervillain';
-our $VERSION = '8.59';
+our $VERSION = '8.60';
sub BUILD_DYNAMIC {
my ($class, $method, $dyn_methods) = @_;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/Mojolicious-8.59/t/mojo/ioloop.t
new/Mojolicious-8.60/t/mojo/ioloop.t
--- old/Mojolicious-8.59/t/mojo/ioloop.t 2020-09-07 17:43:04.000000000
+0200
+++ new/Mojolicious-8.60/t/mojo/ioloop.t 2020-09-27 02:33:17.000000000
+0200
@@ -10,333 +10,354 @@
use Mojo::IOLoop::Server;
use Mojo::IOLoop::Stream;
-# Defaults
-my $loop = Mojo::IOLoop->new;
-is $loop->max_connections, 1000, 'right default';
-$loop = Mojo::IOLoop->new(max_connections => 10);
-is $loop->max_connections, 10, 'right value';
-
-# Double start
-my $err;
-Mojo::IOLoop->next_tick(sub {
- my $loop = shift;
- eval { $loop->start };
- $err = $@;
- $loop->stop;
-});
-Mojo::IOLoop->start;
-like $err, qr/^Mojo::IOLoop already running/, 'right error';
-
-# Double one_tick
-$err = undef;
-Mojo::IOLoop->next_tick(sub {
- my $loop = shift;
- eval { $loop->one_tick };
- $err = $@;
-});
-Mojo::IOLoop->one_tick;
-like $err, qr/^Mojo::IOLoop already running/, 'right error';
-
-# Basic functionality
-my ($ticks, $timer, $hirestimer);
-my $id = $loop->recurring(0 => sub { $ticks++ });
-$loop->timer(
- 1 => sub {
- shift->timer(0 => sub { shift->stop });
- $timer++;
- }
-);
-$loop->timer(0.25 => sub { $hirestimer++ });
-$loop->start;
-ok $timer, 'recursive timer works';
-ok $hirestimer, 'hires timer works';
-$loop->one_tick;
-ok $ticks > 2, 'more than two ticks';
-
-# Run again without first tick event handler
-my $before = $ticks;
-my $after;
-my $id2 = $loop->recurring(0 => sub { $after++ });
-$loop->remove($id);
-$loop->timer(0.5 => sub { shift->stop });
-$loop->start;
-$loop->one_tick;
-$loop->remove($id2);
-ok $after > 1, 'more than one tick';
-is $ticks, $before, 'no additional ticks';
-
-# Recurring timer
-my $count;
-$id = $loop->recurring(0.1 => sub { $count++ });
-$loop->timer(0.5 => sub { shift->stop });
-$loop->start;
-$loop->one_tick;
-$loop->remove($id);
-ok($count > 1, 'more than one recurring event');
-ok($count < 10, 'less than ten recurring events');
-
-# Handle and reset
-my ($handle, $handle2, $reset);
-Mojo::IOLoop->singleton->on(reset => sub { $reset++ });
-$id = Mojo::IOLoop->server(
- (address => '127.0.0.1') => sub {
- my ($loop, $stream) = @_;
- $handle = $stream->handle;
- Mojo::IOLoop->stop;
- }
-);
-my $port = Mojo::IOLoop->acceptor($id)->port;
-Mojo::IOLoop->acceptor($id)->on(accept => sub { $handle2 = pop });
-$id2 = Mojo::IOLoop->client((address => '127.0.0.1', port => $port) => sub {
});
-Mojo::IOLoop->start;
-$count = 0;
-Mojo::IOLoop->recurring(10 => sub { $timer++ });
-my $running;
-Mojo::IOLoop->next_tick(sub {
- Mojo::IOLoop->reset;
- $running = Mojo::IOLoop->is_running;
-});
-Mojo::IOLoop->start;
-ok !$running, 'not running';
-is $count, 0, 'no recurring events';
-ok !Mojo::IOLoop->acceptor($id), 'acceptor has been removed';
-ok !Mojo::IOLoop->stream($id2), 'stream has been removed';
-is $handle, $handle2, 'handles are equal';
-isa_ok $handle, 'IO::Socket', 'right reference';
-is $reset, 1, 'reset event has been emitted once';
-
-# The poll reactor stops when there are no events being watched anymore
-my $time = time;
-Mojo::IOLoop->start;
-Mojo::IOLoop->one_tick;
-Mojo::IOLoop->reset;
-ok time < ($time + 10), 'stopped automatically';
-
-# Reset events
-Mojo::IOLoop->singleton->on(finish => sub { });
-ok !!Mojo::IOLoop->singleton->has_subscribers('finish'), 'has subscribers';
-Mojo::IOLoop->reset;
-ok !Mojo::IOLoop->singleton->has_subscribers('finish'), 'no subscribers';
-
-# Stream
-my $buffer = '';
-$id = Mojo::IOLoop->server(
- (address => '127.0.0.1') => sub {
- my ($loop, $stream) = @_;
- $buffer .= 'accepted';
- $stream->on(
- read => sub {
- my ($stream, $chunk) = @_;
- $buffer .= $chunk;
- return unless $buffer eq 'acceptedhello';
- $stream->write('wo')->write('')->write('rld' => sub { shift->close });
- }
- );
- }
-);
-$port = Mojo::IOLoop->acceptor($id)->port;
-my $delay = Mojo::IOLoop->delay;
-my $end = $delay->begin;
-$handle = undef;
-Mojo::IOLoop->client(
- {port => $port} => sub {
- my ($loop, $err, $stream) = @_;
- $handle = $stream->steal_handle;
- $end->();
- $stream->on(close => sub { $buffer .= 'should not happen' });
- $stream->on(error => sub { $buffer .= 'should not happen either' });
- }
-);
-$delay->wait;
-my $stream = Mojo::IOLoop::Stream->new($handle);
-is $stream->timeout, 15, 'right default';
-is $stream->timeout(16)->timeout, 16, 'right timeout';
-$id = Mojo::IOLoop->stream($stream);
-$stream->on(close => sub { Mojo::IOLoop->stop });
-$stream->on(read => sub { $buffer .= pop });
-$stream->write('hello');
-ok !!Mojo::IOLoop->stream($id), 'stream exists';
-is $stream->timeout, 16, 'right timeout';
-Mojo::IOLoop->start;
-Mojo::IOLoop->timer(0.25 => sub { Mojo::IOLoop->stop });
-Mojo::IOLoop->start;
-ok !Mojo::IOLoop->stream($id), 'stream does not exist anymore';
-is $buffer, 'acceptedhelloworld', 'right result';
-
-# Removed listen socket
-$id = $loop->server({address => '127.0.0.1'} => sub { });
-$port = $loop->acceptor($id)->port;
-my $connected;
-$loop->client(
- {port => $port} => sub {
- my ($loop, $err, $stream) = @_;
- $loop->remove($id);
+subtest 'Defaults' => sub {
+ my $loop = Mojo::IOLoop->new;
+ is $loop->max_connections, 1000, 'right default';
+ $loop = Mojo::IOLoop->new(max_connections => 10);
+ is $loop->max_connections, 10, 'right value';
+};
+
+subtest 'Double start' => sub {
+ my $err;
+ Mojo::IOLoop->next_tick(sub {
+ my $loop = shift;
+ eval { $loop->start };
+ $err = $@;
$loop->stop;
- $connected = 1;
- }
-);
-my $fd = fileno $loop->acceptor($id)->handle;
-like $ENV{MOJO_REUSE}, qr/(?:^|\,)127\.0\.0\.1:\Q$port\E:\Q$fd\E/, 'file
descriptor can be reused';
-$loop->start;
-unlike $ENV{MOJO_REUSE}, qr/(?:^|\,)127\.0\.0\.1:\Q$port\E:\Q$fd\E/,
'environment is clean';
-ok $connected, 'connected';
-ok !$loop->acceptor($id), 'acceptor has been removed';
-
-# Removed connection (with delay)
-my $removed;
-$delay = Mojo::IOLoop->delay(sub { $removed++ });
-$end = $delay->begin;
-$id = Mojo::IOLoop->server(
- (address => '127.0.0.1') => sub {
- my ($loop, $stream) = @_;
- $stream->on(close => $end);
- }
-);
-$port = Mojo::IOLoop->acceptor($id)->port;
-my $end2 = $delay->begin;
-$id = Mojo::IOLoop->client(
- (port => $port) => sub {
- my ($loop, $err, $stream) = @_;
- $stream->on(close => $end2);
- $loop->remove($id);
- }
-);
-$delay->wait;
-is $removed, 1, 'connection has been removed';
-
-# Stream throttling
-my ($client, $server, $client_after, $server_before, $server_after, @waiting);
-$id = Mojo::IOLoop->server(
- {address => '127.0.0.1'} => sub {
- my ($loop, $stream) = @_;
- $stream->timeout(0)->on(
- read => sub {
- my ($stream, $chunk) = @_;
- Mojo::IOLoop->timer(
- 0.5 => sub {
- $server_before = $server;
- $stream->stop;
- $stream->write('works!');
- push @waiting, $stream->bytes_waiting;
- Mojo::IOLoop->timer(
- 0.5 => sub {
- $server_after = $server;
- $client_after = $client;
- push @waiting, $stream->bytes_waiting;
- $stream->start;
- Mojo::IOLoop->timer(0.5 => sub { Mojo::IOLoop->stop });
- }
- );
- }
- ) unless $server;
- $server .= $chunk;
- }
- );
- }
-);
-$port = Mojo::IOLoop->acceptor($id)->port;
-Mojo::IOLoop->client(
- {port => $port} => sub {
- my ($loop, $err, $stream) = @_;
- my $drain;
- $drain = sub { shift->write('1', $drain) };
- $stream->$drain();
- $stream->on(read => sub { $client .= pop });
- }
-);
-Mojo::IOLoop->start;
-is $server_before, $server_after, 'stream has been paused';
-ok length($server) > length($server_after), 'stream has been resumed';
-is $client, $client_after, 'stream was writable while paused';
-is $client, 'works!', 'full message has been written';
-is_deeply \@waiting, [6, 0], 'right buffer sizes';
-
-# Watermarks
-my $fake = IO::Socket::IP->new(Listen => 5, LocalAddr => '127.0.0.1');
-$stream = Mojo::IOLoop::Stream->new($fake);
-$stream->start;
-$stream->high_water_mark(10);
-$stream->write('abcd');
-is $stream->bytes_waiting, 4, 'four bytes waiting';
-ok $stream->can_write, 'stream is still writable';
-$stream->write('efghijk');
-is $stream->bytes_waiting, 11, 'eleven bytes waiting';
-ok !$stream->can_write, 'stream is not writable anymore';
-$stream->high_water_mark(12);
-ok $stream->can_write, 'stream is writable again';
-$stream->close;
-ok !$stream->can_write, 'closed stream is not writable anymore';
-undef $stream;
-
-# Graceful shutdown
-$err = '';
-$loop = Mojo::IOLoop->new;
-$port = $loop->acceptor($loop->server({address => '127.0.0.1'} => sub {
}))->port;
-$id = $loop->client({port => $port} => sub { shift->stop_gracefully });
-my $finish;
-$loop->on(finish => sub { ++$finish and shift->stream($id)->close });
-$loop->timer(30 => sub { shift->stop; $err = 'failed' });
-$loop->start;
-ok !$loop->stream($id), 'stopped gracefully';
-ok !$err, 'no error';
-is $finish, 1, 'finish event has been emitted once';
-
-# Graceful shutdown (without connection)
-$err = $finish = '';
-$loop = Mojo::IOLoop->new;
-$loop->on(finish => sub { $finish++ });
-$loop->next_tick(sub { shift->stop_gracefully });
-$loop->timer(30 => sub { shift->stop; $err = 'failed' });
-$loop->start;
-ok !$err, 'no error';
-is $finish, 1, 'finish event has been emitted once';
-
-# Graceful shutdown (max_accepts)
-$err = '';
-$loop = Mojo::IOLoop->new->max_accepts(1);
-$id = $loop->server({address => '127.0.0.1'} => sub { });
-$port = $loop->acceptor($id)->port;
-$loop->client({port => $port} => sub { pop->close });
-$loop->timer(30 => sub { shift->stop; $err = 'failed' });
-$loop->start;
-ok !$err, 'no error';
-is $loop->max_accepts, 1, 'right value';
-
-# Connection limit
-$err = '';
-$loop = Mojo::IOLoop->new->max_connections(2);
-my @accepting;
-$id = $loop->server(
- {address => '127.0.0.1', single_accept => 1} => sub {
- shift->next_tick(sub {
- my $loop = shift;
- push @accepting, $loop->acceptor($id)->is_accepting;
- $loop->stop if @accepting == 2;
- });
- }
-);
-$port = $loop->acceptor($id)->port;
-$loop->client({port => $port} => sub { }) for 1 .. 2;
-$loop->timer(30 => sub { shift->stop; $err = 'failed' });
-$loop->start;
-ok !$err, 'no error';
-ok $accepting[0], 'accepting connections';
-ok !$accepting[1], 'connection limit reached';
+ });
+ Mojo::IOLoop->start;
+ like $err, qr/^Mojo::IOLoop already running/, 'right error';
+};
+
+subtest 'Double one_tick' => sub {
+ my $err;
+ Mojo::IOLoop->next_tick(sub {
+ my $loop = shift;
+ eval { $loop->one_tick };
+ $err = $@;
+ });
+ Mojo::IOLoop->one_tick;
+ like $err, qr/^Mojo::IOLoop already running/, 'right error';
+};
+
+subtest 'Basic functionality' => sub {
+ my ($ticks, $timer, $hirestimer);
+ my $loop = Mojo::IOLoop->new;
+ my $id = $loop->recurring(0 => sub { $ticks++ });
+ $loop->timer(
+ 1 => sub {
+ shift->timer(0 => sub { shift->stop });
+ $timer++;
+ }
+ );
+ $loop->timer(0.25 => sub { $hirestimer++ });
+ $loop->start;
+ ok $timer, 'recursive timer works';
+ ok $hirestimer, 'hires timer works';
+ $loop->one_tick;
+ ok $ticks > 2, 'more than two ticks';
+
+ # Run again without first tick event handler
+ my $before = $ticks;
+ my $after;
+ my $id2 = $loop->recurring(0 => sub { $after++ });
+ $loop->remove($id);
+ $loop->timer(0.5 => sub { shift->stop });
+ $loop->start;
+ $loop->one_tick;
+ $loop->remove($id2);
+ ok $after > 1, 'more than one tick';
+ is $ticks, $before, 'no additional ticks';
+};
+
+subtest 'Recurring timer' => sub {
+ my $count;
+ my $loop = Mojo::IOLoop->new;
+ my $id = $loop->recurring(0.1 => sub { $count++ });
+ $loop->timer(0.5 => sub { shift->stop });
+ $loop->start;
+ $loop->one_tick;
+ $loop->remove($id);
+ ok($count > 1, 'more than one recurring event');
+ ok($count < 10, 'less than ten recurring events');
+};
+
+subtest 'Handle and reset' => sub {
+ my ($handle, $handle2, $reset, $close);
+ Mojo::IOLoop->singleton->on(reset => sub { $reset++ });
+ my $id = Mojo::IOLoop->server(
+ (address => '127.0.0.1') => sub {
+ my ($loop, $stream) = @_;
+ $stream->on(close => sub { $close++ });
+ $handle = $stream->handle;
+ Mojo::IOLoop->stop;
+ }
+ );
+ my $port = Mojo::IOLoop->acceptor($id)->port;
+ Mojo::IOLoop->acceptor($id)->on(accept => sub { $handle2 = pop });
+ my $id2 = Mojo::IOLoop->client((address => '127.0.0.1', port => $port) =>
sub { pop->on(close => sub { $close++ })});
+ Mojo::IOLoop->start;
+ my ($count, $running, $timer) = (0) x 3;
+ Mojo::IOLoop->recurring(10 => sub { $timer++ });
+ Mojo::IOLoop->next_tick(sub {
+ Mojo::IOLoop->reset;
+ $running = Mojo::IOLoop->is_running;
+ });
+ Mojo::IOLoop->start;
+ ok !$running, 'not running';
+ is $count, 0, 'no recurring events';
+ ok !Mojo::IOLoop->acceptor($id), 'acceptor has been removed';
+ ok !Mojo::IOLoop->stream($id2), 'stream has been removed';
+ is $handle, $handle2, 'handles are equal';
+ isa_ok $handle, 'IO::Socket', 'right reference';
+ is $reset, 1, 'reset event has been emitted once';
+ is $close, undef, 'reset unsubscribed close on streams';
+};
+
+subtest 'The poll reactor stops when there are no events being watched
anymore' => sub {
+ my $time = time;
+ Mojo::IOLoop->start;
+ Mojo::IOLoop->one_tick;
+ Mojo::IOLoop->reset;
+ ok time < ($time + 10), 'stopped automatically';
+};
+
+subtest 'Reset events' => sub {
+ Mojo::IOLoop->singleton->on(finish => sub { });
+ ok !!Mojo::IOLoop->singleton->has_subscribers('finish'), 'has subscribers';
+ Mojo::IOLoop->reset;
+ ok !Mojo::IOLoop->singleton->has_subscribers('finish'), 'no subscribers';
+};
+
+subtest 'Stream' => sub {
+ my $buffer = '';
+ my $id = Mojo::IOLoop->server(
+ (address => '127.0.0.1') => sub {
+ my ($loop, $stream) = @_;
+ $buffer .= 'accepted';
+ $stream->on(
+ read => sub {
+ my ($stream, $chunk) = @_;
+ $buffer .= $chunk;
+ return unless $buffer eq 'acceptedhello';
+ $stream->write('wo')->write('')->write('rld' => sub { shift->close
});
+ }
+ );
+ }
+ );
+ my $port = Mojo::IOLoop->acceptor($id)->port;
+ my $delay = Mojo::IOLoop->delay;
+ my $end = $delay->begin;
+ my $handle;
+ Mojo::IOLoop->client(
+ {port => $port} => sub {
+ my ($loop, $err, $stream) = @_;
+ $handle = $stream->steal_handle;
+ $end->();
+ $stream->on(close => sub { $buffer .= 'should not happen' });
+ $stream->on(error => sub { $buffer .= 'should not happen either' });
+ }
+ );
+ $delay->wait;
+ my $stream = Mojo::IOLoop::Stream->new($handle);
+ is $stream->timeout, 15, 'right default';
+ is $stream->timeout(16)->timeout, 16, 'right timeout';
+ $id = Mojo::IOLoop->stream($stream);
+ $stream->on(close => sub { Mojo::IOLoop->stop });
+ $stream->on(read => sub { $buffer .= pop });
+ $stream->write('hello');
+ ok !!Mojo::IOLoop->stream($id), 'stream exists';
+ is $stream->timeout, 16, 'right timeout';
+ Mojo::IOLoop->start;
+ Mojo::IOLoop->timer(0.25 => sub { Mojo::IOLoop->stop });
+ Mojo::IOLoop->start;
+ ok !Mojo::IOLoop->stream($id), 'stream does not exist anymore';
+ is $buffer, 'acceptedhelloworld', 'right result';
+};
+
+subtest 'Removed listen socket' => sub {
+ my $loop = Mojo::IOLoop->new;
+ my $id = $loop->server({address => '127.0.0.1'} => sub { });
+ my $port = $loop->acceptor($id)->port;
+ my $connected;
+ $loop->client(
+ {port => $port} => sub {
+ my ($loop, $err, $stream) = @_;
+ $loop->remove($id);
+ $loop->stop;
+ $connected = 1;
+ }
+ );
+ my $fd = fileno $loop->acceptor($id)->handle;
+ like $ENV{MOJO_REUSE}, qr/(?:^|\,)127\.0\.0\.1:\Q$port\E:\Q$fd\E/, 'file
descriptor can be reused';
+ $loop->start;
+ unlike $ENV{MOJO_REUSE}, qr/(?:^|\,)127\.0\.0\.1:\Q$port\E:\Q$fd\E/,
'environment is clean';
+ ok $connected, 'connected';
+ ok !$loop->acceptor($id), 'acceptor has been removed';
+};
+
+subtest 'Removed connection (with delay)' => sub {
+ my $removed;
+ my $delay = Mojo::IOLoop->delay(sub { $removed++ });
+ my $end = $delay->begin;
+ my $id = Mojo::IOLoop->server(
+ (address => '127.0.0.1') => sub {
+ my ($loop, $stream) = @_;
+ $stream->on(close => $end);
+ }
+ );
+ my $port = Mojo::IOLoop->acceptor($id)->port;
+ my $end2 = $delay->begin;
+ $id = Mojo::IOLoop->client(
+ (port => $port) => sub {
+ my ($loop, $err, $stream) = @_;
+ $stream->on(close => $end2);
+ $loop->remove($id);
+ }
+ );
+ $delay->wait;
+ is $removed, 1, 'connection has been removed';
+};
+
+subtest 'Stream throttling' => sub {
+ my ($client, $server, $client_after, $server_before, $server_after,
@waiting);
+ my $id = Mojo::IOLoop->server(
+ {address => '127.0.0.1'} => sub {
+ my ($loop, $stream) = @_;
+ $stream->timeout(0)->on(
+ read => sub {
+ my ($stream, $chunk) = @_;
+ Mojo::IOLoop->timer(
+ 0.5 => sub {
+ $server_before = $server;
+ $stream->stop;
+ $stream->write('works!');
+ push @waiting, $stream->bytes_waiting;
+ Mojo::IOLoop->timer(
+ 0.5 => sub {
+ $server_after = $server;
+ $client_after = $client;
+ push @waiting, $stream->bytes_waiting;
+ $stream->start;
+ Mojo::IOLoop->timer(0.5 => sub { Mojo::IOLoop->stop });
+ }
+ );
+ }
+ ) unless $server;
+ $server .= $chunk;
+ }
+ );
+ }
+ );
+ my $port = Mojo::IOLoop->acceptor($id)->port;
+ Mojo::IOLoop->client(
+ {port => $port} => sub {
+ my ($loop, $err, $stream) = @_;
+ my $drain;
+ $drain = sub { shift->write('1', $drain) };
+ $stream->$drain();
+ $stream->on(read => sub { $client .= pop });
+ }
+ );
+ Mojo::IOLoop->start;
+ is $server_before, $server_after, 'stream has been paused';
+ ok length($server) > length($server_after), 'stream has been resumed';
+ is $client, $client_after, 'stream was writable while paused';
+ is $client, 'works!', 'full message has been written';
+ is_deeply \@waiting, [6, 0], 'right buffer sizes';
+};
+
+subtest 'Watermarks' => sub {
+ my $fake = IO::Socket::IP->new(Listen => 5, LocalAddr => '127.0.0.1');
+ my $stream = Mojo::IOLoop::Stream->new($fake);
+ $stream->start;
+ $stream->high_water_mark(10);
+ $stream->write('abcd');
+ is $stream->bytes_waiting, 4, 'four bytes waiting';
+ ok $stream->can_write, 'stream is still writable';
+ $stream->write('efghijk');
+ is $stream->bytes_waiting, 11, 'eleven bytes waiting';
+ ok !$stream->can_write, 'stream is not writable anymore';
+ $stream->high_water_mark(12);
+ ok $stream->can_write, 'stream is writable again';
+ $stream->close;
+ ok !$stream->can_write, 'closed stream is not writable anymore';
+ undef $stream;
+};
+
+subtest 'Graceful shutdown' => sub {
+ my $err;
+ my $loop = Mojo::IOLoop->new;
+ my $port = $loop->acceptor($loop->server({address => '127.0.0.1'} => sub {
}))->port;
+ my $id = $loop->client({port => $port} => sub { shift->stop_gracefully });
+ my $finish;
+ $loop->on(finish => sub { ++$finish and shift->stream($id)->close });
+ $loop->timer(30 => sub { shift->stop; $err = 'failed' });
+ $loop->start;
+ ok !$loop->stream($id), 'stopped gracefully';
+ ok !$err, 'no error';
+ is $finish, 1, 'finish event has been emitted once';
+};
+
+subtest 'Graceful shutdown (without connection)' => sub {
+ my ($err, $finish);
+ my $loop = Mojo::IOLoop->new;
+ $loop->on(finish => sub { $finish++ });
+ $loop->next_tick(sub { shift->stop_gracefully });
+ $loop->timer(30 => sub { shift->stop; $err = 'failed' });
+ $loop->start;
+ ok !$err, 'no error';
+ is $finish, 1, 'finish event has been emitted once';
+};
+
+subtest 'Graceful shutdown (max_accepts)' => sub {
+ my $err;
+ my $loop = Mojo::IOLoop->new->max_accepts(1);
+ my $id = $loop->server({address => '127.0.0.1'} => sub { });
+ my $port = $loop->acceptor($id)->port;
+ $loop->client({port => $port} => sub { pop->close });
+ $loop->timer(30 => sub { shift->stop; $err = 'failed' });
+ $loop->start;
+ ok !$err, 'no error';
+ is $loop->max_accepts, 1, 'right value';
+};
+
+subtest 'Connection limit' => sub {
+ my ($err, $id);
+ my $loop = Mojo::IOLoop->new->max_connections(2);
+ my @accepting;
+ $id = $loop->server(
+ {address => '127.0.0.1', single_accept => 1} => sub {
+ shift->next_tick(sub {
+ my $loop = shift;
+ push @accepting, $loop->acceptor($id)->is_accepting;
+ $loop->stop if @accepting == 2;
+ });
+ }
+ );
+ my $port = $loop->acceptor($id)->port;
+ $loop->client({port => $port} => sub { }) for 1 .. 2;
+ $loop->timer(30 => sub { shift->stop; $err = 'failed' });
+ $loop->start;
+ ok !$err, 'no error';
+ ok $accepting[0], 'accepting connections';
+ ok !$accepting[1], 'connection limit reached';
+};
-# Exception in timer
-{
+subtest 'Exception in timer' => sub {
local *STDERR;
open STDERR, '>', \my $err;
my $loop = Mojo::IOLoop->new;
$loop->timer(0 => sub { die 'Bye!' });
$loop->start;
like $err, qr/^Mojo::Reactor::Poll:.*Bye!/, 'right error';
-}
+};
-# Defaults
-is(Mojo::IOLoop::Client->new->reactor, Mojo::IOLoop->singleton->reactor,
'right default');
-is(Mojo::IOLoop::Delay->new->ioloop, Mojo::IOLoop->singleton,
'right default');
-is(Mojo::IOLoop::Server->new->reactor, Mojo::IOLoop->singleton->reactor,
'right default');
-is(Mojo::IOLoop::Stream->new->reactor, Mojo::IOLoop->singleton->reactor,
'right default');
+subtest 'Defaults' => sub {
+ is(Mojo::IOLoop::Client->new->reactor, Mojo::IOLoop->singleton->reactor,
'right default');
+ is(Mojo::IOLoop::Delay->new->ioloop, Mojo::IOLoop->singleton,
'right default');
+ is(Mojo::IOLoop::Server->new->reactor, Mojo::IOLoop->singleton->reactor,
'right default');
+ is(Mojo::IOLoop::Stream->new->reactor, Mojo::IOLoop->singleton->reactor,
'right default');
+};
done_testing();