Commit 7ed027d1 authored by Xavier Guimard's avatar Xavier Guimard

Import original source of Plack-Handler-FCGI-EV 0.01

parents
MYMETA.*
META.yml
Makefile
inc/
pm_to_blib
*~
Revision history for Perl extension Plack::Handler::FCGI::EV
0.01 Tue May 10 15:04:22 2011
- original version
.gitignore
Changes
inc/Module/Install.pm
inc/Module/Install/Base.pm
inc/Module/Install/Can.pm
inc/Module/Install/Fetch.pm
inc/Module/Install/Makefile.pm
inc/Module/Install/Metadata.pm
inc/Module/Install/ReadmeFromPod.pm
inc/Module/Install/Repository.pm
inc/Module/Install/Win32.pm
inc/Module/Install/WriteAll.pm
lib/Plack/Handler/FCGI/EV.pm
Makefile.PL
MANIFEST This list of files
META.yml
README
t/00_compile.t
t/FCGIUtils.pm
t/suite.t
xt/pod.t
use inc::Module::Install;
all_from 'lib/Plack/Handler/FCGI/EV.pm';
readme_from('lib/Plack/Handler/FCGI/EV.pm');
requires 'FCGI::EV', '1.0.7';
requires 'Plack', 0.9977;
requires 'AnyEvent', 5.3;
requires 'EV', 4;
build_requires 'Test::More', 0.88;
test_requires 'Test::Requires';
auto_set_repository();
WriteAll;
NAME
Plack::Handler::FCGI::EV - PSGI handler for FCGI::EV
SYNOPSIS
> plackup -s FCGI::EV --listen :8080 myapp.psgi
DESCRIPTION
Plack::Handler::FCGI::EV is an asynchronous PSGI handler using FCGI::EV
as its backend.
AUTHORS
mala
Tatsuhiko Miyagawa <miyagawa@bulknews.net>
COPYRIGHT
Copyright 2011- Tatsuhiko Miyagawa
LICENSE
This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
SEE ALSO
package Plack::Handler::FCGI::EV;
use strict;
use warnings;
use 5.008_001;
our $VERSION = '0.01';
use Socket;
use Fcntl;
use Plack::Util;
use EV;
use FCGI::EV;
use AnyEvent;
sub new {
my ( $class, %args ) = @_;
bless { %args }, $class;
}
sub register_service {
my($self, $app) = @_;
my $listen = $self->{listen}[0]
or die "FCGI daemon host/port or socket is not specified\n";
my $sock;
if ($listen =~ /:\d+$/) {
my($host, $port) = split /:/, $listen, 2;
require IO::Socket::INET;
$sock = IO::Socket::INET->new(
LocalAddr => $host || '0.0.0.0',
LocalPort => $port,
ReuseAddr => 1,
Proto => 'tcp',
Listen => 10,
Blocking => 0,
);
} else {
require IO::Socket::UNIX;
socket $sock, AF_UNIX, SOCK_STREAM, 0;
unlink $listen;
my $umask = umask 0;
bind $sock, sockaddr_un($listen);
umask $umask;
listen $sock, SOMAXCONN;
fcntl $sock, F_SETFL, O_NONBLOCK;
bless $sock, 'IO::Socket::UNIX';
}
$sock or die "Couldn't launch FCGI daemon on $listen";
warn "Running FCGI daemon on $listen\n";
# HACK: we return an object and pass it to handler_class. Because it works, and is cleaner
my $handler = Plack::Handler::FCGI::EV::Handler->factory($app);
my $w = EV::io $sock, EV::READ, sub {
my $client = $sock->accept or die "No socket";
$client->blocking(0);
FCGI::EV->new( $client, $handler );
};
$self->{_sock} = $sock;
$self->{_guard} = $w;
return $self;
}
sub run {
my $self = shift;
$self->register_service(@_);
# Could use EV::run, but this is prone to a crash due to:
# syswrite() on closed filehandle GEN1653 at .../lib/site_perl/5.12.3/IO/Stream/EV.pm line 160.
# At least AnyEvent will catch these errors
# EV::run;
AE::cv->recv;
}
package Plack::Handler::FCGI::EV::Handler;
use strict;
use Plack::Util;
use Carp ();
use URI::Escape;
sub factory {
my($class, $app) = @_;
bless { app => $app }, $class;
}
# HACK: This is called from FCGI::EV - but it's actually an instance
# method since we pass our own object to 'handler_class'.
sub new {
my $factory = shift;
my ($server, $env) = @_;
my $app = $factory->{app};
$env = {
SCRIPT_NAME => '',
'psgi.version' => [ 1, 0 ],
'psgi.errors' => *STDERR,
'psgi.url_scheme' => 'http',
'psgi.nonblocking' => 1,
'psgi.streaming' => 1,
'psgi.multithread' => 0,
'psgi.multiprocess' => 1,
'psgi.run_once' => 0,
'psgix.input.buffered' => 1,
%$env,
};
my $request_uri = $env->{REQUEST_URI};
my ( $file, $query_string ) = ( $request_uri =~ /([^?]*)(?:\?(.*))?/s ); # split at ?
$env->{PATH_INFO} = URI::Escape::uri_unescape $file;
$env->{QUERY_STRING} = $query_string || '';
$env->{SERVER_NAME} =~ s/:\d+$//;
# warn Dumper $env;
my $self = {
stdin => '',
server => $server,
env => $env,
app => $app,
};
bless $self, ref $factory;
}
# not support Async Input yet
sub stdin {
my ($self, $stdin, $is_eof) = @_;
$self->{stdin} .= $stdin;
if ($is_eof) {
open my $input, "<", \$self->{stdin};
$self->{env}->{'psgi.input'} = $input;
$self->run_app;
}
}
sub run_app {
my $self = shift;
my $res = Plack::Util::run_app $self->{app}, $self->{env};
if (ref $res eq 'ARRAY') {
$self->handle_response($res);
} elsif (ref $res eq 'CODE') {
$res->(sub { $self->handle_response($_[0]) });
} else {
Carp::croak("Unknown response: $res");
}
}
sub handle_response {
my($self, $res) = @_;
my $hdrs = "Status: $res->[0]\r\n";
Plack::Util::header_iter $res->[1], sub {
$hdrs .= "$_[0]: $_[1]\r\n";
};
$hdrs .= "\r\n";
$self->{server}->stdout($hdrs);
if (defined $res->[2]) {
my $cb = sub { $self->{server}->stdout( $_[0] ) };
Plack::Util::foreach( $res->[2], $cb );
$self->{server}->stdout( "", 1 );
} else {
return Plack::Util::inline_object
write => sub { $self->{server}->stdout($_[0]) },
close => sub { $self->{server}->stdout("", 1) };
}
}
package Plack::Handler::FCGI::EV;
1;
__END__
=encoding utf-8
=for stopwords
=head1 NAME
Plack::Handler::FCGI::EV - PSGI handler for FCGI::EV
=head1 SYNOPSIS
> plackup -s FCGI::EV --listen :8080 myapp.psgi
=head1 DESCRIPTION
Plack::Handler::FCGI::EV is an asynchronous PSGI handler using
L<FCGI::EV> as its backend.
=head1 AUTHORS
mala
Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
=head1 COPYRIGHT
Copyright 2011- Tatsuhiko Miyagawa
=head1 LICENSE
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=head1 SEE ALSO
=cut
use strict;
use Test::More tests => 1;
BEGIN { use_ok 'Plack::Handler::FCGI::EV' }
package t::FCGIUtils;
use strict;
use warnings;
use File::Temp ();
use FindBin;
use Test::More;
use IO::Socket;
use File::Spec;
use Test::TCP qw/test_tcp empty_port/;
use parent qw/Exporter/;
our @EXPORT = qw/ test_lighty_external test_fcgi_standalone /;
# test using FCGI::Client + FCGI External Server
sub test_fcgi_standalone {
my ($callback, $http_port, $fcgi_port) = @_;
$http_port ||= empty_port();
$fcgi_port ||= empty_port($http_port);
require Plack::App::FCGIDispatcher;
my $fcgi_app = Plack::App::FCGIDispatcher->new({ port => $fcgi_port })->to_app;
test_tcp(
server => sub {
my $server = Plack::Loader->load('Standalone', host => '127.0.0.1', port => $http_port);
$server->run($fcgi_app);
},
client => sub {
$callback->($http_port, $fcgi_port);
},
port => $http_port,
);
}
# test for FCGI External Server
sub test_lighty_external (&@) {
my ($callback, $lighty_port, $fcgi_port) = @_;
$lighty_port ||= empty_port();
$fcgi_port ||= empty_port($lighty_port);
my $lighttpd_bin = $ENV{LIGHTTPD_BIN} || `which lighttpd`;
chomp $lighttpd_bin;
plan skip_all => 'Please set LIGHTTPD_BIN to the path to lighttpd'
unless $lighttpd_bin && -x $lighttpd_bin;
my $ver = (`$lighttpd_bin -v` =~ m!lighttpd[-/]1.(\d+\.\d+)!)[0];
if ($ver < 4.17) {
plan skip_all => "Too old lighttpd (1.$ver), known to be broken";
}
diag "Testing with lighttpd 1.$ver";
my $tmpdir = File::Temp::tempdir( CLEANUP => 1 );
test_tcp(
client => sub {
$callback->($lighty_port, $fcgi_port, ($ver && $ver < 4.23));
warn `cat $tmpdir/error.log` if $ENV{DEBUG};
},
server => sub {
my $conffname = File::Spec->catfile($tmpdir, "lighty.conf");
_write_file($conffname => _render_conf($tmpdir, $lighty_port, $fcgi_port));
my $pid = open my $lighttpd, "$lighttpd_bin -D -f $conffname 2>&1 |"
or die "Unable to spawn lighttpd: $!";
$SIG{TERM} = sub {
kill 'INT', $pid;
close $lighttpd;
exit;
};
sleep 60; # waiting tests.
die "server timeout";
},
port => $lighty_port,
);
}
sub _write_file {
my ($fname, $src) = @_;
open my $fh, '>', $fname or die $!;
print {$fh} $src or die $!;
close $fh;
}
sub _render_conf {
my ($tmpdir, $port, $fcgiport) = @_;
my $script_name = $ENV{PLACK_TEST_SCRIPT_NAME} || '/';
<<"END";
# basic lighttpd config file for testing fcgi(external server)+Plack
server.modules += ("mod_fastcgi")
server.document-root = "$tmpdir"
server.bind = "127.0.0.1"
server.port = $port
# HTTP::Engine app specific fcgi setup
fastcgi.server = (
"$script_name" => ((
"check-local" => "disable",
"host" => "127.0.0.1",
"port" => $fcgiport,
"idle-timeout" => 20,
"fix-root-scriptname" => "enable", # for 1.4.23 or later
))
)
END
}
1;
use strict;
use warnings;
use Test::More;
use Plack;
use Plack::Handler::FCGI::EV;
use Plack::Test::Suite;
use t::FCGIUtils;
my $lighty_port;
my $fcgi_port;
test_lighty_external(
sub {
($lighty_port, $fcgi_port) = @_;
Plack::Test::Suite->run_server_tests(\&run_server, $fcgi_port, $lighty_port);
done_testing();
}
);
sub run_server {
my($port, $app) = @_;
$| = 0; # Test::Builder autoflushes this. reset!
my $server = Plack::Handler::FCGI::EV->new(
host => '127.0.0.1',
listen => [ ":$port" ],
);
$server->run($app);
}
use Test::More;
eval "use Test::Pod 1.00";
plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
all_pod_files_ok();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment