Putting 4.2.0 on top of 4.0.17
[usit-rt.git] / lib / RT / Test / Apache.pm
CommitLineData
84fb5b46
MKG
1# BEGIN BPS TAGGED BLOCK {{{
2#
3# COPYRIGHT:
4#
403d7b0b 5# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
84fb5b46
MKG
6# <sales@bestpractical.com>
7#
8# (Except where explicitly superseded by other copyright notices)
9#
10#
11# LICENSE:
12#
13# This work is made available to you under the terms of Version 2 of
14# the GNU General Public License. A copy of that license should have
15# been provided with this software, but in any event can be snarfed
16# from www.gnu.org.
17#
18# This work is distributed in the hope that it will be useful, but
19# WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21# General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License
24# along with this program; if not, write to the Free Software
25# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26# 02110-1301 or visit their web page on the internet at
27# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
28#
29#
30# CONTRIBUTION SUBMISSION POLICY:
31#
32# (The following paragraph is not intended to limit the rights granted
33# to you to modify and distribute this software under the terms of
34# the GNU General Public License and is only of importance to you if
35# you choose to contribute your changes and enhancements to the
36# community by submitting them to Best Practical Solutions, LLC.)
37#
38# By intentionally submitting any modifications, corrections or
39# derivatives to this work, or any other work intended for use with
40# Request Tracker, to Best Practical Solutions, LLC, you confirm that
41# you are the copyright holder for those contributions and you grant
42# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43# royalty-free, perpetual, license to use, copy, create derivative
44# works based on those contributions, and sublicense and distribute
45# those contributions and any derivatives thereof.
46#
47# END BPS TAGGED BLOCK }}}
48
49package RT::Test::Apache;
50use strict;
51use warnings;
52
53my %MODULES = (
54 '2.2' => {
55 "mod_perl" => [qw(authz_host env alias perl)],
56 "fastcgi" => [qw(authz_host env alias mime fastcgi)],
57 },
58);
59
60my $apache_module_prefix = $ENV{RT_TEST_APACHE_MODULES};
61my $apxs =
62 $ENV{RT_TEST_APXS}
63 || RT::Test->find_executable('apxs')
64 || RT::Test->find_executable('apxs2');
65
66if ($apxs and not $apache_module_prefix) {
67 $apache_module_prefix = `$apxs -q LIBEXECDIR`;
68 chomp $apache_module_prefix;
69}
70
71$apache_module_prefix ||= 'modules';
72
73sub basic_auth {
74 my $self = shift;
75 my $passwd = File::Spec->rel2abs( File::Spec->catfile(
76 't', 'data', 'configs', 'passwords' ) );
77
78 return <<"EOT";
79 AuthType Basic
80 AuthName "restricted area"
81 AuthUserFile $passwd
82 Require user root
83EOT
84}
85
af59614d
MKG
86sub basic_auth_anon {
87 my $self = shift;
88
89 return <<"EOT";
90 AuthType Basic
91 AuthName "restricted area"
92 AuthBasicProvider anon
93
94 Anonymous *
95 Anonymous_NoUserID On
96 Anonymous_MustGiveEmail Off
97 Anonymous_VerifyEmail Off
98
99 Require valid-user
100EOT
101}
102
84fb5b46
MKG
103sub start_server {
104 my ($self, %config) = @_;
105 my %tmp = %{$config{tmp}};
106 my %info = $self->apache_server_info( %config );
107
108 RT::Test::diag(do {
109 open( my $fh, '<', $tmp{'config'}{'RT'} ) or die $!;
110 local $/;
111 <$fh>
112 });
113
114 my $tmpl = File::Spec->rel2abs( File::Spec->catfile(
115 't', 'data', 'configs',
116 'apache'. $info{'version'} .'+'. $config{variant} .'.conf'
117 ) );
118 my %opt = (
119 listen => $config{port},
120 server_root => $info{'HTTPD_ROOT'} || $ENV{'HTTPD_ROOT'}
121 || Test::More::BAIL_OUT("Couldn't figure out server root"),
122 document_root => $RT::MasonComponentRoot,
123 tmp_dir => "$tmp{'directory'}",
124 rt_bin_path => $RT::BinPath,
125 rt_sbin_path => $RT::SbinPath,
126 rt_site_config => $ENV{'RT_SITE_CONFIG'},
127 load_modules => $info{load_modules},
84fb5b46 128 );
af59614d
MKG
129 if (not $config{basic_auth}) {
130 $opt{basic_auth} = "";
131 } elsif ($config{basic_auth} eq 'anon') {
132 $opt{basic_auth} = $self->basic_auth_anon;
133 } else {
134 $opt{basic_auth} = $self->basic_auth;
135 }
84fb5b46
MKG
136 foreach (qw(log pid lock)) {
137 $opt{$_ .'_file'} = File::Spec->catfile(
138 "$tmp{'directory'}", "apache.$_"
139 );
140 }
141
142 $tmp{'config'}{'apache'} = File::Spec->catfile(
143 "$tmp{'directory'}", "apache.conf"
144 );
145 $self->process_in_file(
146 in => $tmpl,
147 out => $tmp{'config'}{'apache'},
148 options => \%opt,
149 );
150
151 $self->fork_exec($info{'executable'}, '-f', $tmp{'config'}{'apache'});
152 my $pid = do {
153 my $tries = 15;
154 while ( !-s $opt{'pid_file'} ) {
155 $tries--;
156 last unless $tries;
157 sleep 1;
158 }
159 my $pid_fh;
160 unless (-e $opt{'pid_file'} and open($pid_fh, '<', $opt{'pid_file'})) {
161 Test::More::BAIL_OUT("Couldn't start apache server, no pid file (unknown error)")
162 unless -e $opt{log_file};
163
164 open my $log, "<", $opt{log_file};
165 my $error = do {local $/; <$log>};
166 close $log;
167 $RT::Logger->error($error) if $error;
168 Test::More::BAIL_OUT("Couldn't start apache server!");
169 }
170
171 my $pid = <$pid_fh>;
172 chomp $pid;
173 $pid;
174 };
175
176 Test::More::ok($pid, "Started apache server #$pid");
177 return $pid;
178}
179
180sub apache_server_info {
181 my $self = shift;
182 my %res = @_;
183
184 my $bin = $res{'executable'} = $ENV{'RT_TEST_APACHE'}
185 || $self->find_apache_server
186 || Test::More::BAIL_OUT("Couldn't find apache server, use RT_TEST_APACHE");
187
188 Test::More::BAIL_OUT(
189 "Couldn't find apache modules directory (set APXS= or RT_TEST_APACHE_MODULES=)"
190 ) unless -d $apache_module_prefix;
191
192
193 RT::Test::diag("Using '$bin' apache executable for testing");
194
195 my $info = `$bin -V`;
196 ($res{'version'}) = ($info =~ m{Server\s+version:\s+Apache/(\d+\.\d+)\.});
197 Test::More::BAIL_OUT(
198 "Couldn't figure out version of the server"
199 ) unless $res{'version'};
200
201 my %opts = ($info =~ m/^\s*-D\s+([A-Z_]+?)(?:="(.*)")$/mg);
202 %res = (%res, %opts);
203
204 $res{'modules'} = [
205 map {s/^\s+//; s/\s+$//; $_}
206 grep $_ !~ /Compiled in modules/i,
207 split /\r*\n/, `$bin -l`
208 ];
209
210 Test::More::BAIL_OUT(
211 "Unsupported apache version $res{version}"
212 ) unless exists $MODULES{$res{version}};
213
214 Test::More::BAIL_OUT(
215 "Unsupported apache variant $res{variant}"
216 ) unless exists $MODULES{$res{version}}{$res{variant}};
217
218 my @mlist = @{$MODULES{$res{version}}{$res{variant}}};
af59614d
MKG
219 if ($res{basic_auth}) {
220 push @mlist, "auth_basic", "authz_user";
221 push @mlist, $res{basic_auth} eq 'anon' ? "authn_anon" : "authn_file";
222 }
84fb5b46
MKG
223
224 $res{'load_modules'} = '';
225 foreach my $mod ( @mlist ) {
226 next if grep $_ =~ /^(mod_|)$mod\.c$/, @{ $res{'modules'} };
227
228 my $so_file = $apache_module_prefix."/mod_".$mod.".so";
229 Test::More::BAIL_OUT( "Couldn't load $mod module (expected in $so_file)" )
230 unless -f $so_file;
231 $res{'load_modules'} .=
232 "LoadModule ${mod}_module $so_file\n";
233 }
234 return %res;
235}
236
237sub find_apache_server {
238 my $self = shift;
239 return $_ foreach grep defined,
240 map RT::Test->find_executable($_),
241 qw(httpd apache apache2 apache1);
242 return undef;
243}
244
245sub apache_mpm_type {
246 my $self = shift;
247 my $apache = $self->find_apache_server;
248 my $out = `$apache -l`;
249 if ( $out =~ /^\s*(worker|prefork|event|itk)\.c\s*$/m ) {
250 return $1;
251 }
252}
253
254sub fork_exec {
255 my $self = shift;
256
257 RT::Test::__disconnect_rt();
258 my $pid = fork;
259 unless ( defined $pid ) {
260 die "cannot fork: $!";
261 } elsif ( !$pid ) {
262 exec @_;
263 die "can't exec `". join(' ', @_) ."` program: $!";
264 } else {
265 RT::Test::__reconnect_rt();
266 return $pid;
267 }
268}
269
270sub process_in_file {
271 my $self = shift;
272 my %args = ( in => undef, options => undef, @_ );
273
274 my $text = RT::Test->file_content( $args{'in'} );
275 while ( my ($opt) = ($text =~ /\%\%(.+?)\%\%/) ) {
276 my $value = $args{'options'}{ lc $opt };
277 die "no value for $opt" unless defined $value;
278
279 $text =~ s/\%\%\Q$opt\E\%\%/$value/g;
280 }
281
282 my ($out_fh, $out_conf);
283 unless ( $args{'out'} ) {
284 ($out_fh, $out_conf) = tempfile();
285 } else {
286 $out_conf = $args{'out'};
287 open( $out_fh, '>', $out_conf )
288 or die "couldn't open '$out_conf': $!";
289 }
290 print $out_fh $text;
291 seek $out_fh, 0, 0;
292
293 return ($out_fh, $out_conf);
294}
295
2961;