| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- #!/usr/bin/env perl
- use 5.040;
- use warnings FATAL => 'all';
- use re '/aa';
- use Test2::V0;
- use Test2::Plugin::NoWarnings;
- use Test::MockModule;
- use Time::HiRes ();
- use FindBin::libs;
- ok( require TPSGI::Server, 'TPSGI::Server loads' ) or bail_out("Can't find SUT");
- sub server {
- my (%server) = @_;
- return bless( { server => \%server }, 'TPSGI::Server' );
- }
- subtest 'child_init_hook' => sub {
- my $starman = Test::MockModule->new('Starman::Server');
- my $built = 0;
- $starman->redefine( child_init_hook => sub { $built++ } );
- local $SIG{HUP} = sub { fail('the handler Net::Server installed ran') };
- my $self = server( connected => 0 );
- $self->child_init_hook();
- is( $built, 1, "Starman's hook still runs, so the app is built" );
- $SIG{HUP}->();
- is( $self->{server}{SigHUPed}, 1, 'a HUP marks the worker HUPed, which done() retires it for' );
- is( $self->{server}{_HUP}, 1, 'and makes accept() return instead of retrying' );
- };
- subtest 'accept' => sub {
- my $net = Test::MockModule->new('Net::Server::PreForkSimple');
- # What Net::Server::accept() does when the HUP lands as it takes a
- # connection: it has the client, and returns 0 because _HUP is set.
- $net->redefine( accept => sub { $_[0]{server}{client} = 'a connection'; return 0 } );
- is( server()->accept(), 1, 'a connection accepted as the HUP came is answered' );
- # And when the HUP interrupts the wait, with nothing taken.
- $net->redefine( accept => sub { return 0 } );
- is( server( client => 'the last request' )->accept(), 0, 'a worker that took nothing retires, whatever its last request left behind' );
- $net->redefine( accept => sub { $_[0]{server}{client} = 'a connection'; return 1 } );
- is( server()->accept(), 1, 'an ordinary accept is passed through' );
- };
- subtest 'every connection is answered while the parent is HUPed' => sub {
- skip_all('RELEASE_TESTING not set') unless $ENV{RELEASE_TESTING};
- require File::Temp;
- require Mojo::File;
- require IO::Socket::UNIX;
- require Plack::Handler::TPSGI;
- my $dir = File::Temp::tempdir( CLEANUP => 1 );
- my $sock = "$dir/s.sock";
- my $server = fork() // die "fork: $!";
- if ( !$server ) {
- close(STDERR) or die "Cannot close STDERR: $!";
- Plack::Handler::TPSGI->new( listen => [$sock], workers => 5, pid => "$dir/pid", keepalive => 0 )->run( sub { [ 200, [ 'Content-Type' => 'text/plain' ], ['ok'] ] } );
- exit 0;
- }
- for ( 1 .. 100 ) { last if -S $sock && -s "$dir/pid"; Time::HiRes::sleep(0.1) }
- ok( -S $sock, 'the server is listening' ) or return;
- my $parent = Mojo::File->new("$dir/pid")->slurp;
- chomp $parent;
- # Four clients, each counting the requests that got no answer.
- my @clients = map {
- my $pid = fork() // die "fork: $!";
- if ( !$pid ) {
- my $bad = 0;
- for ( 1 .. 1500 ) {
- my $c = IO::Socket::UNIX->new( Peer => $sock ) or do { $bad++; next };
- print {$c} "GET / HTTP/1.0\r\n\r\n";
- my $got = do { local $/; <$c> }
- // '';
- $bad++ unless $got =~ m{^HTTP/1[.]\d[ ]200}xs;
- }
- exit( $bad > 255 ? 255 : $bad );
- }
- $pid
- } 1 .. 4;
- for ( 1 .. 60 ) { kill( 'HUP', $parent ); Time::HiRes::sleep(0.05) }
- my $unanswered = 0;
- foreach my $pid (@clients) { waitpid( $pid, 0 ); $unanswered += $? >> 8 }
- is( $unanswered, 0, 'no request of 6000 went unanswered across 60 restarts' );
- kill( 'TERM', $parent );
- waitpid( $server, 0 );
- };
- done_testing();
|