File Coverage

blib/lib/Plack/App/FakeApache.pm
Criterion Covered Total %
statement 31 33 93.9
branch n/a
condition n/a
subroutine 11 11 100.0
pod n/a
total 42 44 95.4


line stmt bran cond sub pod time code
1             package Plack::App::FakeApache;
2              
3 2     2   16748 use strict;
  2         4  
  2         72  
4 2     2   9 use warnings;
  2         2  
  2         46  
5              
6 2     2   880 use Plack::Util;
  2         17785  
  2         60  
7 2     2   859 use Plack::Util::Accessor qw( authen_handler authz_handler response_handler handler dir_config root logger request_args request_class);
  2         475  
  2         16  
8 2     2   1434 use CGI::Emulate::PSGI;
  2         58889  
  2         65  
9              
10 2     2   13 use parent qw( Plack::Component );
  2         4  
  2         12  
11 2     2   2653 use attributes;
  2         1990  
  2         9  
12              
13 2     2   90 use Carp;
  2         2  
  2         105  
14 2     2   915 use Module::Load;
  2         1598  
  2         10  
15 2     2   112 use Scalar::Util qw( blessed );
  2         2  
  2         134  
16 2     2   1190 use Apache2::Const qw(OK DECLINED HTTP_OK HTTP_UNAUTHORIZED HTTP_NOT_FOUND);
  0            
  0            
17              
18             our $VERSION = 0.05;
19              
20             sub _get_phase_handlers
21             {
22             my $self = shift;
23             my $phase = shift;
24             my $accessor = $phase.'_handler';
25             my $handlers = $self->$accessor or return;
26             return @{$handlers};
27             }
28              
29             # RUN_FIRST
30             # Run until a handler returns something other than DECLINED...
31             sub _run_first
32             {
33             my $self = shift;
34             my $phase = shift;
35             my $fake_req = shift;
36             my $fallback_status = shift;
37              
38              
39             # NOTE. I don't think this will mess people around who don't use
40             # CGI, but if it does this could be conditionally applied with a
41             # boolean $fake_req->shim_cgi_brain_damage or something.
42              
43             # Mangle env to cope with certain kinds of CGI brain damage.
44             my $env = $fake_req->plack_request->env;
45             local %ENV = (%ENV, CGI::Emulate::PSGI->emulate_environment($env));
46             # and stdin!
47             local *STDIN = $env->{'psgi.input'};
48              
49              
50             my $status = OK;
51             foreach my $handler ($self->_get_phase_handlers($phase))
52             {
53             $status = $handler->($fake_req);
54             last if $status != DECLINED;
55             }
56             return (defined($status) and $status != DECLINED) ? $status : $fallback_status; # mod_perl seems to do this if all handlers decline
57             }
58              
59             sub call {
60             my ($self, $env) = @_;
61              
62             my %args = (
63             env => $env,
64             dir_config => $self->dir_config,
65             request_class => $self->request_class || 'Plack::App::FakeApache::Request',
66             %{$self->request_args || {}}
67             );
68              
69             $args{root} = $self->root if defined $self->root;
70              
71             if ( $self->logger ) {
72             my $logger = $self->logger;
73             $args{log} = $logger if blessed($logger) and !$logger->isa('IO::Handle');
74             $args{log} ||= Plack::FakeApache::Log->new( logger => sub { print $logger @_ } );
75             }
76             my $request_class = $args{request_class};
77             my $fake_req = $request_class->new(%args);
78              
79             my $status = $self->_run_handlers($fake_req);
80              
81             $fake_req->status($status == OK ? HTTP_OK : $status);
82             return $fake_req->finalize;
83             }
84              
85             sub _run_handlers
86             {
87             my $self = shift;
88             my $fake_req = shift;
89             my $status;
90              
91             # TODO: More request phases here...
92              
93             $status = $self->_run_first('authen', $fake_req, HTTP_UNAUTHORIZED);
94             return $status if $status != OK;
95              
96             $status = $self->_run_first('authz', $fake_req, HTTP_UNAUTHORIZED);
97             return $status if $status != OK;
98              
99             # we wrap the call to $handler->( ... ) in tie statements so
100             # prints, etc are caught and sent to the right place
101             tie *STDOUT, "Plack::App::FakeApache::Tie", $fake_req;
102             $status = $self->_run_first('response', $fake_req, HTTP_NOT_FOUND);
103             untie *STDOUT;
104             return $status if $status != OK;
105              
106             # TODO: More request phases here...
107              
108             return OK;
109             }
110              
111             sub prepare_app {
112             my $self = shift;
113             my $request_class = $self->request_class || 'Plack::App::FakeApache::Request';
114             load $request_class;
115              
116             $self->response_handler($self->response_handler || $self->handler);
117              
118             foreach my $accessor ( qw(authen_handler authz_handler response_handler) )
119             {
120             my $handlers = $self->$accessor or next;
121             my @handlers = ref($handlers) eq 'ARRAY' ? @{$handlers} : ($handlers);
122             @handlers = map({ $self->_massage_handler($_) } @handlers);
123             $self->$accessor([ @handlers ]);
124             }
125              
126             carp "handler or response_handler not defined" unless $self->response_handler;
127              
128             # Workaround for mod_perl handlers doing CGI->new($r). CGI doesn't
129             # know our fake request class, so we hijack CGI->new() and explicitly
130             # pass the request query string instead...
131             my $new = CGI->can('new');
132             no warnings qw(redefine);
133             *CGI::new = sub {
134             my $request_class = $self->request_class || 'Plack::App::FakeApache::Request';
135              
136             if (blessed($_[1]) and $_[1]->isa($request_class)) {
137             return $new->(CGI => $_[1]->env->{QUERY_STRING} || $_[1]->plack_request->content);
138             }
139             return $new->(@_);
140             };
141              
142             return;
143             }
144              
145             sub _massage_handler
146             {
147             my $self = shift;
148             my $handler = shift;
149             my ($class, $method);
150             if ( blessed $handler ) {
151             $handler = sub { $handler->handler( @_ ) };
152             } elsif ( my ($class, $method) = $handler =~ m/(.+)->(.+)/ ) {
153             Plack::Util::load_class( $class );
154             $handler = sub { $class->$method( @_ ) };
155             } else {
156             my $class = $handler;
157             Plack::Util::load_class( $class );
158             my $method = eval { $class->can("handler") };
159             if ( grep { $_ eq 'method' } attributes::get($method) ) {
160             $handler = sub { $class->handler( @_ ) };
161             } else {
162             $handler = $method;
163             }
164             }
165             return $handler;
166             }
167              
168             package Plack::App::FakeApache::Tie;
169              
170             sub TIEHANDLE {
171             my $class = shift;
172             my $r = shift;
173             return bless \$r, $class;
174             }
175              
176             sub PRINT { my $r = ${ shift() }; $r->print(@_) }
177             sub WRITE { my $r = ${ shift() }; $r->write(@_) }
178              
179             1;
180              
181             __END__