in reply to Double Click of Death on Perl Web Server

Thank you atcroft, haj and anon for the useful clues. Sorry I forgot there is an error message, with diagnostics enabled, that's not ambiguous and agrees with your suggestions:

Can't close(IO::Socket::INET=GLOB(0x7fbc9ab6e300)) filehandle: 'Broken pipe'

I tried a few things that didn't help (like $SIG{PIPE} = 'IGNORE') but this eval on close does the trick:

while (my $client = $server->accept) {
  ...
}
eval { close $client } # autodie in use

The Perl Cookbook says to use "shutdown" (0,1,2) instead of "close" but I get this:

Can't shutdown('IO::Socket::INET=GLOB(0x7fd3857a5e40)', '0'): Socket is not connected

What is the correct way to handle this condition (besides eval hack)?

Thanks again

  • Comment on Re: Double Click of Death on Perl Web Server

Replies are listed 'Best First'.
Re^2: Double Click of Death on Perl Web Server
by syphilis (Archbishop) on Sep 30, 2018 at 14:04 UTC
    # autodie in use

    Why ?
    It seems to me that death on failure is not something you want here.

    Cheers,
    Rob
      > # autodie in use
      > Why ?
      > It seems to me that death on failure is not something you want here.

      THIS WAS THE PROBLEM! I have some autodie homework to do, but just putting "no autodie" in a block with "close $client" stops double-clicks killing the server.

      Thank you Rob!

        print doesn't always write to the underlying file handle immediately. What you print often ends up in a buffer that gets written to the underlying file handle on a later print, or when the handle is closed. This is an optimization called "buffering".

        What's happening here is that the actual printing is being delayed until you call close, but the browser has already closed the socket by that time (to abort the first request before making the second request). Your code is therefore attempting to write to a closed socket, which normally results in a SIGPIPE signal killing your process, but you must be on Windows or have that signal ignored, because it results in close failing instead. autodie converts this into an exception.

        All of that is perfectly fine! The problem is in your handling of that error, or lack thereof. Your server code should handle all exceptions that occur while handling a request. As such, your code should look like this:

        use autodie; my $server = IO::Socket::INET->new(...); while (1) { my $client = $server->accept() or die("Can't accept connections: $!\n"); if (!eval { ... close($client); return 1; # No exception }) { warn("Error servicing request: $@"); } }
Re^2: Double Click of Death on Perl Web Server
by tybalt89 (Monsignor) on Sep 30, 2018 at 14:26 UTC

    If you are actually doing this "while" condition

    while (my $client = $server->accept) {

    and the process gets a signal (say SIGCHLD), the accept will return false, with error EINTR (interrupted system call) and your "while" loop will exit.

    I've seen this bug in several other's programs and it can cause the intermittent failure you are seeing.

    Try something like

    while(1) { if( my $client = $server->accept) { #accept code...
Re^2: Double Click of Death on Perl Web Server
by haj (Vicar) on Sep 30, 2018 at 13:59 UTC
    Glad that you've solved it, but your example has another issue:
    while (my $client = $server->accept) { ... } eval { close $client } # autodie in use

    This eval doesn't do any good. Outside of the while loop the lexical variable $client isn't declared, so you are trying to close an undefined value, then throw away the error. Both use warnings; and use strict; should complain about that. I guess you want to disconnect within the loop?

      Apologies for the typo:
      
      my $server = IO::Socket::INET->new(...);
      
      while (my $client = $server->accept) {
        while (<$client>) {
          ...
        }
        ...
        close $client;               # server dies
        eval { close $client }       # does not die
        shutdown $client, 2;         # dies
        eval { shutdown $client, 2 } # does not die
      }
      close $server;
      
        Fork it:
        my $server = IO::Socket::INET->new(...); while (my $client = $server->accept) { if (my $pid = fork()) { # parent waits for another connection close $client } elsif (defined $pid) { # child handles the request while (<$client>) { ... } ... close $client; close $server; exit; } } close $server;

        2018-10-06 Athanasius changed pre tags to code tags