1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
package Plack::Handler::Apache2;
use strict;
use warnings;
use Apache2::RequestRec;
use Apache2::RequestIO;
use Apache2::RequestUtil;
use Apache2::Response;
use Apache2::Const -compile => qw(OK);
use APR::Table;
use IO::Handle;
use Plack::Util;
use Scalar::Util;
my %apps; # psgi file to $app mapping
sub preload {
my $class = shift;
for my $app (@_) {
$class->load_app($app);
}
}
sub load_app {
my($class, $app) = @_;
return $apps{$app} ||= do {
local $ENV{MOD_PERL}; # trick Catalyst/CGI.pm etc.
Plack::Util::load_psgi $app;
};
}
sub call_app {
my ($class, $r, $app) = @_;
$r->subprocess_env; # let Apache create %ENV for us :)
my $env = {
%ENV,
'psgi.version' => [ 1, 1 ],
'psgi.url_scheme' => ($ENV{HTTPS}||'off') =~ /^(?:on|1)$/i ? 'https' : 'http',
'psgi.input' => $r,
'psgi.errors' => *STDERR,
'psgi.multithread' => Plack::Util::FALSE,
'psgi.multiprocess' => Plack::Util::TRUE,
'psgi.run_once' => Plack::Util::FALSE,
'psgi.streaming' => Plack::Util::TRUE,
'psgi.nonblocking' => Plack::Util::FALSE,
};
$class->_recalc_paths($r, $env);
my $res = $app->($env);
if (ref $res eq 'ARRAY') {
_handle_response($r, $res);
}
elsif (ref $res eq 'CODE') {
$res->(sub {
_handle_response($r, $_[0]);
});
}
else {
die "Bad response $res";
}
return Apache2::Const::OK;
}
sub handler {
my $class = __PACKAGE__;
my $r = shift;
my $psgi = $r->dir_config('psgi_app');
$class->call_app($r, $class->load_app($psgi));
}
# The method for PH::Apache2::Regitsry to override.
sub _recalc_paths {
my ($class, $r, $env) = @_;
my $vpath = $env->{SCRIPT_NAME} . $env->{PATH_INFO};
my $location = $r->location || "/";
$location =~ s{/$}{};
(my $path_info = $vpath) =~ s/^\Q$location\E//;
$env->{SCRIPT_NAME} = $location;
$env->{PATH_INFO} = $path_info;
}
sub _handle_response {
my ($r, $res) = @_;
my ($status, $headers, $body) = @{ $res };
my $hdrs = ($status >= 200 && $status < 300)
? $r->headers_out : $r->err_headers_out;
Plack::Util::header_iter($headers, sub {
my($h, $v) = @_;
if (lc $h eq 'content-type') {
$r->content_type($v);
} elsif (lc $h eq 'content-length') {
$r->set_content_length($v);
} else {
$hdrs->add($h => $v);
}
});
$r->status($status);
if (Scalar::Util::blessed($body) and $body->can('path') and my $path = $body->path) {
$r->sendfile($path);
} elsif (defined $body) {
Plack::Util::foreach($body, sub { $r->print(@_) });
$r->rflush;
}
else {
return Plack::Util::inline_object
write => sub { $r->print(@_); $r->rflush },
close => sub { $r->rflush };
}
return Apache2::Const::OK;
}
1;
__END__
=head1 NAME
Plack::Handler::Apache2 - Apache 2.0 handlers to run PSGI application
=head1 SYNOPSIS
<Location />
SetHandler perl-script
PerlResponseHandler Plack::Handler::Apache2
PerlSetVar psgi_app /path/to/app.psgi
</Location>
# Optional, preload the application in the parent like startup.pl
<Perl>
use Plack::Handler::Apache2;
Plack::Handler::Apache2->preload("/path/to/app.psgi");
</Perl>
=head1 DESCRIPTION
This is a handler module to run any PSGI application with mod_perl on Apache 2.x.
=head1 CREATING CUSTOM HANDLER
If you want to create a custom handler that loads or creates PSGI
applications using other means than loading from C<.psgi> files, you
can create your own handler class and use C<call_app> class method to
run your application.
package My::ModPerl::Handler;
use Plack::Handler::Apache2;
sub get_app {
# magic!
}
sub handler {
my $r = shift;
my $app = get_app();
Plack::Handler::Apache2->call_app($r, $app);
}
=head1 AUTHOR
Tatsuhiko Miyagawa
=head1 CONTRIBUTORS
Paul Driver
=head1 SEE ALSO
L<Plack>
=cut
|