8 use POSIX
qw(:sys_wait_h dup2);
9 use Errno
qw(EADDRINUSE);
12 my @DEFAULT_PATHS = ('/usr/bin/', '/usr/local/bin/');
13 my ($envname, $program) = @_;
16 if (defined $ENV{$envname}) {
17 $location = $ENV{$envname};
19 $location = `which "$program" 2>/dev/null`;
22 for my $path (@DEFAULT_PATHS) {
23 $location = $path . $program;
30 $ENV{$envname} = $location;
33 delete $ENV{$envname};
39 our $HAVE_PHP = find_program
('PHP', 'php-cgi');
40 our $HAVE_PERL = find_program
('PERL', 'perl');
42 die "Couldn't find path to perl, but it obviously seems to be running";
48 my @stat = stat $file;
49 return @stat ?
$stat[9] : 0;
57 $self->{CONFIGFILE
} = 'lighttpd.conf';
59 $lpath = (defined $ENV{'top_builddir'} ?
$ENV{'top_builddir'} : '..');
60 $self->{BASEDIR
} = abs_path
($lpath);
62 $lpath = (defined $ENV{'top_builddir'} ?
$ENV{'top_builddir'}."/tests/" : '.');
63 $self->{TESTDIR
} = abs_path
($lpath);
65 $lpath = (defined $ENV{'srcdir'} ?
$ENV{'srcdir'} : '.');
66 $self->{SRCDIR
} = abs_path
($lpath);
69 if (mtime
($self->{BASEDIR
}.'/src/lighttpd') > mtime
($self->{BASEDIR
}.'/build/lighttpd')) {
70 $self->{BINDIR
} = $self->{BASEDIR
}.'/src';
71 if (mtime
($self->{BASEDIR
}.'/src/.libs')) {
72 $self->{MODULES_PATH
} = $self->{BASEDIR
}.'/src/.libs';
74 $self->{MODULES_PATH
} = $self->{BASEDIR
}.'/src';
77 $self->{BINDIR
} = $self->{BASEDIR
}.'/build';
78 $self->{MODULES_PATH
} = $self->{BASEDIR
}.'/build';
80 $self->{LIGHTTPD_PATH
} = $self->{BINDIR
}.'/lighttpd';
83 my ($name, $aliases, $addrtype, $net) = gethostbyaddr(inet_aton
("127.0.0.1"), AF_INET
);
85 $self->{HOSTNAME
} = $name;
96 my $remote = IO
::Socket
::INET
->new(
98 PeerAddr
=> "127.0.0.1",
99 PeerPort
=> $port) or return 0;
109 my $pid = $self->{LIGHTTPD_PID
};
110 if (defined $pid && $pid != -1) {
111 kill('TERM', $pid) or return -1;
112 return -1 if ($pid != waitpid($pid, 0));
114 diag
("\nProcess not started, nothing to stop");
121 sub wait_for_port_with_proc
{
125 my $timeout = 10*10; # 10 secs (valgrind might take a while), select waits 0.1 s
127 while (0 == $self->listening_on($port)) {
128 select(undef, undef, undef, 0.1);
131 # the process is gone, we failed
132 if (0 != waitpid($child, WNOHANG
)) {
136 diag
("\nTimeout while trying to connect; killing child");
137 kill('TERM', $child);
147 # kill old proc if necessary
150 if ($self->listening_on($self->{PORT
})) {
151 diag
("\nPort ".$self->{PORT
}." already in use");
155 # pre-process configfile if necessary
158 $ENV{'SRCDIR'} = $self->{BASEDIR
}.'/tests';
159 $ENV{'PORT'} = $self->{PORT
};
161 my @cmdline = ($self->{LIGHTTPD_PATH
}, "-D", "-f", $self->{SRCDIR
}."/".$self->{CONFIGFILE
}, "-m", $self->{MODULES_PATH
});
162 if (defined $ENV{"TRACEME"} && $ENV{"TRACEME"} eq 'strace') {
163 @cmdline = (qw(strace -tt -s 4096 -o strace -f -v), @cmdline);
164 } elsif (defined $ENV{"TRACEME"} && $ENV{"TRACEME"} eq 'truss') {
165 @cmdline = (qw(truss -a -l -w all -v all -o strace), @cmdline);
166 } elsif (defined $ENV{"TRACEME"} && $ENV{"TRACEME"} eq 'gdb') {
167 @cmdline = ('gdb', '--batch', '--ex', 'run', '--ex', 'bt full', '--args', @cmdline);
168 } elsif (defined $ENV{"TRACEME"} && $ENV{"TRACEME"} eq 'valgrind') {
169 @cmdline = (qw(valgrind --tool=memcheck --track-origins=yes --show-reachable=yes --leak-check=yes --log-file=valgrind.%p), @cmdline);
171 # diag("\nstarting lighttpd at :".$self->{PORT}.", cmdline: ".@cmdline );
173 if (not defined $child) {
174 diag("\nFork failed");
178 exec @cmdline or die($?);
181 if (0 != $self->wait_for_port_with_proc($self->{PORT}, $child)) {
182 diag(sprintf('\nThe process %i is not up', $child));
186 $self->{LIGHTTPD_PID} = $child;
194 my $EOL = "\015\012";
195 my $BLANK = $EOL x 2;
196 my $host = "127.0.0.1";
198 my @request = $t->{REQUEST};
199 my @response = $t->{RESPONSE};
200 my $slow = defined $t->{SLOWREQUEST};
201 my $is_debug = $ENV{"TRACE_HTTP"};
204 IO::Socket::INET->new(
207 PeerPort => $self->{PORT});
209 if (not defined $remote) {
210 diag("\nconnect failed: $!");
214 $remote->autoflush(1);
217 diag("\nsending request header to ".$host.":".$self->{PORT}) if $is_debug;
223 print $remote $_.$BLANK;
224 diag("\n<< ".$_) if $is_debug;
227 diag("\nsending request header to ".$host.":".$self->{PORT}) if $is_debug;
235 diag("<< ".$_."\n") if $is_debug;
236 select(undef, undef, undef, 0.1);
237 print $remote "\015";
238 select(undef, undef, undef, 0.1);
239 print $remote "\012";
240 select(undef, undef, undef, 0.1);
241 print $remote "\015";
242 select(undef, undef, undef, 0.1);
243 print $remote "\012";
244 select(undef, undef, undef, 0.1);
248 diag("\n... done") if $is_debug;
252 diag("\nreceiving response") if $is_debug;
256 diag(">> ".$_) if $is_debug;
258 diag("\n... done") if $is_debug;
262 my $full_response = $lines;
265 foreach $href ( @{ $t->{RESPONSE} }) {
266 # first line is always response header
272 for (my $ln = 0; defined $lines; $ln++) {
273 (my $line, $lines) = split($EOL, $lines, 2);
276 last if(!defined $line or length($line) == 0);
284 if ($line =~ /^([^:]+):\s*(.+)$/) {
285 (my $h = $1) =~ tr/[A-Z]/[a-z]/;
287 if (defined $resp_hdr{$h}) {
288 # diag(sprintf("\nheader '%s' is duplicated: '%s' and '%s'\n",
289 # $h, $resp_hdr{$h}, $2));
290 $resp_hdr{$h} .= ', '.$2;
295 diag(sprintf("\nunexpected line '%s'", $line));
301 if (not defined($resp_line)) {
302 diag(sprintf("\nempty response"));
306 $t->{etag} = $resp_hdr{'etag'};
307 $t->{date} = $resp_hdr{'date'};
310 if (defined $resp_hdr{"content-length"}) {
311 $resp_body = substr($lines, 0, $resp_hdr{"content-length"});
312 if (length($lines) < $resp_hdr{"content-length"}) {
315 $lines = substr($lines, $resp_hdr{"content-length"});
317 undef $lines if (length($lines) == 0);
324 if ($resp_line =~ /^(HTTP\/1\.[01]) ([0-9]{3}) .+$/) {
325 if ($href->{'HTTP-Protocol'} ne $1) {
326 diag(sprintf("\nproto failed: expected '%s', got '%s'", $href->{'HTTP-Protocol'}, $1));
329 if ($href->{'HTTP-Status'} ne $2) {
330 diag(sprintf("\nstatus failed: expected '%s', got '%s'", $href->{'HTTP-Status'}, $2));
334 diag(sprintf("\nunexpected resp_line '%s'", $resp_line));
338 if (defined $href->{'HTTP-Content'}) {
339 $resp_body = "" unless defined $resp_body;
340 if ($href->{'HTTP-Content'} ne $resp_body) {
341 diag(sprintf("\nbody failed: expected '%s', got '%s'", $href->{'HTTP-Content'}, $resp_body));
346 if (defined $href->{'-HTTP-Content'}) {
347 if (defined $resp_body && $resp_body ne '') {
348 diag(sprintf("\nbody failed: expected empty body, got '%s'", $resp_body));
353 foreach (keys %{ $href }) {
354 next if $_ eq 'HTTP-Protocol';
355 next if $_ eq 'HTTP-Status';
356 next if $_ eq 'HTTP-Content';
357 next if $_ eq '-HTTP-Content';
359 (my $k = $_) =~ tr/[A-Z]/[a-z]/;
361 my $verify_value = 1;
362 my $key_inverted = 0;
364 if (substr($k, 0, 1) eq '+') {
367 } elsif (substr($k, 0, 1) eq '-') {
368 ## the key should NOT exist
371 $verify_value = 0; ## skip the value check
375 if (defined $resp_hdr{$k}) {
376 diag(sprintf("\nheader '%s' MUST not be set", $k));
380 if (not defined $resp_hdr{$k}) {
381 diag(sprintf("\nrequired header '%s' is missing", $k));
387 if ($href->{$_} =~ /^\/(.+)\/$/) {
388 if ($resp_hdr{$k} !~ /$1/) {
390 "\nresponse-header failed: expected '%s', got '%s', regex: %s",
391 $href->{$_}, $resp_hdr{$k}, $1));
394 } elsif ($href->{$_} ne $resp_hdr{$k}) {
396 "\nresponse-header failed: expected '%s', got '%s'",
397 $href->{$_}, $resp_hdr{$k}));
404 # we should have sucked up everything
405 if (defined $lines) {
406 diag(sprintf("\nunexpected lines '%s'", $lines));
414 my ($self, $binary, $port) = @_;
416 if (not defined $child) {
417 diag("\nCouldn't fork");
421 my $iaddr = inet_aton('localhost') || die "no host: localhost";
422 my $proto = getprotobyname('tcp');
423 socket(SOCK, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
424 setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) || die "setsockopt: $!";
425 bind(SOCK, sockaddr_in($port, $iaddr)) || die "bind: $!";
426 listen(SOCK, 1024) || die "listen: $!";
427 dup2(fileno(SOCK), 0) || die "dup2: $!";
428 exec { $binary } ($binary) or die($?);
430 if (0 != $self->wait_for_port_with_proc($port, $child)) {
431 diag(sprintf("\nThe process %i is not up (port %i, %s)", $child, $port, $binary));
439 my ($self, $pid) = @_;
440 return -1 if (-1 == $pid);