File Coverage

lib/Mock/Apache.pm
Criterion Covered Total %
statement 104 134 77.6
branch 13 34 38.2
condition 18 38 47.3
subroutine 20 21 95.2
pod 2 5 40.0
total 157 232 67.6


line stmt bran cond sub pod time code
1             # Mock::Apache - a package to mock the mod_perl 1.x environment
2             #
3             # Method descriptions are taken from my book: "Mod_perl Pocket Reference",
4             # Andrew Ford, O'Reilly & Associates, 2001, 0-596-00047-2.
5             # Page references in the comments (marked "MPPR pNN") refer to the book.
6             #
7             # Copyright (C) 2013, Andrew Ford. All rights reserved.
8             # This library is free software; you can redistribute it and/or modify
9             # it under the same terms as Perl itself.
10              
11             package Mock::Apache;
12              
13 5     5   251829 use strict;
  5         18  
  5         182  
14              
15 5     5   5479 use Apache::ConfigParser;
  5         229186  
  5         324  
16 5     5   59 use Carp;
  5         20  
  5         330  
17 5     5   29 use Cwd;
  5         10  
  5         314  
18 5     5   3229 use HTTP::Headers;
  5         29826  
  5         176  
19 5     5   5224 use HTTP::Response;
  5         94155  
  5         187  
20 5     5   5202 use IO::Scalar;
  5         23911  
  5         274  
21 5     5   4983 use Module::Loaded;
  5         3295  
  5         347  
22 5     5   4799 use Readonly;
  5         16164  
  5         296  
23              
24 5     5   4643 use parent 'Class::Accessor';
  5         1594  
  5         28  
25              
26             __PACKAGE__->mk_accessors(qw(server));
27              
28             our $VERSION = "0.09";
29             our $DEBUG;
30              
31             BEGIN {
32 5     5   242 Readonly our @APACHE_CLASSES
33             => qw( Apache Apache::SubRequest Apache::Server Apache::Connection
34             Apache::File Apache::Log Apache::Table Apache::URI Apache::Util
35             Apache::Constants Apache::ModuleConfig Apache::Symbol
36             Apache::Request Apache::Upload Apache::Cookie );
37              
38             # Lie about the Apache::* modules being loaded
39             mark_as_loaded($_)
40 5         430 for @APACHE_CLASSES;
41              
42             # alias the DEBUG() and NYI_DEBUG() functions into each class
43 5     5   22999 no strict 'refs';
  5         13  
  5         549  
44 5         2735 *{"${_}::DEBUG"} = \&DEBUG for @APACHE_CLASSES;
  75         1145  
45 5         86 *{"${_}::NYI_DEBUG"} = \&NYI_DEBUG for @APACHE_CLASSES;
  75         809  
46              
47 5         221 $ENV{MOD_PERL} = 'CGI-Perl/1.1';
48             }
49              
50             # These packages need to come after the Apache::* modules have been
51             # marked as loaded, to avoid the real Apache::* classes being dragged
52             # in.
53 5     5   4254 use Mock::Apache::Emulation;
  5         17  
  5         207  
54 5     5   3315 use Mock::Apache::RemoteClient;
  5         13  
  5         43  
