Annotation of embedaddon/lighttpd/tests/LightyTest.pm, revision 1.1

1.1     ! misho       1: #! /usr/bin/perl -w
        !             2: 
        !             3: package LightyTest;
        !             4: use strict;
        !             5: use IO::Socket;
        !             6: use Test::More;
        !             7: use Socket;
        !             8: use Cwd 'abs_path';
        !             9: use POSIX qw(:sys_wait_h dup2);
        !            10: use Errno qw(EADDRINUSE);
        !            11: 
        !            12: sub mtime {
        !            13:        my $file = shift;
        !            14:        my @stat = stat $file;
        !            15:        return @stat ? $stat[9] : 0;
        !            16: }
        !            17: sub new {
        !            18:        my $class = shift;
        !            19:        my $self = {};
        !            20:        my $lpath;
        !            21: 
        !            22:        $self->{CONFIGFILE} = 'lighttpd.conf';
        !            23: 
        !            24:        $lpath = (defined $ENV{'top_builddir'} ? $ENV{'top_builddir'} : '..');
        !            25:        $self->{BASEDIR} = abs_path($lpath);
        !            26: 
        !            27:        $lpath = (defined $ENV{'top_builddir'} ? $ENV{'top_builddir'}."/tests/" : '.');
        !            28:        $self->{TESTDIR} = abs_path($lpath);
        !            29: 
        !            30:        $lpath = (defined $ENV{'srcdir'} ? $ENV{'srcdir'} : '.');
        !            31:        $self->{SRCDIR} = abs_path($lpath);
        !            32: 
        !            33: 
        !            34:        if (mtime($self->{BASEDIR}.'/src/lighttpd') > mtime($self->{BASEDIR}.'/build/lighttpd')) {
        !            35:                $self->{BINDIR} = $self->{BASEDIR}.'/src';
        !            36:                if (mtime($self->{BASEDIR}.'/src/.libs')) {
        !            37:                        $self->{MODULES_PATH} = $self->{BASEDIR}.'/src/.libs';
        !            38:                } else {
        !            39:                        $self->{MODULES_PATH} = $self->{BASEDIR}.'/src';
        !            40:                }
        !            41:        } else {
        !            42:                $self->{BINDIR} = $self->{BASEDIR}.'/build';
        !            43:                $self->{MODULES_PATH} = $self->{BASEDIR}.'/build';
        !            44:        }
        !            45:        $self->{LIGHTTPD_PATH} = $self->{BINDIR}.'/lighttpd';
        !            46:        $self->{PORT} = 2048;
        !            47: 
        !            48:        my ($name, $aliases, $addrtype, $net) = gethostbyaddr(inet_aton("127.0.0.1"), AF_INET);
        !            49: 
        !            50:        $self->{HOSTNAME} = $name;
        !            51: 
        !            52:        bless($self, $class);
        !            53: 
        !            54:        return $self;
        !            55: }
        !            56: 
        !            57: sub listening_on {
        !            58:        my $self = shift;
        !            59:        my $port = shift;
        !            60: 
        !            61:        my $remote = 
        !            62:          IO::Socket::INET->new(Proto    => "tcp",
        !            63:                                PeerAddr => "127.0.0.1",
        !            64:                                PeerPort => $port) or return 0;
        !            65: 
        !            66:        close $remote;
        !            67: 
        !            68:        return 1;
        !            69: }
        !            70: 
        !            71: sub stop_proc {
        !            72:        my $self = shift;
        !            73: 
        !            74:        my $pid = $self->{LIGHTTPD_PID};
        !            75:        if (defined $pid && $pid != -1) {
        !            76:                kill('TERM', $pid) or return -1;
        !            77:                return -1 if ($pid != waitpid($pid, 0));
        !            78:        } else {
        !            79:                diag("\nProcess not started, nothing to stop");
        !            80:                return -1;
        !            81:        }
        !            82: 
        !            83:        return 0;
        !            84: }
        !            85: 
        !            86: sub wait_for_port_with_proc {
        !            87:        my $self = shift;
        !            88:        my $port = shift;
        !            89:        my $child = shift;
        !            90:        my $timeout = 5*10; # 5 secs, select waits 0.1 s
        !            91: 
        !            92:        while (0 == $self->listening_on($port)) {
        !            93:                select(undef, undef, undef, 0.1);
        !            94:                $timeout--;
        !            95: 
        !            96:                # the process is gone, we failed
        !            97:                if (0 != waitpid($child, WNOHANG)) {
        !            98:                        return -1;
        !            99:                }
        !           100:                if (0 >= $timeout) {
        !           101:                        diag("\nTimeout while trying to connect; killing child");
        !           102:                        kill('TERM', $child);
        !           103:                        return -1;
        !           104:                }
        !           105:        }
        !           106: 
        !           107:        return 0;
        !           108: }
        !           109: 
        !           110: sub start_proc {
        !           111:        my $self = shift;
        !           112:        # kill old proc if necessary
        !           113:        #$self->stop_proc;
        !           114: 
        !           115:        if ($self->listening_on($self->{PORT})) {
        !           116:                diag("\nPort ".$self->{PORT}." already in use");
        !           117:                return -1;
        !           118:        }
        !           119: 
        !           120:        # pre-process configfile if necessary
        !           121:        #
        !           122: 
        !           123:        $ENV{'SRCDIR'} = $self->{BASEDIR}.'/tests';
        !           124:        $ENV{'PORT'} = $self->{PORT};
        !           125: 
        !           126:        my @cmdline = ($self->{LIGHTTPD_PATH}, "-D", "-f", $self->{SRCDIR}."/".$self->{CONFIGFILE}, "-m", $self->{MODULES_PATH});
        !           127:        if (defined $ENV{"TRACEME"} && $ENV{"TRACEME"} eq 'strace') {
        !           128:                @cmdline = (qw(strace -tt -s 512 -o strace), @cmdline);
        !           129:        } elsif (defined $ENV{"TRACEME"} && $ENV{"TRACEME"} eq 'truss') {
        !           130:                @cmdline = (qw(truss -a -l -w all -v all -o strace), @cmdline);
        !           131:        } elsif (defined $ENV{"TRACEME"} && $ENV{"TRACEME"} eq 'gdb') {
        !           132:                @cmdline = ('gdb', '--batch', '--ex', 'run', '--ex', 'bt full', '--args', @cmdline);
        !           133:        } elsif (defined $ENV{"TRACEME"} && $ENV{"TRACEME"} eq 'valgrind') {
        !           134:                @cmdline = (qw(valgrind --tool=memcheck --show-reachable=yes --leak-check=yes --log-file=valgrind), @cmdline);
        !           135:        }
        !           136:        # diag("\nstarting lighttpd at :".$self->{PORT}.", cmdline: ".@cmdline );
        !           137:        my $child = fork();
        !           138:        if (not defined $child) {
        !           139:                diag("\nFork failed");
        !           140:                return -1;
        !           141:        }
        !           142:        if ($child == 0) {
        !           143:                exec @cmdline or die($?);
        !           144:        }
        !           145: 
        !           146:        if (0 != $self->wait_for_port_with_proc($self->{PORT}, $child)) {
        !           147:                diag(sprintf('\nThe process %i is not up', $child));
        !           148:                return -1;
        !           149:        }
        !           150: 
        !           151:        $self->{LIGHTTPD_PID} = $child;
        !           152: 
        !           153:        0;
        !           154: }
        !           155: 
        !           156: sub handle_http {
        !           157:        my $self = shift;
        !           158:        my $t = shift;
        !           159:        my $EOL = "\015\012";
        !           160:        my $BLANK = $EOL x 2;
        !           161:        my $host = "127.0.0.1";
        !           162: 
        !           163:        my @request = $t->{REQUEST};
        !           164:        my @response = $t->{RESPONSE};
        !           165:        my $slow = defined $t->{SLOWREQUEST};
        !           166:        my $is_debug = $ENV{"TRACE_HTTP"};
        !           167: 
        !           168:        my $remote = 
        !           169:          IO::Socket::INET->new(Proto    => "tcp",
        !           170:                                PeerAddr => $host,
        !           171:                                PeerPort => $self->{PORT});
        !           172: 
        !           173:        if (not defined $remote) {
        !           174:                diag("\nconnect failed: $!");
        !           175:                return -1;
        !           176:        }
        !           177: 
        !           178:        $remote->autoflush(1);
        !           179: 
        !           180:        if (!$slow) {
        !           181:                diag("\nsending request header to ".$host.":".$self->{PORT}) if $is_debug;
        !           182:                foreach(@request) {
        !           183:                        # pipeline requests
        !           184:                        s/\r//g;
        !           185:                        s/\n/$EOL/g;
        !           186: 
        !           187:                        print $remote $_.$BLANK;
        !           188:                        diag("\n<< ".$_) if $is_debug;
        !           189:                }
        !           190:                shutdown($remote, 1); # I've stopped writing data
        !           191:        } else {
        !           192:                diag("\nsending request header to ".$host.":".$self->{PORT}) if $is_debug;
        !           193:                foreach(@request) {
        !           194:                        # pipeline requests
        !           195:                        chomp;
        !           196:                        s/\r//g;
        !           197:                        s/\n/$EOL/g;
        !           198: 
        !           199:                        print $remote $_;
        !           200:                        diag("<< ".$_."\n") if $is_debug;
        !           201:                        select(undef, undef, undef, 0.1);
        !           202:                        print $remote "\015";
        !           203:                        select(undef, undef, undef, 0.1);
        !           204:                        print $remote "\012";
        !           205:                        select(undef, undef, undef, 0.1);
        !           206:                        print $remote "\015";
        !           207:                        select(undef, undef, undef, 0.1);
        !           208:                        print $remote "\012";
        !           209:                        select(undef, undef, undef, 0.1);
        !           210:                }
        !           211:        
        !           212:        }
        !           213:        diag("\n... done") if $is_debug;
        !           214: 
        !           215:        my $lines = "";
        !           216: 
        !           217:        diag("\nreceiving response") if $is_debug;
        !           218:        # read everything
        !           219:        while(<$remote>) {
        !           220:                $lines .= $_;
        !           221:                diag(">> ".$_) if $is_debug;
        !           222:        }
        !           223:        diag("\n... done") if $is_debug;
        !           224:        
        !           225:        close $remote;
        !           226: 
        !           227:        my $full_response = $lines;
        !           228: 
        !           229:        my $href;
        !           230:        foreach $href ( @{ $t->{RESPONSE} }) {
        !           231:                # first line is always response header
        !           232:                my %resp_hdr;
        !           233:                my $resp_body;
        !           234:                my $resp_line;
        !           235:                my $conditions = $_;
        !           236: 
        !           237:                for (my $ln = 0; defined $lines; $ln++) {
        !           238:                        (my $line, $lines) = split($EOL, $lines, 2);
        !           239: 
        !           240:                        # header finished
        !           241:                        last if(!defined $line or length($line) == 0);
        !           242: 
        !           243:                        if ($ln == 0) {
        !           244:                                # response header
        !           245:                                $resp_line = $line;
        !           246:                        } else {
        !           247:                                # response vars
        !           248: 
        !           249:                                if ($line =~ /^([^:]+):\s*(.+)$/) {
        !           250:                                        (my $h = $1) =~ tr/[A-Z]/[a-z]/;
        !           251: 
        !           252:                                        if (defined $resp_hdr{$h}) {
        !           253: #                                              diag(sprintf("\nheader '%s' is duplicated: '%s' and '%s'\n",
        !           254: #                                                           $h, $resp_hdr{$h}, $2));
        !           255:                                                $resp_hdr{$h} .= ', '.$2;
        !           256:                                        } else {
        !           257:                                                $resp_hdr{$h} = $2;
        !           258:                                        }
        !           259:                                } else {
        !           260:                                        diag(sprintf("\nunexpected line '%s'", $line));
        !           261:                                        return -1;
        !           262:                                }
        !           263:                        }
        !           264:                }
        !           265: 
        !           266:                if (not defined($resp_line)) {
        !           267:                        diag(sprintf("\nempty response"));
        !           268:                        return -1;
        !           269:                }
        !           270: 
        !           271:                $t->{etag} = $resp_hdr{'etag'};
        !           272:                $t->{date} = $resp_hdr{'date'};
        !           273: 
        !           274:                # check length
        !           275:                if (defined $resp_hdr{"content-length"}) {
        !           276:                        $resp_body = substr($lines, 0, $resp_hdr{"content-length"});
        !           277:                        if (length($lines) < $resp_hdr{"content-length"}) {
        !           278:                                $lines = "";
        !           279:                        } else {
        !           280:                                $lines = substr($lines, $resp_hdr{"content-length"});
        !           281:                        }
        !           282:                        undef $lines if (length($lines) == 0);
        !           283:                } else {
        !           284:                        $resp_body = $lines;
        !           285:                        undef $lines;
        !           286:                }
        !           287: 
        !           288:                # check conditions
        !           289:                if ($resp_line =~ /^(HTTP\/1\.[01]) ([0-9]{3}) .+$/) {
        !           290:                        if ($href->{'HTTP-Protocol'} ne $1) {
        !           291:                                diag(sprintf("\nproto failed: expected '%s', got '%s'", $href->{'HTTP-Protocol'}, $1));
        !           292:                                return -1;
        !           293:                        }
        !           294:                        if ($href->{'HTTP-Status'} ne $2) {
        !           295:                                diag(sprintf("\nstatus failed: expected '%s', got '%s'", $href->{'HTTP-Status'}, $2));
        !           296:                                return -1;
        !           297:                        }
        !           298:                } else {
        !           299:                        diag(sprintf("\nunexpected resp_line '%s'", $resp_line));
        !           300:                        return -1;
        !           301:                }
        !           302: 
        !           303:                if (defined $href->{'HTTP-Content'}) {
        !           304:                        $resp_body = "" unless defined $resp_body;
        !           305:                        if ($href->{'HTTP-Content'} ne $resp_body) {
        !           306:                                diag(sprintf("\nbody failed: expected '%s', got '%s'", $href->{'HTTP-Content'}, $resp_body));
        !           307:                                return -1;
        !           308:                        }
        !           309:                }
        !           310:                
        !           311:                if (defined $href->{'-HTTP-Content'}) {
        !           312:                        if (defined $resp_body && $resp_body ne '') {
        !           313:                                diag(sprintf("\nbody failed: expected empty body, got '%s'", $resp_body));
        !           314:                                return -1;
        !           315:                        }
        !           316:                }
        !           317: 
        !           318:                foreach (keys %{ $href }) {
        !           319:                        next if $_ eq 'HTTP-Protocol';
        !           320:                        next if $_ eq 'HTTP-Status';
        !           321:                        next if $_ eq 'HTTP-Content';
        !           322:                        next if $_ eq '-HTTP-Content';
        !           323: 
        !           324:                        (my $k = $_) =~ tr/[A-Z]/[a-z]/;
        !           325: 
        !           326:                        my $verify_value = 1;
        !           327:                        my $key_inverted = 0;
        !           328: 
        !           329:                        if (substr($k, 0, 1) eq '+') {
        !           330:                                $k = substr($k, 1);
        !           331:                                $verify_value = 0;
        !           332:                        } elsif (substr($k, 0, 1) eq '-') {
        !           333:                                ## the key should NOT exist
        !           334:                                $k = substr($k, 1);
        !           335:                                $key_inverted = 1;
        !           336:                                $verify_value = 0; ## skip the value check
        !           337:                         }
        !           338: 
        !           339:                        if ($key_inverted) {
        !           340:                                if (defined $resp_hdr{$k}) {
        !           341:                                        diag(sprintf("\nheader '%s' MUST not be set", $k));
        !           342:                                        return -1;
        !           343:                                }
        !           344:                        } else {
        !           345:                                if (not defined $resp_hdr{$k}) {
        !           346:                                        diag(sprintf("\nrequired header '%s' is missing", $k));
        !           347:                                        return -1;
        !           348:                                }
        !           349:                        }
        !           350: 
        !           351:                        if ($verify_value) {
        !           352:                                if ($href->{$_} =~ /^\/(.+)\/$/) {
        !           353:                                        if ($resp_hdr{$k} !~ /$1/) {
        !           354:                                                diag(sprintf("\nresponse-header failed: expected '%s', got '%s', regex: %s", 
        !           355:                                                     $href->{$_}, $resp_hdr{$k}, $1));
        !           356:                                                return -1;
        !           357:                                        }
        !           358:                                } elsif ($href->{$_} ne $resp_hdr{$k}) {
        !           359:                                        diag(sprintf("\nresponse-header failed: expected '%s', got '%s'",
        !           360:                                             $href->{$_}, $resp_hdr{$k}));
        !           361:                                        return -1;
        !           362:                                }
        !           363:                        }
        !           364:                }
        !           365:        }
        !           366: 
        !           367:        # we should have sucked up everything
        !           368:        if (defined $lines) {
        !           369:                diag(sprintf("\nunexpected lines '%s'", $lines));
        !           370:                return -1;
        !           371:        }
        !           372: 
        !           373:        return 0;
        !           374: }
        !           375: 
        !           376: sub spawnfcgi {
        !           377:        my ($self, $binary, $port) = @_;
        !           378:        my $child = fork();
        !           379:        if (not defined $child) {
        !           380:                diag("\nCouldn't fork");
        !           381:                return -1;
        !           382:        }
        !           383:        if ($child == 0) {
        !           384:                my $iaddr   = inet_aton('localhost') || die "no host: localhost";
        !           385:                my $proto   = getprotobyname('tcp');
        !           386:                socket(SOCK, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
        !           387:                setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) || die "setsockopt: $!";
        !           388:                bind(SOCK, sockaddr_in($port, $iaddr)) || die "bind: $!";
        !           389:                listen(SOCK, 1024) || die "listen: $!";
        !           390:                dup2(fileno(SOCK), 0) || die "dup2: $!";
        !           391:                exec { $binary } ($binary) or die($?);
        !           392:        } else {
        !           393:                if (0 != $self->wait_for_port_with_proc($port, $child)) {
        !           394:                        diag(sprintf("\nThe process %i is not up (port %i, %s)", $child, $port, $binary));
        !           395:                        return -1;
        !           396:                }
        !           397:                return $child;
        !           398:        }
        !           399: }
        !           400: 
        !           401: sub endspawnfcgi {
        !           402:        my ($self, $pid) = @_;
        !           403:        return -1 if (-1 == $pid);
        !           404:        kill(2, $pid);
        !           405:        waitpid($pid, 0);
        !           406:        return 0;
        !           407: }
        !           408: 
        !           409: 1;

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>