File Coverage

blib/lib/Catalyst/Engine/Apache.pm
Criterion Covered Total %
statement 24 122 19.6
branch 0 58 0.0
condition 1 42 2.3
subroutine 8 17 47.0
pod 9 9 100.0
total 42 248 16.9


line stmt bran cond sub pod time code
1             package Catalyst::Engine::Apache;
2              
3 1     1   490 use strict;
  1         1  
  1         25  
4 1     1   3 use warnings;
  1         1  
  1         28  
5 1     1   3 use base 'Catalyst::Engine';
  1         7  
  1         452  
6              
7 1     1   672195 use File::Spec;
  1         1  
  1         20  
8 1     1   3 use URI;
  1         1  
  1         16  
9 1     1   528 use URI::http;
  1         5646  
  1         38  
10 1     1   570 use URI::https;
  1         232  
  1         75  
11              
12             use constant MP2 => (
13             exists $ENV{MOD_PERL_API_VERSION} and
14 1   33     1540 $ENV{MOD_PERL_API_VERSION} >= 2
15 1     1   6 );
  1         2  
16              
17             our $VERSION = '1.13_01';
18             $VERSION = eval $VERSION;
19              
20             __PACKAGE__->mk_accessors(qw/apache return/);
21              
22             sub prepare_request {
23 0     0 1   my ( $self, $c, $r ) = @_;
24 0           $self->apache( $r );
25 0           $self->return( undef );
26             }
27              
28             sub prepare_connection {
29 0     0 1   my ( $self, $c ) = @_;
30              
31 0           $c->request->address( $self->apache->connection->remote_ip );
32              
33             PROXY_CHECK:
34             {
35 0           my $headers = $self->apache->headers_in;
  0            
36 0 0         unless ( $c->config->{using_frontend_proxy} ) {
37 0 0         last PROXY_CHECK if $c->request->address ne '127.0.0.1';
38 0 0         last PROXY_CHECK if $c->config->{ignore_frontend_proxy};
39             }
40 0 0         last PROXY_CHECK unless $headers->{'X-Forwarded-For'};
41              
42             # If we are running as a backend server, the user will always appear
43             # as 127.0.0.1. Select the most recent upstream IP (last in the list)
44 0           my ($ip) = $headers->{'X-Forwarded-For'} =~ /([^,\s]+)$/;
45 0           $c->request->address( $ip );
46             }
47              
48 0           $c->request->hostname( $self->apache->connection->remote_host );
49 0           $c->request->protocol( $self->apache->protocol );
50 0           $c->request->user( $self->apache->user );
51 0           $c->request->remote_user( $self->apache->user );
52              
53             # when config options are set, check them here first
54 0 0         if ($INC{'Apache2/ModSSL.pm'}) {
55 0 0         $c->request->secure(1) if $self->apache->connection->is_https;
56             } else {
57 0           my $https = $self->apache->subprocess_env('HTTPS');
58 0 0 0       $c->request->secure(1) if defined $https and uc $https eq 'ON';
59             }
60              
61             }
62              
63             sub prepare_query_parameters {
64 0     0 1   my ( $self, $c ) = @_;
65            
66 0 0         if ( my $query_string = $self->apache->args ) {
67 0           $self->SUPER::prepare_query_parameters( $c, $query_string );
68             }
69             }
70              
71             sub prepare_headers {
72 0     0 1   my ( $self, $c ) = @_;
73              
74 0           $c->request->method( $self->apache->method );
75              
76 0 0         if ( my %headers = %{ $self->apache->headers_in } ) {
  0            
77 0           $c->request->header( %headers );
78             }
79             }
80              
81             sub prepare_path {
82 0     0 1   my ( $self, $c ) = @_;
83              
84 0 0         my $scheme = $c->request->secure ? 'https' : 'http';
85 0   0       my $host = $self->apache->hostname || 'localhost';
86 0           my $port = $self->apache->get_server_port;
87              
88             # If we are running as a backend proxy, get the true hostname
89             PROXY_CHECK:
90             {
91 0 0         unless ( $c->config->{using_frontend_proxy} ) {
  0            
92 0 0         last PROXY_CHECK if $host !~ /localhost|127.0.0.1/;
93 0 0         last PROXY_CHECK if $c->config->{ignore_frontend_proxy};
94             }
95 0 0         last PROXY_CHECK unless $c->request->header( 'X-Forwarded-Host' );
96            
97 0           $host = $c->request->header( 'X-Forwarded-Host' );
98              
99 0 0         if ( $host =~ /^(.+):(\d+)$/ ) {
100 0           $host = $1;
101 0           $port = $2;
102             } else {
103             # backend could be on any port, so
104             # assume frontend is on the default port
105 0 0         $port = $c->request->secure ? 443 : 80;
106             }
107             }
108              
109 0           my $base_path = '';
110              
111             # Are we running in a non-root Location block?
112 0           my $location = $self->apache->location;
113 0 0 0       if ( $location && $location ne '/' ) {
114 0           $base_path = $location;
115             }
116            
117             # Using URI directly is way too slow, so we construct the URLs manually
118 0           my $uri_class = "URI::$scheme";
119            
120 0 0 0       if ( $port !~ /^(?:80|443)$/ && $host !~ /:/ ) {
121 0           $host .= ":$port";
122             }
123            
124             # We want the path before Apache escapes it. Under mod_perl2 this is available
125             # with the unparsed_uri method. Under mod_perl 1 we must parse it out of the
126             # request line.
127 0           my ($path, $qs);
128            
129 0           if ( MP2 ) {
130             ($path, $qs) = split /\?/, $self->apache->unparsed_uri, 2;
131             }
132             else {
133 0           my (undef, $path_query) = split / /, $self->apache->the_request, 3;
134 0           ($path, $qs) = split /\?/, $path_query, 2;
135             }
136            
137             # Don't check for LocationMatch blocks if requested
138             # http://rt.cpan.org/Ticket/Display.html?id=26921
139 0 0         if ( $self->apache->dir_config('CatalystDisableLocationMatch') ) {
    0          
140 0           $base_path = '';
141             }
142            
143             # Check if $base_path appears to be a regex (contains invalid characters),
144             # meaning we're in a LocationMatch block
145             elsif ( $base_path =~ m/[^$URI::uric]/o ) {
146             # Find out what part of the URI path matches the LocationMatch regex,
147             # that will become our base
148 0           my $match = qr/($base_path)/;
149 0           my ($base_match) = $path =~ $match;
150            
151 0   0       $base_path = $base_match || '';
152             }
153              
154             # Strip leading slash
155 0           $path =~ s{^/+}{};
156            
157             # base must end in a slash
158 0 0         $base_path .= '/' unless $base_path =~ m{/$};
159              
160             # Are we an Apache::Registry script? Why anyone would ever want to run
161             # this way is beyond me, but we'll support it!
162             # XXX: This needs a test
163 0 0 0       if ( defined $ENV{SCRIPT_NAME} && $self->apache->filename && -f $self->apache->filename && -x _ ) {
      0        
      0        
164 0           $base_path .= $ENV{SCRIPT_NAME};
165             }
166            
167             # If the path is contained within the base, we need to make the path
168             # match base. This handles the case where the app is running at /deep/path
169             # but a request to /deep/path fails where /deep/path/ does not.
170 0 0 0       if ( $base_path ne '/' && $base_path ne $path && $base_path =~ m{/$path} ) {
      0        
171 0           $path = $base_path;
172 0           $path =~ s{^/+}{};
173             }
174            
175 0 0         my $query = $qs ? '?' . $qs : '';
176 0           my $uri = $scheme . '://' . $host . '/' . $path . $query;
177              
178 0           $c->request->uri( bless \$uri, $uri_class );
179            
180 0           my $base_uri = $scheme . '://' . $host . $base_path;
181              
182 0           $c->request->base( bless \$base_uri, $uri_class );
183             }
184              
185             sub read_chunk {
186 0     0 1   my $self = shift;
187 0           my $c = shift;
188            
189 0           $self->apache->read( @_ );
190             }
191              
192             sub finalize_body {
193 0     0 1   my ( $self, $c ) = @_;
194            
195 0           $self->SUPER::finalize_body($c);
196            
197             # Data sent using $self->apache->print is buffered, so we need
198             # to flush it after we are done writing.
199 0           $self->apache->rflush;
200             }
201              
202             sub finalize_headers {
203 0     0 1   my ( $self, $c ) = @_;
204              
205 0           for my $name ( $c->response->headers->header_field_names ) {
206 0 0         next if $name =~ /^Content-(Length|Type)$/i;
207 0           my @values = $c->response->header($name);
208             # allow X headers to persist on error
209 0 0         if ( $name =~ /^X-/i ) {
210 0           $self->apache->err_headers_out->add( $name => $_ ) for @values;
211             }
212             else {
213 0           $self->apache->headers_out->add( $name => $_ ) for @values;
214             }
215             }
216              
217             # persist cookies on error responses
218 0 0 0       if ( $c->response->header('Set-Cookie') && $c->response->status >= 400 ) {
219 0           for my $cookie ( $c->response->header('Set-Cookie') ) {
220 0           $self->apache->err_headers_out->add( 'Set-Cookie' => $cookie );
221             }
222             }
223              
224             # The trick with Apache is to set the status code in $apache->status but
225             # always return the OK constant back to Apache from the handler.
226 0           $self->apache->status( $c->response->status );
227 0   0       $c->response->status( $self->return || $self->ok_constant );
228              
229 0   0       my $type = $c->response->header('Content-Type') || 'text/html';
230 0           $self->apache->content_type( $type );
231              
232 0 0         if ( my $length = $c->response->content_length ) {
233 0           $self->apache->set_content_length( $length );
234             }
235              
236 0           return 0;
237             }
238              
239             sub write {
240 0     0 1   my ( $self, $c, $buffer ) = @_;
241              
242 0 0 0       if ( ! $self->apache->connection->aborted && defined $buffer) {
243 0           return $self->apache->print( $buffer );
244             }
245 0           return;
246             }
247              
248             1;
249             __END__
250              
251             =head1 NAME
252              
253             Catalyst::Engine::Apache - Catalyst Apache Engines
254              
255             =head1 SYNOPSIS
256              
257             For example Apache configurations, see the documentation for the engine that
258             corresponds to your Apache version.
259              
260             C<Catalyst::Engine::Apache::MP13> - mod_perl 1.3x
261              
262             C<Catalyst::Engine::Apache2::MP19> - mod_perl 1.99x
263              
264             C<Catalyst::Engine::Apache2::MP20> - mod_perl 2.x
265              
266             =head1 DESCRIPTION
267              
268             These classes provide mod_perl support for Catalyst.
269              
270             =head1 METHODS
271              
272             =head2 $c->engine->apache
273              
274             Returns an C<Apache>, C<Apache::RequestRec> or C<Apache2::RequestRec> object,
275             depending on your mod_perl version. This method is also available as
276             $c->apache.
277              
278             =head2 $c->engine->return
279              
280             If you need to return something other than OK from the mod_perl handler,
281             you may set any other Apache constant in this method. You should only use
282             this method if you know what you are doing or bad things may happen!
283             For example, to return DECLINED in mod_perl 2:
284              
285             use Apache2::Const -compile => qw(DECLINED);
286             $c->engine->return( Apache2::Const::DECLINED );
287              
288             =head2 NOTES ABOUT LOCATIONMATCH
289              
290             The Apache engine tries to figure out the correct base path if your app is
291             running within a LocationMatch block. For example:
292              
293             <LocationMatch ^/match/(this|that)*>
294             SetHandler modperl
295             PerlResponseHandler MyApp
296             </LocationMatch>
297              
298             This will correctly set the base path to '/match/this/' or '/match/that/' depending
299             on which path was used for the request.
300              
301             In some cases this may not be what you want, so you can disable this behavior
302             by adding this to your configuration:
303              
304             PerlSetVar CatalystDisableLocationMatch 1
305              
306             =head2 NOTES ON NON-STANDARD PORTS
307              
308             If you wish to run your site on a non-standard port you will need to use the
309             C<Port> Apache config rather than C<Listen>. This will result in the correct
310             port being added to urls created using C<uri_for>.
311              
312             Port 8080
313              
314             =head1 OVERLOADED METHODS
315              
316             This class overloads some methods from C<Catalyst::Engine>.
317              
318             =over 4
319              
320             =item prepare_request($r)
321              
322             =item prepare_connection
323              
324             =item prepare_query_parameters
325              
326             =item prepare_headers
327              
328             =item prepare_path
329              
330             =item read_chunk
331              
332             =item finalize_body
333              
334             =item finalize_headers
335              
336             =item write
337              
338             =back
339              
340             =head1 SEE ALSO
341              
342             L<Catalyst> L<Catalyst::Engine>.
343              
344             =head1 AUTHORS
345              
346             Sebastian Riedel, <sri@cpan.org>
347              
348             Christian Hansen, <ch@ngmedia.com>
349              
350             Andy Grundman, <andy@hybridized.org>
351              
352             =head1 COPYRIGHT
353              
354             This program is free software, you can redistribute it and/or modify it under
355             the same terms as Perl itself.
356              
357             =cut