Randy Kobes wrote:
On Sun, 31 Jul 2005, Randy Kobes wrote:On Sun, 31 Jul 2005, Randy Kobes wrote:[ ... ]Here's a scaled-down version of the problem - I usedcommands with single letters, as my Win32 console sent a \r\n after each letter.[ ... ]sub handler { my $c = shift; $| = 1; my $socket = $c->client_socket; $socket->opt_set(APR::Const::SO_NONBLOCK, 0); $socket->send("Welcome to " . __PACKAGE__ . "\r\nAvailable commands: @cmds\r\n"); while (1) { my $cmd; next unless $cmd = getline($socket);[ ... ] I found that if I change that last line to last unless $cmd = getline($socket); then one can interrupt the telnet session with 'CTRL ]' and close the connection without the Apache process consuming 100% cpu.
OK, I wrote a test case that reproduces the problem. If you run: perl Makefile.PL make test things work, but if you do: t/TEST -start t/TEST -runthe process starts spinning in the getline() call, as $sock->recv doesn't fail. This is our "bug", well it was supposed to be a feature as the internals are going as:
rc = apr_socket_recv(socket, SvPVX(buffer), &len);
if (!(rc == APR_SUCCESS || rc == APR_EOF)) {
modperl_croak(aTHX_ rc, "APR::Socket::recv");
}
So if recv has returned EOF, the call was always successful. So basically
we eat the EOF event and user tries to read again and again.
I think as long as we are in the blocking mode that approach is fine, i.e.: - if $sock->recv was successful: * if you received some string, you are good * if you received nothing, that means you've got EOF - otherwise handle the errorand that getline code doesn't seem to do the right thing anyway, since it may return an error code but the caller expects a string.
Here is a rewrite that doesn't spin. Notice that I've dropped the $c-aborted check, I don't know if it's needed, since recv() should have caught that anyway. But please restore it if needed.
package MyTest::Protocol;
use strict;
use warnings FATAL => 'all';
use Apache2::Connection ();
use APR::Socket ();
use APR::Status ();
use Apache2::Const -compile => qw(OK DONE DECLINED);
use APR::Const -compile => qw(SO_NONBLOCK);
my @cmds = qw(d q);
my %commands = map { $_, \&{$_} } @cmds;
sub handler {
my $c = shift;
$| = 1;
my $socket = $c->client_socket;
$socket->opt_set(APR::Const::SO_NONBLOCK, 0);
$socket->send("Welcome to " . __PACKAGE__ .
"\r\nAvailable commands: @cmds\r\n");
while (1) {
my $cmd;
eval {
$cmd = getline($socket);
};
if ($@) {
return Apache2::Const::DONE if APR::Status::is_ECONNABORTED($@);
}
last unless defined $cmd; # EOF
next unless length $cmd; # new line with no commands
warn "READ: $cmd\n";
if (my $sub = $commands{$cmd}) {
last unless $sub->($socket) == Apache2::Const::OK;
} else {
$socket->send("Commands: @cmds\r\n");
}
}
return Apache2::Const::OK;
}
# returns either of:
# - undef on EOF
# - CRLF stripped line on normal read
#
# may throw an exception (via recv())
sub getline {
my $socket = shift;
$socket->recv(my $line, 1024);
return undef unless length $line;
$line =~ s/[\r\n]*$//;
return $line;
}
sub d {
my $socket = shift;
$socket->send(scalar(localtime) . "\r\n");
return Apache2::Const::OK;
}
sub q { Apache2::Const::DONE }
1;
__END__
<NoAutoConfig>
<VirtualHost MyTest::Protocol>
PerlProcessConnectionHandler MyTest::Protocol
<Location MyTest__Protocol>
Order Deny,Allow
Allow from all
</Location>
</VirtualHost>
</NoAutoConfig>
--
__________________________________________________________________
Stas Bekman JAm_pH ------> Just Another mod_perl Hacker
http://stason.org/ mod_perl Guide ---> http://perl.apache.org
mailto:[EMAIL PROTECTED] http://use.perl.org http://apacheweek.com
http://modperlbook.org http://apache.org http://ticketmaster.com
bug-reporting-skeleton-mp2.tar.gz
Description: GNU Zip compressed data