55              
56              
57             Readonly our $DEFAULT_HOSTNAME => 'server.example.com';
58             Readonly our $DEFAULT_ADDR => '22.22.22.22';
59             Readonly our $DEFAULT_ADMIN => 'webmaster';
60              
61             # Default locations (RedHat-inspired)
62              
63             Readonly our $DEFAULT_SERVER_ROOT => '/etc/httpd';
64             Readonly our $DEFAULT_DOCUMENT_ROOT => '/var/www/html';
65              
66             # I am still playing with the API to Mock::Apache.
67             # I envisage having methods to:
68             # * set up the mock server
69             # * run a request through the server
70             # * create an apache request object
71              
72             # Set up a mock Apache server
73              
74             sub setup_server {
75 4     4 1 10490 my ($class, %params) = @_;
76              
77 4         91 my $cfg = Apache::ConfigParser->new;
78              
79 4 100       557 if (my $config_file = $params{config_file}) {
80 1         6 $cfg->parse_file($config_file);
81             }
82              
83 4         967 $DEBUG = delete $params{DEBUG};
84              
85 4   33     42 $params{document_root} ||= _get_config_value($cfg, 'DocumentRoot', $DEFAULT_DOCUMENT_ROOT);
86 4   33     29 $params{server_root} ||= _get_config_value($cfg, 'ServerRoot', $DEFAULT_SERVER_ROOT);
87 4   66     41 $params{server_hostname} ||= $DEFAULT_HOSTNAME;
88 4   50     43 $params{server_port} ||= 80;
89 4   33     33 $params{server_admin} ||= _get_config_value($cfg, 'ServerAdmin',
90             $DEFAULT_ADMIN . '@' . $params{server_hostname});
91 4   50     4705 $params{gid} ||= getgrnam('apache') || 48;
      33        
92 4   50     707 $params{uid} ||= getpwnam('apache') || 48;
      33        
93              
94              
95 4         50 my $self = bless { %params }, $class;
96              
97 4         69 $self->{server} = $Apache::server = Apache::Server->new($self, %params);
98              
99 4         279 return $self;
100             }
101              
102             sub _get_config_value {
103 12     12   149 my ($config, $directive, $default) = @_;
104              
105 12 100 66     129 if ($config and my @dirs = $config->find_down_directive_names($directive)) {
106 1         113 return $dirs[0]->value;
107             }
108 11         891 return $default;
109             }
110              
111             sub mock_client {
112 4     4 0 580 my ($self, %params) = @_;
113              
114 4         48 return Mock::Apache::RemoteClient->new(%params, mock_apache => $self);
115             }
116              
117              
118             # $mock_apache->execute_handler($handler, $request)
119             # $mock_apache->execute_handler($handler, $client, $request)
120              
121             sub execute_handler {
122 4     4 1 540 my ($self, $handler, $client) = (shift, shift, shift);
123              
124 4         10 my $request;
125 4 100 66     81 if (ref $client and $client->isa('Apache')) {
126 3         6 $request = $client;
127 3         21 $client = $client->_mock_client;
128             }
129 4 50 33     115 croak "no mock client specified"
130             unless ref $client and $client->isa('Mock::Apache::RemoteClient');
131              
132 4 50       17 if (!ref $handler) {
133 5     5   3007 no strict 'refs';
  5         16  
  5         4776  
134 0         0 $handler = \&{$handler};
  0         0  
135             }
136              
137 4   66     20 $request ||= $client->new_request(@_);
138              
139 4         10 my $saved_debug = $Mock::Apache::DEBUG;
140 4         17 local $Mock::Apache::DEBUG = 0;
141              
142 4         25 local($ENV{REMOTE_ADDR}) = $request->subprocess_env('REMOTE_ADDR');
143 4         22 local($ENV{REMOTE_HOST}) = $request->subprocess_env('REMOTE_HOST');
144              
145 4         11 local $Apache::request = $request;
146              
147 4         12 my $rc = eval {
148 4         10 local $Mock::Apache::DEBUG = $saved_debug;
149 4         15 local *STDOUT;
150 4         64 tie *STDOUT, 'IO::Scalar', \$request->{_output};
151 4         905 $handler->($request);
152             };
153 4 50       68 if ($@) {
154 0         0 printf STDERR "handler failed: $@\n";
155 0         0 $request->status_line('500 Internal server error');
156             }
157              
158 4         23 my $status = $request->status;
159 4 50       15 if (!$status) {
160 4 50       15 if ($rc == &Apache::Constants::OK) {
    0          
161 4         24 $request->status_line(($status = &Apache::Constants::HTTP_OK) . ' ok');
162             }
163             elsif ($rc == &Apache::Constants::MOVED) {
164 0         0 $request->status_line(($status = &Apache::Constants::HTTP_MOVED_PERMANENTLY) . ' moved permanently');
165             }
166             }
167 4   50     17 (my $message = $request->status_line || '') =~ s/^... //;
168 4         41 my $headers = HTTP::Headers->new;
169 4 50       54 if (!$request->header_out('content-length')) {
170 4         21 $request->header_out('content-length', length($request->_output));
171             }
172 4         17 while (my($field, $value) = each %{$request->headers_out}) {
  9         156  
173 5         174 $headers->push_header($field, $value);
174             }
175 4         82 my $output = $request->_output;
176              
177 4         79 return HTTP::Response->new( $status, $message, $headers, $output );
178             }
179              
180             sub DEBUG {
181 89     89 0 189 my ($message, @args) = @_;
182              
183 89 50       309 return unless $Mock::Apache::DEBUG;
184 0 0         $message .= "\n" unless $message =~ qr{\n$};
185 0           printf STDERR "DEBUG: $message", @args;
186 0 0         if ($DEBUG > 1) {
187 0           my ($package, $file, $line, $subr) = ((caller(1))[0..2], (caller(2))[3]);
188 0 0         if ($file eq __FILE__) {
189 0           ($package, $file, $line, $subr) = ((caller(2))[0..2], (caller(3))[3]);
190             }
191 0           my $dir = getcwd;
192 0           $file =~ s{^$dir/}{};
193 0           print STDERR " from $subr at line $line of $file\n";
194             }
195              
196 0           return;
197             }
198              
199             sub NYI_DEBUG {
200 0     0 0   my ($message, @args) = @_;
201              
202 0 0         $message .= "\n" unless $message =~ qr{\n$};
203 0           printf STDERR "DEBUG: $message", @args;
204              
205 0           my $carp_level = 1;
206 0           my ($package, $file, $line, $subr) = ((caller(1))[0..2], (caller(2))[3]);
207 0 0         if ($file eq __FILE__) {
208 0           $carp_level++;
209 0           ($package, $file, $line, $subr) = ((caller(2))[0..2], (caller(3))[3]);
210             }
211 0 0         if ($DEBUG > 1) {
212 0           my $dir = getcwd;
213 0           $file =~ s{^$dir/}{};
214 0           print STDERR " from $subr at line $line of $file";
215             }
216 0           $DB::single = 1;
217 0           local $Carp::CarpLevel = $carp_level;
218 0           croak((caller(1))[3] . " - NOT YET IMPLEMENTED");
219             }
220              
221             1;
222              
223             __END__