@@ -1,5 +1,8 @@
Revision history for Perl extension Twiggy
+0.1025 2015-01-04 07:00:16 JST
+ - Fix a bug where Twiggy's run loop exits after a streaming request is cut off (hoelzro) #41
+
0.1024 2013-10-12 11:35:35 PDT
- Fix a bug where exit_guard is not correctly decremented when writing header failed (maedama) #37
@@ -1,4 +1,4 @@
-This software is copyright (c) 2013 by Tatsuhiko Miyagawa.
+This software is copyright (c) 2015 by Tatsuhiko Miyagawa.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
@@ -12,7 +12,7 @@ b) the "Artistic License"
--- The GNU General Public License, Version 1, February 1989 ---
-This software is Copyright (c) 2013 by Tatsuhiko Miyagawa.
+This software is Copyright (c) 2015 by Tatsuhiko Miyagawa.
This is free software, licensed under:
@@ -22,7 +22,7 @@ This is free software, licensed under:
Version 1, February 1989
Copyright (C) 1989 Free Software Foundation, Inc.
- 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -272,7 +272,7 @@ That's all there is to it!
--- The Artistic License 1.0 ---
-This software is Copyright (c) 2013 by Tatsuhiko Miyagawa.
+This software is Copyright (c) 2015 by Tatsuhiko Miyagawa.
This is free software, licensed under:
@@ -1,10 +1,10 @@
+# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.029.
Changes
LICENSE
MANIFEST
META.json
META.yml
Makefile.PL
-README
cpanfile
dist.ini
eg/chat-websocket/chat.psgi
@@ -29,6 +29,7 @@ script/twiggy
t/00_compile.t
t/anyevent.t
t/anyevent_closed_connection.t
+t/anyevent_closed_streaming.t
t/anyevent_extensions.t
t/anyevent_manyconnections.t
t/anyevent_server_starter.t
@@ -4,13 +4,13 @@
"Tatsuhiko Miyagawa"
],
"dynamic_config" : 0,
- "generated_by" : "Dist::Milla version v1.0.4, Dist::Zilla version 4.300039, CPAN::Meta::Converter version 2.132830",
+ "generated_by" : "Dist::Zilla version 5.029, Dist::Milla version v1.0.9, CPAN::Meta::Converter version 2.143240",
"license" : [
"perl_5"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
- "version" : "2"
+ "version" : 2
},
"name" : "Twiggy",
"no_index" : {
@@ -26,11 +26,12 @@
"prereqs" : {
"configure" : {
"requires" : {
- "ExtUtils::MakeMaker" : "6.30"
+ "ExtUtils::MakeMaker" : "0"
}
},
"develop" : {
"requires" : {
+ "Dist::Milla" : "v1.0.9",
"Test::Pod" : "1.41"
}
},
@@ -63,13 +64,14 @@
"web" : "https://github.com/miyagawa/Twiggy"
}
},
- "version" : "0.1024",
+ "version" : "0.1025",
"x_contributors" : [
"Adam Thomason <ad@mthomason.net>",
"Chia-liang Kao <clkao@clkao.org>",
"Kazuho Oku <kazuho@kazdev.in.labs.cybozu.co.jp>",
"Moritz Onken <onken@netcubed.de>",
"Pedro Melo <melo@simplicidade.org>",
+ "Rob Hoelz <rob@hoelz.ro>",
"Sergey Zasenko <d3fin3@gmail.com>",
"Tatsuhiko Miyagawa <miyagawa@gmail.com>",
"Tomas Doran (t0m) <t0m@state51.co.uk>",
@@ -3,17 +3,17 @@ abstract: 'AnyEvent HTTP server for PSGI (like Thin)'
author:
- 'Tatsuhiko Miyagawa'
build_requires:
- Test::More: 0
- Test::Requires: 0
- Test::TCP: 0
+ Test::More: '0'
+ Test::Requires: '0'
+ Test::TCP: '0'
configure_requires:
- ExtUtils::MakeMaker: 6.30
+ ExtUtils::MakeMaker: '0'
dynamic_config: 0
-generated_by: 'Dist::Milla version v1.0.4, Dist::Zilla version 4.300039, CPAN::Meta::Converter version 2.132830'
+generated_by: 'Dist::Zilla version 5.029, Dist::Milla version v1.0.9, CPAN::Meta::Converter version 2.143240'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
- version: 1.4
+ version: '1.4'
name: Twiggy
no_index:
directory:
@@ -24,22 +24,23 @@ no_index:
- eg
- examples
requires:
- AnyEvent: 0
- HTTP::Status: 0
- Plack: 0.99
- Try::Tiny: 0
- perl: 5.008001
+ AnyEvent: '0'
+ HTTP::Status: '0'
+ Plack: '0.99'
+ Try::Tiny: '0'
+ perl: '5.008001'
resources:
bugtracker: https://github.com/miyagawa/Twiggy/issues
homepage: https://github.com/miyagawa/Twiggy
repository: https://github.com/miyagawa/Twiggy.git
-version: 0.1024
+version: '0.1025'
x_contributors:
- 'Adam Thomason <ad@mthomason.net>'
- 'Chia-liang Kao <clkao@clkao.org>'
- 'Kazuho Oku <kazuho@kazdev.in.labs.cybozu.co.jp>'
- 'Moritz Onken <onken@netcubed.de>'
- 'Pedro Melo <melo@simplicidade.org>'
+ - 'Rob Hoelz <rob@hoelz.ro>'
- 'Sergey Zasenko <d3fin3@gmail.com>'
- 'Tatsuhiko Miyagawa <miyagawa@gmail.com>'
- 'Tomas Doran (t0m) <t0m@state51.co.uk>'
@@ -1,25 +1,26 @@
+# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.029.
use strict;
use warnings;
use 5.008001;
-use ExtUtils::MakeMaker 6.30;
+use ExtUtils::MakeMaker;
my %WriteMakefileArgs = (
"ABSTRACT" => "AnyEvent HTTP server for PSGI (like Thin)",
"AUTHOR" => "Tatsuhiko Miyagawa",
- "BUILD_REQUIRES" => {},
"CONFIGURE_REQUIRES" => {
- "ExtUtils::MakeMaker" => "6.30"
+ "ExtUtils::MakeMaker" => 0
},
"DISTNAME" => "Twiggy",
"EXE_FILES" => [
"script/twiggy"
],
"LICENSE" => "perl",
+ "MIN_PERL_VERSION" => "5.008001",
"NAME" => "Twiggy",
"PREREQ_PM" => {
"AnyEvent" => 0,
@@ -32,37 +33,29 @@ my %WriteMakefileArgs = (
"Test::Requires" => 0,
"Test::TCP" => 0
},
- "VERSION" => "0.1024",
+ "VERSION" => "0.1025",
"test" => {
"TESTS" => "t/*.t"
}
);
-unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
- my $tr = delete $WriteMakefileArgs{TEST_REQUIRES};
- my $br = $WriteMakefileArgs{BUILD_REQUIRES};
- for my $mod ( keys %$tr ) {
- if ( exists $br->{$mod} ) {
- $br->{$mod} = $tr->{$mod} if $tr->{$mod} > $br->{$mod};
- }
- else {
- $br->{$mod} = $tr->{$mod};
- }
- }
-}
+my %FallbackPrereqs = (
+ "AnyEvent" => 0,
+ "ExtUtils::MakeMaker" => 0,
+ "HTTP::Status" => 0,
+ "Plack" => "0.99",
+ "Test::More" => 0,
+ "Test::Requires" => 0,
+ "Test::TCP" => 0,
+ "Try::Tiny" => 0
+);
-unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) {
- my $br = delete $WriteMakefileArgs{BUILD_REQUIRES};
- my $pp = $WriteMakefileArgs{PREREQ_PM};
- for my $mod ( keys %$br ) {
- if ( exists $pp->{$mod} ) {
- $pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod};
- }
- else {
- $pp->{$mod} = $br->{$mod};
- }
- }
+
+unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
+ delete $WriteMakefileArgs{TEST_REQUIRES};
+ delete $WriteMakefileArgs{BUILD_REQUIRES};
+ $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs;
}
delete $WriteMakefileArgs{CONFIGURE_REQUIRES}
@@ -1,97 +0,0 @@
-NAME
- Twiggy - AnyEvent HTTP server for PSGI (like Thin)
-
-SYNOPSIS
- twiggy --listen :8080
-
- See "twiggy -h" for more details.
-
- use Twiggy::Server;
-
- my $server = Twiggy::Server->new(
- host => $host,
- port => $port,
- );
- $server->register_service($app);
-
- AE::cv->recv;
-
-DESCRIPTION
- Twiggy is a lightweight and fast HTTP server with unique features such
- as:
-
- PSGI
- Can run any PSGI applications. Fully supports *psgi.nonblocking* and
- *psgi.streaming* interfaces.
-
- AnyEvent
- This server uses AnyEvent and runs in a non-blocking event loop, so
- it's best to run event-driven web applications that runs I/O bound
- jobs or delayed responses such as long-poll, WebSocket or streaming
- content (server push).
-
- This software used to be called Plack::Server::AnyEvent but was
- renamed to Twiggy. See "NAMING" for details.
-
- Fast header parser
- Uses XS/C based HTTP header parser for the best performance.
- (optional, install the HTTP::Parser::XS module to enable it; see
- also Plack::HTTPParser for more information).
-
- Lightweight and Fast
- The memory required to run twiggy is 6MB and it can serve more than
- 4500 req/s with a single process on Perl 5.10 with MacBook Pro 13"
- late 2009.
-
- Superdaemon aware
- Supports Server::Starter for hot deploy and graceful restarts.
-
- To use it, instead of the usual:
-
- plackup --server Twiggy --port 8111 app.psgi
-
- install Server::Starter and use:
-
- start_server --port 8111 plackup --server Twiggy app.psgi
-
-ENVIRONMENT
- The following environment variables are supported.
-
- TWIGGY_DEBUG
- Set to true to enable debug messages from Twiggy.
-
-NAMING
- Twiggy?
- Because it is like Thin <http://code.macournoyer.com/thin/>, Ruby's Rack
- web server using EventMachine. You know, Twiggy is thin :)
-
- Why the cute name instead of more descriptive namespace? Are you on drugs?
- I'm sick of naming Perl software like
- HTTP::Server::PSGI::How::Its::Written::With::What::Module and people
- call it HSPHIWWWM on IRC. It's hard to say on speeches and newbies would
- ask questions what they stand for every day. That's crazy.
-
- This module actually includes the longer alias and an empty subclass
- AnyEvent::Server::PSGI for those who like to type more ::'s. It would
- actually help you find this software by searching for *PSGI Server
- AnyEvent* on CPAN, which i believe is a good thing.
-
- Yes, maybe I'm on drugs. We'll see.
-
-LICENSE
- This module is licensed under the same terms as Perl itself.
-
-AUTHOR
- Tatsuhiko Miyagawa
-
- Tokuhiro Matsuno
-
- Yuval Kogman
-
- Hideki Yamamura
-
- Daisuke Maki
-
-SEE ALSO
- Plack AnyEvent Tatsumaki
-
@@ -315,6 +315,8 @@ sub _run_app {
Carp::carp("Returning AnyEvent condvar is deprecated and will be removed in the next release of Twiggy. Use the streaming callback interface intstead.");
$res->cb(sub { $self->_write_psgi_response($sock, shift->recv) });
} elsif ( ref $res eq 'CODE' ) {
+ my $created_writer;
+
$res->(
sub {
my $res = shift;
@@ -327,6 +329,7 @@ sub _run_app {
$self->_flush($sock);
my $writer = Twiggy::Writer->new($sock, $self->{exit_guard});
+ $created_writer = 1;
my $buf = $self->_format_headers($status, $headers);
$writer->write($$buf);
@@ -340,6 +343,10 @@ sub _run_app {
},
$sock,
);
+
+ if($created_writer) {
+ $self->{exit_guard}->end; # normally _write_psgi_response calls this, but it doesn't get called when we use a writer!
+ }
} else {
croak("Unknown response type: $res");
}
@@ -598,6 +605,8 @@ use AnyEvent::Handle;
sub new {
my ( $class, $socket, $exit ) = @_;
+ $exit->begin if $exit;
+
bless { handle => AnyEvent::Handle->new( fh => $socket ), exit_guard => $exit }, $class;
}
@@ -2,7 +2,7 @@ package Twiggy;
use strict;
use warnings;
use 5.008_001;
-our $VERSION = '0.1024';
+our $VERSION = '0.1025';
1;
__END__
@@ -0,0 +1,92 @@
+use strict;
+use warnings;
+
+use Test::Requires qw(AnyEvent::HTTP);
+use AnyEvent::HTTP;
+use Test::More;
+use Test::TCP;
+use Plack::Loader;
+use POSIX ();
+use Time::HiRes qw(usleep);
+
+sub do_streaming_request {
+ my ( $url, $callback ) = @_;
+
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+ my $cond = AnyEvent->condvar;
+
+ http_get $url, timeout => 3, want_body_handle => 1, sub {
+ my ( $h, $headers ) = @_;
+
+ is $headers->{'Status'}, 200, 'streaming response should succeed';
+
+ $h->on_read(sub {
+ $h->push_read(line => sub {
+ my ( undef, $line ) = @_;
+
+ my $stop = $callback->($line, $cond);
+ if($stop) {
+ $h->destroy;
+ $cond->send;
+ }
+ });
+ });
+
+ $h->on_error(sub {
+ my ( undef, undef, $error ) = @_;
+
+ fail "Unexpected error: $error";
+ $h->destroy;
+ $cond->send;
+ });
+
+ $h->on_eof(sub {
+ $h->destroy;
+ $cond->send;
+ });
+ };
+ $cond->recv;
+}
+
+my $app = sub {
+ my ( $env ) = @_;
+
+ return sub {
+ my ( $respond ) = @_;
+
+ my $writer = $respond->( [200, ['Content-Type', 'text/plain'] ] );
+
+ foreach my $number ( 1 .. 10 ) {
+ $writer->write($number . "\n");
+ usleep 100_000;
+ }
+ };
+};
+
+my $server = Test::TCP->new(
+ code => sub {
+ my ( $port ) = @_;
+
+ my $server = Plack::Loader->load('Twiggy', port => $port, host => '127.0.0.1');
+ $server->run($app);
+ exit;
+ },
+);
+
+do_streaming_request('http://127.0.0.1:' . $server->port, sub {
+ my ( $line, $cond ) = @_;
+
+ if($line == 5) {
+ return 1;
+ }
+ return;
+});
+
+sleep 1; # give the process a bit to clean up, if it died
+
+my $kid = waitpid $server->pid, POSIX::WNOHANG;
+
+ok $kid != $server->pid, 'Server should stay alive after a single client breaks it connection';
+
+done_testing();
@@ -7,9 +7,8 @@ BEGIN {
}
}
+# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests.
use Test::More;
-
-eval "use Test::Pod 1.41";
-plan skip_all => "Test::Pod 1.41 required for testing POD" if $@;
+use Test::Pod 1.41;
all_pod_files_ok();