commit perl-Mojolicious for openSUSE:Factory
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 <timueller+perl@suse.de> + +- 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();
participants (1)
-
root