Date: | Fri, 06 Aug 2004 02:31:22 -0400 |
From: | Tom Metro <tmetro [...] cpan.org> |
To: | paul [...] seamons.com, bbb [...] cpan.org, bug-Net-Server [...] rt.cpan.org |
Subject: | Net::Server t\Server_Fork fails on Win32 |
On my Win32 system (Windows NT 4.0sp6a, perl, v5.8.0,
MSWin32-x86-multi-thread, ActiveState build 806) the test suite for
Net::Server hangs on test 5 in Server_Fork. Looking at CPAN testers, it
appears others have had problems on Win32 as well:
http://testers.cpan.org/show/Net-Server.html#Net-Server-0.87
though the reported failures are different. I assume, given that earlier
versions have passed tests on Win32, that the code is expected to work
on Win32.
With a few clarifications, I might be able to determine why the test
suite is failing on Win32.
Quoting from t\Server_Fork:
Show quoted text
> ### find some open ports
[...]
Show quoted text> my $start_port = 20200;
> my $num_ports = 1;
> my @ports = ();
> for my $i (0..99){
> my $sock = IO::Socket::INET->new(PeerAddr => 'localhost',
> PeerPort => ($start_port + $i),
> Timeout => 2,
> Proto => 'tcp');
> push @ports, ($start_port + $i) if ! defined $sock;
> last if $num_ports == @ports;
> }
This might work better if you tested to see if the port is free by
seeing if you can bind to it, rather than connect to it. Otherwise the
port you find might be a port that is blocked by a firewall (which will
fail later anyway) or just a slow or non-responsive server (in which
case the bind will later fail). In my testing the code below seemed to
also run faster.
Secondly, $sock is never explicitly freed (and the port closed), though
of course it goes out of scope once the 'for' block is exited.
### find some open ports
my $start_port = 20200;
my $port_range = 99;
my $num_ports_needed = 1;
my @ports = ();
for my $port ($start_port .. $start_port + $port_range) {
my $sock = IO::Socket::INET->new(LocalPort => $port,
Listen => 1,
Timeout => 2,
Proto => 'tcp');
push(@ports, $port) if defined $sock;
$sock = undef;
last if $num_ports_needed == @ports;
}
Show quoted text> if( $num_ports_needed == @ports ){
> print "ok 4\n";
> }else{
> print "not ok 4\n";
> }
Might as well do:
[...]
}else{
print "not ok 4\n";
print "not ok 5\n";
exit;
}
Show quoted text> eval {
> alarm $alarm;
alarm() doesn't work on Win32. You can use the timeout attribute for the
sockets and I'd assume select() on the pipe I/O.
Show quoted text> ### parent does the client
> if( $pid ){
>
> <READ>; ### wait until the child writes to us
>
> ### connect to child
> my $remote = IO::Socket::INET->new(PeerAddr => 'localhost',
> PeerPort => $ports[0],
> Proto => 'tcp');
[...]
Show quoted text> my $line = <$remote>;
> die unless $line =~ /Net::Server/;
> print $remote "exit\n";
>
> ### child does the server
> }else{
> close STDERR;
> Net::Server::Test->run(port => $ports[0],
> setsid => 1,
> );
> exit;
> }
So the flow here is...
fork->
child parent
Net::Server::Test->run() wait for text over the pipe
run() calls accept()
accept() writes to pipe open socket to server
grabs a line
does it match /Net::Server/
send "exit"
Where's the code that implements the behavior on the server side that
sends out "Net::Server" on connect and understands an "exit" command? I
don't see a process_request() in the Net::Server::Test subclass. Is this
behavior part of a default implementation?
That aside, it appears that setsid => 1 is the culprit. It's side effect
of closing off STDERR made it take longer to get to the bottom of
things, but commenting out that attribute seems to allow the 5th test to
pass, although the parent process still hangs (it outputs "Server
closing!", but run() doesn't return; it appears that accept() gets
called perpetually every minute or so).
Here's the output from a hacked up version of t\Server_Fork:
% perl -I../blib/lib Server_Fork.t
1..5
ok 1
ok 2
ok 3
Port: 20200
ok 4
PARENT: wait for pipe
CHILD: run server on port: 20200
2004/08/06-02:08:43 Net::Server::Test (type Net::Server::Fork) starting!
pid(-568)
Binding to TCP port 20200 on host *
Group Not Defined. Defaulting to EGID '0'
User Not Defined. Defaulting to EUID '0'
CHILD: sending "ready!"
PARENT: connect to child
PARENT: read from child
CHILD: sending "ready!"
ok 5
2004/08/06-02:08:45 Server closing!
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
CHILD: sending "ready!"
Terminating on signal SIGINT(2)
On a side note, the run() method doesn't seem to be formally documented,
even though it is frequently referenced in the man page. Likewise for
process_request().
-Tom