@@ -2,6 +2,49 @@ Revision history for Perl extension Plack
Take a look at http://github.com/miyagawa/Plack/issues for the planned changes before 1.0 release.
+0.9929 Wed Mar 31 00:33:10 PDT 2010
+ - Middleware::JSONP: Simplified code and does not support IO response body type
+ - fcgi.t: skip tests with lighttpd < 1.4.17 per CPAN Testers #7040400
+
+0.9928 Mon Mar 29 17:02:42 PDT 2010
+ - log_dispatch.t: require Log::Dispatch::Array
+
+0.9927 Mon Mar 29 12:43:44 PDT 2010
+ - require newer Log::Dispatch (confound)
+ - StackTrace: Encode exceptions in utf-8 in case they include wide characters #95 (tokuhirom)
+ - StackTrace: Depends on a new Devel::StackTrace::AsHTML that escapes wide characters
+ - StackTrace: Display stacktrace only if the thrown exception is a direct error #91 (frodwith)
+ - StackTrace: Added 'force' option to force stacktrace in 500 errors
+ - Avoid warnings when response_cb filter returns undef in ARRAY response body #92 (hiratara)
+ - URLMap: Ignore port number if it matches with SERVER_PORT #90 (omega)
+ - URLMap: Enable debug print with PLACK_URLMAP_DEBUG=1 #94
+ - JSONP: Fixed possible infinite-loop when using with IO response body (hiratara)
+ - Fixed the compatiblity issues with FastCGI docs and tests with lighttpd 1.4.26 (tadam)
+ - LighttpdScriptNameFix: Added 'script_name' option (tadam)
+
+0.9926 Sun Mar 28 14:37:03 PDT 2010
+ - Added -v|--version option to plackup and the ability for Plack::Runner users to override
+
+0.9925 Sat Mar 27 19:03:57 PDT 2010
+ - Make this a non-devel release
+
+0.99_24 Sat Mar 27 13:31:51 PDT 2010
+ - Disable Devel::StackTrace::WithLexicals for now until PadWalker RT #55242 is fixed
+
+0.99_23 Sat Mar 27 01:02:24 PDT 2010
+ - Dropped keep-alive code from HTTP::Server::PSGI now that Starlet clones the code
+ - Special case --disable-* and --enable-* command line options in plackup and Plack::Runner
+
+0.99_22 Thu Mar 25 19:48:08 PDT 2010
+ - INCOMPATIBLE: removed --max-workers option from the default standalone server.
+ Now it gives you warnings and falls back to the single process mode.
+
+0.99_21 Thu Mar 25 15:05:53 PDT 2010
+ - INCOMPATIBLE: removed a workaround for lighttpd SCRIPT_NAME bug in FCGI handler
+ See http://github.com/miyagawa/Plack/issues#issue/68 for details.
+ - HTTPException now logs standard exceptions to psgi.errors
+ - micro optimization for Plack::Request content() method
+
0.9920 Thu Mar 18 23:48:06 PDT 2010
- Fixed URL path prefix matching in URLMap (hiratara)
- Fixed Plack::Request->content on GET with FastCGI servers (sunnavy)
@@ -85,6 +85,7 @@ lib/Plack/Middleware/ErrorDocument.pm
lib/Plack/Middleware/Head.pm
lib/Plack/Middleware/HTTPExceptions.pm
lib/Plack/Middleware/JSONP.pm
+lib/Plack/Middleware/LighttpdScriptNameFix.pm
lib/Plack/Middleware/Lint.pm
lib/Plack/Middleware/Log4perl.pm
lib/Plack/Middleware/LogDispatch.pm
@@ -143,7 +144,6 @@ t/Plack-Handler/cgi.t
t/Plack-Handler/fcgi.t
t/Plack-Handler/fcgi_client.t
t/Plack-Handler/http_server_simple.t
-t/Plack-Handler/standalone-prefork.t
t/Plack-Handler/standalone.t
t/Plack-HTTPParser-PP/simple.t
t/Plack-Loader/auto.t
@@ -162,6 +162,7 @@ t/Plack-Middleware/cgi-bin/hello3.cgi
t/Plack-Middleware/cgibin.t
t/Plack-Middleware/chunked.t
t/Plack-Middleware/component-leak.t
+t/Plack-Middleware/component.t
t/Plack-Middleware/conditional.t
t/Plack-Middleware/conditionalget.t
t/Plack-Middleware/content_length.t
@@ -190,12 +191,16 @@ t/Plack-Middleware/runtime.t
t/Plack-Middleware/simple_content_filter.t
t/Plack-Middleware/simple_logger.t
t/Plack-Middleware/stacktrace.t
+t/Plack-Middleware/stacktrace_force.t
+t/Plack-Middleware/stacktrace_streaming.t
+t/Plack-Middleware/stacktrace_utf8.t
t/Plack-Middleware/static.t
t/Plack-Middleware/static.txt
t/Plack-Middleware/static_env.t
t/Plack-Middleware/urlmap.t
t/Plack-Middleware/urlmap_builder.t
t/Plack-Middleware/urlmap_env.t
+t/Plack-Middleware/urlmap_ports.t
t/Plack-Middleware/wrapcgi.t
t/Plack-Middleware/xframework.t
t/Plack-Middleware/xsendfile.t
@@ -1,7 +1,7 @@
---
abstract: 'Perl Superglue for Web frameworks and Web Servers (PSGI toolkit)'
author:
- - '- Tatsuhiko Miyagawa'
+ - 'Tatsuhiko Miyagawa'
build_requires:
ExtUtils::MakeMaker: 6.42
Test::More: 0.88
@@ -24,7 +24,7 @@ no_index:
- xt
requires:
Devel::StackTrace: 0
- Devel::StackTrace::AsHTML: 0
+ Devel::StackTrace::AsHTML: 0.09
File::ShareDir: 1.00
Filesys::Notify::Simple: 0
HTTP::Body: 1.06
@@ -38,4 +38,4 @@ requires:
resources:
license: http://dev.perl.org/licenses/
repository: git://github.com/miyagawa/Plack.git
-version: 0.9920
+version: 0.9929
@@ -9,26 +9,26 @@ name 'Plack';
all_from 'lib/Plack.pm';
readme_from 'lib/Plack.pm';
-requires 'LWP', 5.814; # HTTP::Status, HTTP::Headers and HTTP::Request
-requires 'URI', 1.36;
-requires 'Pod::Usage'; # plackup
-requires 'File::ShareDir', '1.00'; # Plack::Test::Suite
+requires 'LWP', 5.814; # HTTP::Status, HTTP::Headers and HTTP::Request
+requires 'URI', 1.36; # URI::Escape
+requires 'Pod::Usage'; # plackup
+requires 'File::ShareDir', '1.00'; # Plack::Test::Suite
requires 'Try::Tiny';
requires 'parent';
-requires 'Devel::StackTrace'; # Middleware::StackTrace
-requires 'Devel::StackTrace::AsHTML'; # Middleware::StackTrace
+requires 'Devel::StackTrace'; # Middleware::StackTrace
+requires 'Devel::StackTrace::AsHTML', 0.09; # Middleware::StackTrace
-requires 'Filesys::Notify::Simple'; # plackup -r
+requires 'Filesys::Notify::Simple'; # plackup -r
-requires 'Hash::MultiValue', 0.05; # Plack::Request
-requires 'HTTP::Body', 1.06; # Plack::Request
+requires 'Hash::MultiValue', 0.05; # Plack::Request
+requires 'HTTP::Body', 1.06; # Plack::Request
build_requires 'Test::More', 0.88;
build_requires 'Test::TCP', 0.11;
test_requires 'Test::Requires';
-tests 't/*.t t/*/*.t t/*/*/*.t t/*/*/*/*.t t/*/*/*/*/*.t';
+tests 't/*.t t/*/*.t';
author_tests 'xt';
install_share 'share';
@@ -90,8 +90,13 @@ MODULES AND UTILITIES
CONTRIBUTING
Patches and Bug Fixes
Small patches and bug fixes can be either submitted via nopaste on IRC
- <irc://irc.perl.org/#plack> or email. You could also fork on github
- (http://github.com/miyagawa/Plack) to make larger fixes.
+ <irc://irc.perl.org/#plack> or the github issue tracker
+ <http://github.com/miyagawa/Plack/issues>. Forking on github
+ <http://github.com/miyagawa/Plack> is another good way if you intend to
+ make larger fixes.
+
+ See also <http://contributing.appspot.com/plack> when you think this
+ document is terribly outdated.
Module Namespaces
Modules added to the Plack:: sub-namespaces should be reasonably generic
@@ -117,12 +122,12 @@ CONTRIBUTING
it's supposed to run on CGI and that is a really bad choice and confuse
people.
-COPYRIGHT
- Copyright 2009- Tatsuhiko Miyagawa
-
AUTHOR
Tatsuhiko Miyagawa
+COPYRIGHT
+ Copyright 2009-2010 Tatsuhiko Miyagawa
+
CONTRIBUTORS
Yuval Kogman (nothingmuch)
@@ -130,7 +135,7 @@ CONTRIBUTORS
Kazuhiro Osawa (Yappo)
- Kzzuho Oku
+ Kazuho Oku
Florian Ragwitz (rafl)
@@ -17,12 +17,12 @@ my $url = 'http://127.0.0.1/';
my @try = (
[ 'AnyEvent::HTTPD' ],
[ 'HTTP::Server::PSGI' ],
- [ 'HTTP::Server::PSGI', ' (workers=10)', max_workers => 10 ],
[ 'Twiggy' ],
[ 'HTTP::Server::Simple' ],
[ 'Corona' ],
[ 'Danga::Socket' ],
- [ 'POE' ],
+ [ '+POE::Component::Server::PSGI' ],
+ [ 'Starlet', ' (workers=10)', max_workers => 10 ],
[ 'Starman', ' (workers=10)', workers => 10 ],
);
@@ -113,7 +113,7 @@ sub _res_from_psgi {
$res->headers->header(@$headers) if @$headers;
if (ref $body eq 'ARRAY') {
- $res->content(join '', @$body);
+ $res->content(join '', grep defined, @$body);
} else {
local $/ = \4096;
my $content;
@@ -2,6 +2,7 @@ package HTTP::Server::PSGI;
use strict;
use warnings;
+use Carp ();
use Plack;
use Plack::HTTPParser qw( parse_http_request );
use IO::Socket::INET;
@@ -37,27 +38,16 @@ sub new {
host => $args{host} || 0,
port => $args{port} || 8080,
timeout => $args{timeout} || 300,
- keepalive_timeout => $args{keepalive_timeout} || 2,
- max_keepalive_reqs => $args{max_keepalive_reqs},
server_software => $args{server_software} || $class,
server_ready => $args{server_ready} || sub {},
- max_workers => $args{max_workers} || 1,
max_reqs_per_child => $args{max_reqs_per_child} || 100,
}, $class;
- if ($self->{max_workers} > 1) {
- try {
- require Parallel::Prefork;
- $self->{prefork} = 1;
- $self->{max_keepalive_reqs} ||= 100;
- $self->{server_software} .= " (prefork)";
- } catch {
- die "You need to install Parallel::Prefork to run multi workers (max_workers=$self->{max_workers}): $_";
- };
- }
-
- unless ($self->{prefork}) {
- $self->{max_keepalive_reqs} ||= 1;
+ if ($args{max_workers} && $args{max_workers} > 1) {
+ Carp::carp(
+ "Preforking in $class is deprecated. Falling back to the non-forking mode. ",
+ "If you need preforking, use Starman or Starlet instead and run like `plackup -s Starlet`",
+ );
}
$self;
@@ -66,12 +56,7 @@ sub new {
sub run {
my($self, $app) = @_;
$self->setup_listener();
-
- if ($self->{prefork}) {
- $self->run_prefork($app);
- } else {
- $self->accept_loop($app);
- }
+ $self->accept_loop($app);
}
sub setup_listener {
@@ -87,24 +72,6 @@ sub setup_listener {
$self->{server_ready}->($self);
}
-sub run_prefork {
- my($self, $app) = @_;
-
- my $pm = Parallel::Prefork->new({
- max_workers => $self->{max_workers},
- trap_signals => {
- TERM => 'TERM',
- HUP => 'TERM',
- },
- });
- while ($pm->signal_received ne 'TERM') {
- $pm->start and next;
- $self->accept_loop($app, $self->{max_reqs_per_child});
- $pm->finish;
- }
- $pm->wait_all_children;
-}
-
sub accept_loop {
# TODO handle $max_reqs_per_child
my($self, $app, $max_reqs_per_child) = @_;
@@ -117,43 +84,32 @@ sub accept_loop {
if (my $conn = $self->{listen_sock}->accept) {
$conn->setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
or die "setsockopt(TCP_NODELAY) failed:$!";
- my $req_count = 0;
- while (1) {
- ++$req_count;
- ++$proc_req_count;
- my $env = {
- SERVER_PORT => $self->{port},
- SERVER_NAME => $self->{host},
- SCRIPT_NAME => '',
- REMOTE_ADDR => $conn->peerhost,
- 'psgi.version' => [ 1, 1 ],
- 'psgi.errors' => *STDERR,
- 'psgi.url_scheme' => 'http',
- 'psgi.run_once' => Plack::Util::FALSE,
- 'psgi.multithread' => Plack::Util::FALSE,
- 'psgi.multiprocess' => $self->{prefork},
- 'psgi.streaming' => Plack::Util::TRUE,
- 'psgi.nonblocking' => Plack::Util::FALSE,
- 'psgix.input.buffered' => Plack::Util::TRUE,
- 'psgix.io' => $conn,
- };
-
- # no need to take care of pipelining since this module is a HTTP/1.0 server
- my $may_keepalive = $req_count < $self->{max_keepalive_reqs};
- if ($may_keepalive && $max_reqs_per_child && $proc_req_count >= $max_reqs_per_child) {
- $may_keepalive = undef;
- }
- $self->handle_connection($env, $conn, $app, $may_keepalive, $req_count != 1)
- or last;
- # TODO add special cases for clients with broken keep-alive support, as well as disabling keep-alive for HTTP/1.0 proxies
- }
+ ++$proc_req_count;
+ my $env = {
+ SERVER_PORT => $self->{port},
+ SERVER_NAME => $self->{host},
+ SCRIPT_NAME => '',
+ REMOTE_ADDR => $conn->peerhost,
+ 'psgi.version' => [ 1, 1 ],
+ 'psgi.errors' => *STDERR,
+ 'psgi.url_scheme' => 'http',
+ 'psgi.run_once' => Plack::Util::FALSE,
+ 'psgi.multithread' => Plack::Util::FALSE,
+ 'psgi.multiprocess' => Plack::Util::FALSE,
+ 'psgi.streaming' => Plack::Util::TRUE,
+ 'psgi.nonblocking' => Plack::Util::FALSE,
+ 'psgix.input.buffered' => Plack::Util::TRUE,
+ 'psgix.io' => $conn,
+ };
+
+ $self->handle_connection($env, $conn, $app);
$conn->close;
}
}
}
sub handle_connection {
- my($self, $env, $conn, $app, $use_keepalive, $is_keepalive) = @_;
+ my($self, $env, $conn, $app) = @_;
my $buf = '';
my $res = [ 400, [ 'Content-Type' => 'text/plain' ], [ 'Bad Request' ] ];
@@ -161,19 +117,10 @@ sub handle_connection {
while (1) {
my $rlen = $self->read_timeout(
$conn, \$buf, MAX_REQUEST_SIZE - length($buf), length($buf),
- $is_keepalive ? $self->{keepalive_timeout} : $self->{timeout},
+ $self->{timeout},
) or return;
my $reqlen = parse_http_request($buf, $env);
if ($reqlen >= 0) {
- # handle request
- if ($use_keepalive) {
- if (my $c = $env->{HTTP_CONNECTION}) {
- $use_keepalive = undef
- unless $c =~ /^\s*keep-alive\s*/i;
- } else {
- $use_keepalive = undef;
- }
- }
$buf = substr $buf, $reqlen;
if (my $cl = $env->{CONTENT_LENGTH}) {
my $buffer = Plack::TempBuffer->new($cl);
@@ -206,20 +153,20 @@ sub handle_connection {
}
if (ref $res eq 'ARRAY') {
- $self->_handle_response($res, $conn, \$use_keepalive);
+ $self->_handle_response($res, $conn);
} elsif (ref $res eq 'CODE') {
$res->(sub {
- $self->_handle_response($_[0], $conn, \$use_keepalive);
+ $self->_handle_response($_[0], $conn);
});
} else {
die "Bad response $res";
}
- return $use_keepalive;
+ return;
}
sub _handle_response {
- my($self, $res, $conn, $use_keepalive_r) = @_;
+ my($self, $res, $conn) = @_;
my @lines = (
"Date: @{[HTTP::Date::time2str()]}\015\012",
@@ -228,19 +175,9 @@ sub _handle_response {
Plack::Util::header_iter($res->[1], sub {
my ($k, $v) = @_;
- if (lc $k eq 'connection') {
- $$use_keepalive_r = undef
- if $$use_keepalive_r && lc $v ne 'keep-alive';
- } else {
- push @lines, "$k: $v\015\012";
- }
+ push @lines, "$k: $v\015\012";
});
- if ($$use_keepalive_r) {
- $$use_keepalive_r = undef
- unless Plack::Util::header_exists($res->[1], 'Content-Length');
- }
- push @lines, "Connection: keep-alive\015\012"
- if $$use_keepalive_r;
+
unshift @lines, "HTTP/1.0 $res->[0] @{[ HTTP::Status::status_message($res->[0]) ]}\015\012";
push @lines, "\015\012";
@@ -348,8 +285,7 @@ HTTP::Server::PSGI - Standalone PSGI compatible HTTP server
=head1 DESCRIPTION
HTTP::Server::PSGI is a standalone, single-process and PSGI compatible
-HTTP server implementations. It runs reasonably fast and HTTP/1.0 and
-Keep-Alive requests are supported.
+HTTP server implementations.
This server should be great for the development and testig, but might
not be suitable for production.
@@ -357,8 +293,10 @@ not be suitable for production.
Some features in HTTP/1.1, notably chunked requests, responses and
pipeline requests are B<NOT> supported yet.
-See L<Starman> if you want a multi-process prefork server with some
-HTTP/1.1 features support.
+=head1 PREFORKING
+
+L<HTTP::Server::PSGI> does B<NOT> support preforking. See L<Starman>
+or L<Starlet> if you want a multi-process prefork web servers.
=head1 AUTHOR
@@ -368,6 +306,6 @@ Tatsuhiko Miyagawa
=head1 SEE ALSO
-L<Plack::Handler::Standalone>
+L<Plack::Handler::Standalone> L<Starman> L<Starlet>
=cut
@@ -2,6 +2,7 @@ package Plack::App::URLMap;
use strict;
use warnings;
use parent qw(Plack::Component);
+use constant DEBUG => $ENV{PLACK_URLMAP_DEBUG};
use Carp ();
@@ -43,15 +44,21 @@ sub call {
my($http_host, $server_name) = @{$env}{qw( HTTP_HOST SERVER_NAME )};
+ if ($http_host and my $port = $env->{SERVER_PORT}) {
+ $http_host =~ s/:$port$//;
+ }
+
for my $map (@{ $self->{_sorted_mapping} }) {
my($host, $location, $app) = @$map;
my $path = $path_info; # copy
no warnings 'uninitialized';
+ DEBUG && warn "Matching request (Host=$http_host Path=$path) and the map (Host=$host Path=$location)\n";
next unless not defined $host or
$http_host eq $host or
$server_name eq $host;
next unless $location eq '' or $path =~ s!^\Q$location\E!!;
next unless $path eq '' or $path =~ m!^/!;
+ DEBUG && warn "-> Matched!\n";
my $orig_path_info = $env->{PATH_INFO};
my $orig_script_name = $env->{SCRIPT_NAME};
@@ -64,6 +71,8 @@ sub call {
});
}
+ DEBUG && warn "All matching failed.\n";
+
return [404, [ 'Content-Type' => 'text/plain' ], [ "Not Found" ]];
}
@@ -132,6 +141,12 @@ should also work.
=back
+=head1 DEBUGGING
+
+You can set the environment variable C<PLACK_URLMAP_DEBUG> to see how
+this application matches with the incoming request host names and
+paths.
+
=head1 AUTHOR
Tatsuhiko Miyagawa
@@ -55,7 +55,8 @@ sub response_cb {
$line = $filter_cb->($line);
}
# Send EOF.
- push @{ $res->[2] }, $filter_cb->( undef );
+ my $eof = $filter_cb->( undef );
+ push @{ $res->[2] }, $eof if defined $eof;
} else {
my $body = $res->[2];
my $getline = sub { $body->getline };
@@ -87,6 +87,10 @@ package Plack::Handler::CGI;
1;
__END__
+=head1 NAME
+
+Plack::Handler::CGI - CGI handler for Plack
+
=head1 SYNOPSIS
Want to run PSGI application as a CGI script? Rename .psgi to .cgi and
@@ -88,12 +88,15 @@ sub run {
'psgi.nonblocking' => Plack::Util::FALSE,
};
- # If we're running under Lighttpd, swap PATH_INFO and SCRIPT_NAME if PATH_INFO is empty
- # http://lists.rawmode.org/pipermail/catalyst/2006-June/008361.html
- # Thanks to Mark Blythe for this fix
- if ($env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ /lighttpd/) {
- $env->{PATH_INFO} ||= delete $env->{SCRIPT_NAME};
- $env->{SCRIPT_NAME} ||= '';
+ if ($env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ m!lighttpd[-/]1\.(\d+\.\d+)!) {
+ no warnings;
+ if ($ENV{PLACK_ENV} eq 'development' && $1 < 4.23 && $env->{PATH_INFO} eq '') {
+ warn "You're using lighttpd 1.$1 and appear to mount your FastCGI handler under the root ('/'). ",
+ "It's known to be causing issues because of the lighttpd bug. You're recommended to enable ",
+ "LighttpdScriptNameFix middleware, or upgrade lighttpd to 1.4.23 or later and include ",
+ "'fix-root-scriptname' flag in 'fastcgi.server'. See perldoc Plack::Handler::FCGI for details. ",
+ "This friendly warning will go away in the next major release of Plack.";
+ }
$env->{SERVER_NAME} =~ s/:\d+$//; # cut off port number
}
@@ -161,6 +164,10 @@ sub daemon_detach {
__END__
+=head1 NAME
+
+Plack::Handler::FCGI - FastCGI handler for Plack
+
=head1 SYNOPSIS
# Run as a standalone daemon
@@ -276,15 +283,24 @@ See L<http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html#FastCgiExternalSe
=head3 lighttpd
-Host in the root path:
+To host the app in the root path, you're recommended to use lighttpd
+1.4.23 or newer with C<fix-root-scriptname> flag like below.
- fastcgi.server = ( "" =>
+ fastcgi.server = ( "/" =>
((
"socket" => "/tmp/fcgi.sock",
"check-local" => "disable"
+ "fix-root-scriptname" => "enable",
))
-Or in the non-root path over TCP:
+If you use lighttpd older than 1.4.22 where you don't have
+C<fix-root-scriptname>, mouting apps under the root causes wrong
+C<SCRIPT_NAME> and C<PATH_INFO> set. Also, mouting under the empty
+root (C<"">) or a path that has a trailing slash would still cause
+weird values set even with C<fix-root-scriptname>. In such cases you
+can use L<Plack::Middleware::LighttpdScriptNameFix> to fix it.
+
+To mount in the non-root path over TCP:
fastcgi.server = ( "/foo" =>
((
@@ -293,9 +309,10 @@ Or in the non-root path over TCP:
"check-local" => "disable"
))
-Plack::Handler::FCGI has a workaround for lighttpd's weird
-C<SCRIPT_NAME> and C<PATH_INFO> setting when you set I<check-local> to
-C<disable> so both configurations (root or non-root) should work fine.
+It's recommended that your mount path does B<NOT> have the trailing
+slash. If you I<really> need to have one, you should consider using
+L<Plack::Middleware::LighttpdScriptNameFix> to fix the wrong
+B<PATH_INFO> values set by lighttpd.
=cut
@@ -32,11 +32,13 @@ Plack::Handler::HTTP::Server::PSGI - adapter for HTTP::Server::PSGI
% plackup -s HTTP::Server::PSGI \
--host 127.0.0.1 --port 9091 --timeout 120
-=head1 CONFIGURATIONS
+=head1 BACKWARD COMPATIBLITY
+
+Since Plack 0.99_22 this handler doesn't support preforking
+configuration i.e. C<--max-workers>. Use L<Starman> or L<Starlet> if
+you need preforking PSGI web server.
-This adapter automatically loads Prefork implementation when
-C<max-workers> is set, but otherwise the default HTTP::Server::PSGI
-which is single process.
+=head1 CONFIGURATIONS
=over 4
@@ -52,26 +54,10 @@ Port number the server listens on. Defaults to 8080.
Number of seconds a request times out. Defaults to 300.
-=item max-keepalive-reqs
-
-Max requests per a keep-alive request. Defaults to 1, which means Keep-alive is off.
-
-=item keepalive-timeout
-
-Number of seconds a keep-alive request times out. Defaults to 2.
-
-=item max-workers
-
-Number of prefork workers. Defaults to 10.
-
=item max-reqs-per-child
Number of requests per worker to process. Defaults to 100.
-=item max-keepalive-reqs
-
-Max requests per a keep-alive request. Defaults to 100.
-
=back
=head1 AUTHOR
@@ -13,7 +13,7 @@ sub call {
my $res = try {
$self->app->($env);
} catch {
- $self->transform_error($_);
+ $self->transform_error($_, $env);
};
return $res if ref $res eq 'ARRAY';
@@ -29,7 +29,7 @@ sub call {
Carp::cluck $_;
$writer->close;
} else {
- my $res = $self->transform_error($_);
+ my $res = $self->transform_error($_, $env);
$respond->($res);
}
};
@@ -37,7 +37,7 @@ sub call {
}
sub transform_error {
- my($self, $e) = @_;
+ my($self, $e, $env) = @_;
my($code, $message);
if (blessed $e && $e->can('code')) {
@@ -47,6 +47,7 @@ sub transform_error {
overload::Method($e, '""') ? "$e" : undef;
} else {
$code = 500;
+ $env->{'psgi.errors'}->print($e);
}
if ($code !~ /^[3-5]\d\d$/) {
@@ -9,22 +9,18 @@ sub call {
my $res = $self->app->($env);
$self->response_cb($res, sub {
my $res = shift;
-
- my $h = Plack::Util::headers($res->[1]);
- if ($h->get('Content-Type') =~ m!/(?:json|javascript)! &&
+ if (defined $res->[2] && ref $res->[2] eq 'ARRAY' && @{$res->[2]} == 1) {
+ my $h = Plack::Util::headers($res->[1]);
+ if ($h->get('Content-Type') =~ m!/(?:json|javascript)! &&
$env->{QUERY_STRING} =~ /(?:^|&)callback=([^&]+)/) {
- # TODO: support callback params other than 'callback'
- my $cb = URI::Escape::uri_unescape($1);
-
- if ($cb =~ /^[\w\.\[\]]+$/) {
- $h->set('Content-Type', 'text/javascript');
-
- # The filter to transform the body into a JSONP response.
- my $isnt_first = 0;
- return sub {
- return ( $isnt_first++ ? '' : "$cb(" )
- . ( defined $_[0] ? $_[0] : ')' );
- };
+ # TODO: support callback params other than 'callback'
+ my $cb = URI::Escape::uri_unescape($1);
+ if ($cb =~ /^[\w\.\[\]]+$/) {
+ my $jsonp = "$cb($res->[2][0])";
+ $res->[2] = [ $jsonp ];
+ $h->set('Content-Length', length $jsonp);
+ $h->set('Content-Type', 'text/javascript');
+ }
}
}
});
@@ -44,7 +40,9 @@ Plack::Middleware::JSONP wraps JSON response, which has Content-Type
value either C<text/javascript> or C<application/json> as a JSONP
response which is specified with the C<callback> query parameter.
-Since this middleware removes the Content-Length header to rewrite the content body, you may also want to enable Plack::Middleware::ContentLength.
+This middleware only works with an application response with content
+body set as a single element array ref and doesn't touch the response
+otherwise.
=head1 AUTHOR
@@ -52,7 +50,7 @@ Tatsuhiko Miyagawa
=head1 SEE ALSO
-L<Plack> L<Plack::Middleware::ContentLength>
+L<Plack>
=cut
@@ -0,0 +1,93 @@
+package Plack::Middleware::LighttpdScriptNameFix;
+use strict;
+use parent qw/Plack::Middleware/;
+use Plack::Util::Accessor qw(script_name);
+
+sub prepare_app {
+ my $self = shift;
+
+ my $script_name = $self->script_name;
+ $script_name = '' unless defined($script_name);
+ $script_name =~ s!/$!!;
+ $self->script_name($script_name);
+}
+
+sub call {
+ my($self, $env) = @_;
+
+ if ($env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ /lighttpd/) {
+ $env->{PATH_INFO} = $env->{SCRIPT_NAME} . $env->{PATH_INFO};
+ $env->{SCRIPT_NAME} = $self->script_name;
+ $env->{PATH_INFO} =~ s/^\Q$env->{SCRIPT_NAME}\E//;
+ }
+
+ return $self->app->($env);
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Plack::Middleware::LighttpdScriptNameFix - fixes wrong SCRIPT_NAME and PATH_INFO that lighttpd sets
+
+=head1 SYNOPSIS
+
+ # in your app.psgi
+ use Plack::Builder;
+
+ builder {
+ enable "LighttpdScriptNameFix";
+ $app;
+ };
+
+ # Or from the command line
+ plackup -s FCGI -e 'enable "LighttpdScriptNameFix"' /path/to/app.psgi
+
+=head1 DESCRIPTION
+
+This middleware fixes wrong C<SCRIPT_NAME> and C<PATH_INFO> set by
+lighttpd when you mount your app under the root path ("/"). If you use
+lighttpd 1.4.23 or later you can instead enable C<fix-root-scriptname>
+flag inside C<fastcgi.server> instead of using this middleware.
+
+=head1 CONFIGURATION
+
+=over 4
+
+=item script_name
+
+Even with C<fix-root-scriptname>, lighttpd I<still> sets weird
+C<SCRIPT_NAME> and C<PATH_INFO> if you mount your application at C<"">
+or something that ends with C</>. Setting C<script_name> option tells
+the middleware how to reconstruct the new correct C<SCRIPT_NAME> and
+C<PATH_INFO>.
+
+If you mount the app under C</something/>, you should set:
+
+ enable "LighttpdScriptNameFix", script_name => "/something";
+
+and when a request for C</something/a/b?param=1> comes, C<SCRIPT_NAME>
+becomes C</something> and C<PATH_INFO> becomes C</a/b>.
+
+C<script_name> option is set to empty by default, which means all the
+request path is set to C<PATH_INFO> and it behaves like your fastcgi
+application is mounted in the root path.
+
+=back
+
+=head1 AUTHORS
+
+Yury Zavarin
+
+Tatsuhiko Miyagawa
+
+=head1 SEE ALSO
+
+L<Plack::Handler::FCGI>
+L<http://github.com/miyagawa/Plack/issues#issue/68>
+L<https://redmine.lighttpd.net/issues/729>
+
+=cut
+
@@ -5,11 +5,12 @@ use parent qw/Plack::Middleware/;
use Devel::StackTrace;
use Devel::StackTrace::AsHTML;
use Try::Tiny;
+use Plack::Util::Accessor qw( force );
our $StackTraceClass = "Devel::StackTrace";
# Optional since it needs PadWalker
-if (try { require Devel::StackTrace::WithLexicals; 1 }) {
+if ($ENV{PLACK_STACKTRACE_LEXICALS} && try { require Devel::StackTrace::WithLexicals; 1 }) {
$StackTraceClass = "Devel::StackTrace::WithLexicals";
}
@@ -22,13 +23,14 @@ sub call {
die @_;
};
- my $res = try { $self->app->($env) };
+ my $caught;
+ my $res = try { $self->app->($env) } catch { $caught = $_ };
- if ($trace && (!$res or $res->[0] == 500)) {
+ if ($trace && ($caught || $self->{force} && ref $res eq 'ARRAY' && $res->[0] == 500) ) {
if (($env->{HTTP_ACCEPT} || '*/*') =~ /html/) {
- $res = [500, ['Content-Type' => 'text/html; charset=utf-8'], [ $trace->as_html ]];
+ $res = [500, ['Content-Type' => 'text/html; charset=utf-8'], [ utf8_safe($trace->as_html) ]];
} else {
- $res = [500, ['Content-Type' => 'text/plain; charset=utf-8'], [ $trace->as_string ]];
+ $res = [500, ['Content-Type' => 'text/plain; charset=utf-8'], [ utf8_safe($trace->as_string) ]];
}
}
@@ -40,6 +42,22 @@ sub call {
return $res;
}
+sub utf8_safe {
+ my $str = shift;
+
+ # NOTE: I know messing with utf8:: in the code is WRONG, but
+ # because we're running someone else's code that we can't
+ # guarnatee which encoding an exception is encoded, there's no
+ # better way than doing this. The latest Devel::StackTrace::AsHTML
+ # (0.08 or later) encodes high-bit chars as HTML entities, so this
+ # path won't be executed.
+ if (utf8::is_utf8($str)) {
+ utf8::encode($str);
+ }
+
+ $str;
+}
+
1;
__END__
@@ -50,7 +68,7 @@ Plack::Middleware::StackTrace - Displays stack trace when your app dies
=head1 SYNOPSIS
- enable "Plack::Middleware::StackTrace";
+ enable "StackTrace";
=head1 DESCRIPTION
@@ -58,11 +76,37 @@ This middleware catches exceptions (run-time errors) happening in your
application and displays nice stack trace screen.
This middleware is enabled by default when you run L<plackup> in the
-default development mode.
+default I<development> mode.
+
+You're recommended to use this middleware during the development and
+use L<Plack::Middleware::HTTPExceptions> in the deployment mode as a
+replacement, so that all the exceptions thrown from your application
+still get caught and rendered as a 500 error response, rather than
+crashing the web server.
+
+Catching errors in streaming response is not supported.
=head1 CONFIGURATION
-No configuration option is available.
+=over 4
+
+=item force
+
+ enable "StackTrace", force => 1;
+
+Force display the stack trace when an error occurs within your
+application and the response code from your application is
+500. Defaults to off.
+
+The use case of this option is that when your framework catches all
+the exceptions in the main handler and returns all failures in your
+code as a normal 500 PSGI error response. In such cases, this
+middleware would never have a chance to display errors because it
+can't tell if it's an application error or just random C<eval> in your
+code. This option enforces the middleware to display stack trace even
+if it's not the direct error thrown by the application.
+
+=back
=head1 AUTHOR
@@ -72,7 +116,7 @@ Tatsuhiko Miyagawa
=head1 SEE ALSO
-L<Devel::StackTrace::AsHTML> L<Plack::Middleware>
+L<Devel::StackTrace::AsHTML> L<Plack::Middleware> L<Plack::Middleware::HTTPExceptions>
=cut
@@ -2,7 +2,7 @@ package Plack::Request;
use strict;
use warnings;
use 5.008_001;
-our $VERSION = '0.9920';
+our $VERSION = '0.9929';
$VERSION = eval $VERSION;
use HTTP::Headers;
@@ -46,6 +46,9 @@ sub secure { $_[0]->scheme eq 'https' }
sub body { $_[0]->env->{'psgi.input'} }
sub input { $_[0]->env->{'psgi.input'} }
+sub content_length { $_[0]->env->{CONTENT_LENGTH} }
+sub content_type { $_[0]->env->{CONTENT_TYPE} }
+
sub session { $_[0]->env->{'psgix.session'} }
sub session_options { $_[0]->env->{'psgix.session.options'} }
sub logger { $_[0]->env->{'psgix.logger'} }
@@ -90,9 +93,9 @@ sub content {
$self->_parse_request_body;
}
- my $fh = $self->input or return '';
- my $cl = $self->content_length or return '';
- $fh->read(my($content), $self->content_length || 0, 0);
+ my $fh = $self->input or return '';
+ my $cl = $self->env->{CONTENT_LENGTH} or return'';
+ $fh->read(my($content), $cl, 0);
$fh->seek(0, 0);
return $content;
@@ -116,10 +119,8 @@ sub headers {
}
$self->{headers};
}
-# shortcut
+
sub content_encoding { shift->headers->content_encoding(@_) }
-sub content_length { shift->headers->content_length(@_) }
-sub content_type { shift->headers->content_type(@_) }
sub header { shift->headers->header(@_) }
sub referer { shift->headers->referer(@_) }
sub user_agent { shift->headers->user_agent(@_) }
@@ -252,7 +253,6 @@ sub _parse_request_body {
return;
}
- # Do not use ->content_type to get multipart boundary correctly
my $body = HTTP::Body->new($ct, $cl);
my $input = $self->input;
@@ -1,7 +1,7 @@
package Plack::Response;
use strict;
use warnings;
-our $VERSION = '0.9920';
+our $VERSION = '0.9929';
$VERSION = eval $VERSION;
use Plack::Util::Accessor qw(body status);
@@ -50,7 +50,8 @@ sub parse_options {
'r|reload' => sub { $self->{loader} = "Restarter" },
'R|Reload=s' => sub { $self->{loader} = "Restarter"; $self->loader->watch(split ",", $_[1]) },
'L|loader=s' => \$self->{loader},
- "h|help", => \$self->{help},
+ "h|help" => \$self->{help},
+ "v|version" => \$self->{version},
);
my(@options, @argv);
@@ -60,8 +61,10 @@ sub parse_options {
$v[0] =~ tr/-/_/;
if (@v == 2) {
push @options, @v;
+ } elsif ($v[0] =~ s/^(disable|enable)_//) {
+ push @options, $v[0], $1 eq 'enable';
} else {
- push @options, @v, shift @ARGV;
+ push @options, $v[0], shift @ARGV;
}
} else {
push @argv, $_;
@@ -104,6 +107,14 @@ sub mangle_host_port_socket {
return host => $host, port => $port, listen => \@listen, socket => $socket;
}
+sub version_cb {
+ my $self = shift;
+ $self->{version_cb} || sub {
+ require Plack;
+ print "Plack $Plack::VERSION\n";
+ };
+}
+
sub setup {
my $self = shift;
@@ -112,6 +123,11 @@ sub setup {
Pod::Usage::pod2usage(0);
}
+ if ($self->{version}) {
+ $self->version_cb->();
+ exit;
+ }
+
lib->import(@{$self->{includes}}) if @{$self->{includes}};
if ($self->{eval}) {
@@ -1,6 +1,6 @@
package Plack::Server::ServerSimple;
use strict;
-our $VERSION = '0.9920';
+our $VERSION = '0.9929';
$VERSION = eval $VERSION;
use parent qw(Plack::Handler::HTTP::Server::Simple);
@@ -19,6 +19,10 @@ Plack::Server::Standalone::Prefork - DEPRECATED
=head1 DESCRIPTION
-B<This module is deprecated>. See L<Plack::Handler::Standalone>.
+B<This module is deprecated>.
+
+=head1 SEE ALSO
+
+L<HTTP::Server::PSGI> L<Starman> L<Starlet>
=cut
@@ -308,12 +308,7 @@ our @TEST = (
},
],
[
- # PEP-333 says:
- # If the iterable returned by the application has a close() method,
- # the server or gateway must call that method upon completion of the
- # current request, whether the request was completed normally, or
- # terminated early due to an error.
- 'call close after read file-like',
+ 'call close after read IO::Handle-like',
sub {
my $cb = shift;
my $res = $cb->(GET "http://127.0.0.1/call_close");
@@ -322,14 +317,13 @@ our @TEST = (
sub {
my $env = shift;
{
- package CalledClose;
our $closed = -1;
- sub new { $closed = 0; my $i=0; bless \$i, 'CalledClose' }
- sub getline {
+ sub CalledClose::new { $closed = 0; my $i=0; bless \$i, 'CalledClose' }
+ sub CalledClose::getline {
my $self = shift;
return $$self++ < 4 ? $$self : undef;
}
- sub close { ::ok(1, 'closed') if defined &::ok }
+ sub CalledClose::close { ::ok(1, 'closed') if defined &::ok }
}
return [
200,
@@ -3,7 +3,7 @@ package Plack;
use strict;
use warnings;
use 5.008_001;
-our $VERSION = '0.9920';
+our $VERSION = '0.9929';
$VERSION = eval $VERSION;
1;
@@ -120,8 +120,13 @@ L<Plack::Test::Suite> is a test suite to test a new PSGI server backend.
=head2 Patches and Bug Fixes
Small patches and bug fixes can be either submitted via nopaste on IRC
-L<irc://irc.perl.org/#plack> or email. You could also fork on github
-(http://github.com/miyagawa/Plack) to make larger fixes.
+L<irc://irc.perl.org/#plack> or L<the github issue
+tracker|http://github.com/miyagawa/Plack/issues>. Forking on
+L<github|http://github.com/miyagawa/Plack> is another good way if you
+intend to make larger fixes.
+
+See also L<http://contributing.appspot.com/plack> when you think this
+document is terribly outdated.
=head2 Module Namespaces
@@ -148,14 +153,14 @@ framework. It's like naming your application under CGI:: namespace if
it's supposed to run on CGI and that is a really bad choice and
confuse people.
-=head1 COPYRIGHT
-
-Copyright 2009- Tatsuhiko Miyagawa
-
=head1 AUTHOR
Tatsuhiko Miyagawa
+=head1 COPYRIGHT
+
+Copyright 2009-2010 Tatsuhiko Miyagawa
+
=head1 CONTRIBUTORS
Yuval Kogman (nothingmuch)
@@ -164,7 +169,7 @@ Tokuhiro Matsuno (tokuhirom)
Kazuhiro Osawa (Yappo)
-Kzzuho Oku
+Kazuho Oku
Florian Ragwitz (rafl)
@@ -53,11 +53,18 @@ sub test_lighty_external (&@) {
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);
+ $callback->($lighty_port, $fcgi_port, ($ver && $ver < 4.23));
warn `cat $tmpdir/error.log` if $ENV{DEBUG};
},
server => sub {
@@ -98,11 +105,12 @@ server.port = $port
# HTTP::Engine app specific fcgi setup
fastcgi.server = (
- "" => ((
+ "/" => ((
"check-local" => "disable",
"host" => "127.0.0.1",
"port" => $fcgiport,
"idle-timeout" => 20,
+ "fix-root-scriptname" => "enable", # for 1.4.23 or later
))
)
END
@@ -12,24 +12,32 @@ my $fcgi_port;
test_lighty_external(
sub {
- ($lighty_port, $fcgi_port) = @_;
- Plack::Test::Suite->run_server_tests(\&run_server, $fcgi_port, $lighty_port);
+ ($lighty_port, $fcgi_port, my $needs_fix) = @_;
+ Plack::Test::Suite->run_server_tests(run_server_cb($needs_fix), $fcgi_port, $lighty_port);
done_testing();
}
);
-sub run_server {
- my($port, $app) = @_;
+sub run_server_cb {
+ my $needs_fix = shift;
- $| = 0; # Test::Builder autoflushes this. reset!
+ require Plack::Middleware::LighttpdScriptNameFix;
+ return sub {
+ my($port, $app) = @_;
- my $server = Plack::Handler::FCGI->new(
- host => '127.0.0.1',
- port => $port,
- manager => '',
- keep_stderr => 1,
- );
- $server->run($app);
+ note "Applying LighttpdScriptNameFix" if $needs_fix;
+ $app = Plack::Middleware::LighttpdScriptNameFix->wrap($app) if $needs_fix;
+
+ $| = 0; # Test::Builder autoflushes this. reset!
+
+ my $server = Plack::Handler::FCGI->new(
+ host => '127.0.0.1',
+ port => $port,
+ manager => '',
+ keep_stderr => 1,
+ );
+ $server->run($app);
+ };
}
@@ -1,15 +0,0 @@
-use strict;
-use warnings;
-use Test::More;
-use Test::Requires {
- 'HTTP::Parser::XS' => 0,
- 'Parallel::Prefork' => 0.04,
-};
-
-use FindBin;
-use Plack;
-use Plack::Test::Suite;
-
-Plack::Test::Suite->run_server_tests('Standalone', undef, undef, max_workers => 10);
-done_testing();
-
@@ -1,4 +1,5 @@
use strict;
+no warnings 'redefine';
use Test::More;
use Plack::Loader;
@@ -0,0 +1,62 @@
+use strict;
+use Test::Requires qw(IO::Handle::Util);
+
+package MyComponent;
+use parent 'Plack::Component';
+use Plack::Util::Accessor qw( res cb );
+
+sub call { return $_[0]->response_cb( $_[0]->res, $_[0]->cb ); }
+
+package main;
+use IO::Handle::Util qw(:io_from);
+use HTTP::Request::Common;
+use Test::More;
+use Plack::Test;
+
+# Various kinds of PSGI responses.
+sub generate_responses {
+ [200, ['Content-Type' => 'text/plain'], ['Hello']],
+ [200, ['Content-Type' => 'text/plain'], io_from_array ['Hello']],
+ sub { $_[0]->([ 200, ['Content-Type' => 'text/plain'], ['Hello'] ]) },
+ sub {
+ my $writer = $_[0]->([ 200, ['Content-Type' => 'text/plain'] ]);
+ $writer->write( 'Hello' );
+ $writer->close;
+ },
+}
+
+# $body filters can return undef with no warnings.
+for my $res ( generate_responses ) {
+ my @warns;
+ local $SIG{__WARN__} = sub { push @warns, @_ };
+
+ my $app = MyComponent->new(
+ res => $res, cb => sub { sub { $_[0] } },
+ );
+ test_psgi( $app, sub { $_[0]->(GET '/') } );
+
+ is_deeply \@warns, [];
+}
+
+for my $res ( generate_responses ) {
+ my $app = MyComponent->new(
+ res => $res, cb => sub {
+ my $done;
+ sub {
+ return if $done;
+ if (defined $_[0]) {
+ return $_[0];
+ } else {
+ $done = 1;
+ return 'END';
+ }
+ },
+ },
+ );
+ test_psgi( $app, sub {
+ my $res = $_[0]->(GET '/');
+ is $res->content, 'HelloEND';
+ } );
+}
+
+done_testing;
@@ -17,16 +17,6 @@ my @app = (
);
};
},
- sub {
- return sub {
- my $respond = shift;
- my $writer = $respond->(
- [ 200, [ 'Content-Type' => 'application/json' ] ],
- );
- $writer->write( $json );
- $writer->close;
- };
- },
);
for my $app ( @app ) {
@@ -1,6 +1,6 @@
use strict;
use Plack::Test;
-use Test::Requires qw(Log::Dispatch::Array);
+use Test::Requires { 'Log::Dispatch' => 2.25, 'Log::Dispatch::Array' => 1.001 };
use Test::More;
use Plack::Middleware::LogDispatch;
@@ -0,0 +1,39 @@
+use strict;
+use warnings;
+use Test::More;
+use Plack::Middleware::StackTrace;
+use Plack::Test;
+use HTTP::Request::Common;
+
+my $app = sub {
+ eval { die "Blah" };
+
+ return [ 500, [ 'Content-Type', 'text/html' ], [ "Fancy Error" ] ];
+};
+
+my $default_app = Plack::Middleware::StackTrace->wrap($app);
+
+test_psgi $default_app, sub {
+ my $cb = shift;
+
+ my $req = GET "/";
+ my $res = $cb->($req);
+
+ is $res->code, 500;
+ like $res->content, qr/Fancy Error/;
+};
+
+my $force_app = Plack::Middleware::StackTrace->wrap($app, force => 1);
+
+test_psgi $force_app, sub {
+ my $cb = shift;
+
+ my $req = GET "/";
+ my $res = $cb->($req);
+
+ is $res->code, 500;
+ like $res->content, qr/Blah/;
+};
+
+done_testing;
+
@@ -0,0 +1,30 @@
+use strict;
+use warnings;
+use Test::More;
+use Plack::Middleware::StackTrace;
+use Plack::Test;
+use HTTP::Request::Common;
+
+my $app = sub {
+ eval { require DooBar };
+
+ return sub {
+ my $respond = shift;
+ $respond->([ 200, [ "Content-Type", "text/plain" ], [ "Hello World" ] ]);
+ };
+};
+
+$app = Plack::Middleware::StackTrace->wrap($app);
+
+test_psgi $app, sub {
+ my $cb = shift;
+
+ my $req = GET "/";
+ my $res = $cb->($req);
+
+ ok $res->is_success;
+ like $res->content, qr/Hello World/;
+};
+
+done_testing;
+
@@ -0,0 +1,28 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::Requires { 'Devel::StackTrace::AsHTML' => 0.08 };
+use Plack::Middleware::StackTrace;
+use Plack::Test;
+use HTTP::Request::Common;
+
+$Plack::Test::Impl = "Server";
+my $app = Plack::Middleware::StackTrace->wrap(sub { die "Foo \x{30c6}" });
+
+test_psgi $app, sub {
+ my $cb = shift;
+
+ my $req = GET "/";
+ $req->header(Accept => "text/html,*/*");
+ my $res = $cb->($req);
+
+ like $res->content, qr/Foo テ/;
+
+ $req = GET "/";
+ $res = $cb->($req);
+ is $res->code, 500;
+ like $res->content, qr/Foo/;
+};
+
+done_testing;
+
@@ -0,0 +1,33 @@
+use strict;
+use Test::More;
+use Plack::App::URLMap;
+use Plack::Test;
+use HTTP::Request::Common;
+
+$Plack::Test::Impl = "Server";
+
+my $make_app = sub {
+ my $name = shift;
+ sub {
+ my $env = shift;
+ my $body = join "|", $name, $env->{SCRIPT_NAME}, $env->{PATH_INFO};
+ return [ 200, [ 'Content-Type' => 'text/plain' ], [ $body ] ];
+ };
+};
+
+my $app1 = $make_app->("app1");
+my $app2 = $make_app->("app2");
+
+my $app = Plack::App::URLMap->new;
+$app->map("http://127.0.0.1/" => $app1);
+$app->map("/" => $app2);
+
+test_psgi app => $app, client => sub {
+ my $cb = shift;
+
+ my $res;
+ $res = $cb->(GET "http://127.0.0.1/");
+ is $res->content, 'app1||/';
+};
+
+done_testing;
@@ -18,6 +18,8 @@ is_deeply p('-l', ':80'),
{ host => undef, port => 80, listen => [ ':80' ], socket => undef };
is_deeply p('-l', '10.0.0.1:80', '-l', 'unix.sock'),
{ host => '10.0.0.1', port => 80, listen => [ '10.0.0.1:80', 'unix.sock' ], socket => 'unix.sock' };
+is_deeply p('-l', ':80', '--disable-foo', '--enable-bar'),
+ { host => undef, port => 80, listen => [ ':80' ], socket => undef, foo => '', bar => 1 };
done_testing;