perl-modperl mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Peter Valdemar Mørch <pe...@morch.com>
Subject User abort/stop, modperl 2 and TCP FIN / RST?
Date Sun, 10 Jun 2012 18:23:55 GMT
Hi,

I'm trying to find out how to detect user hitting 'stop' aka 'abort'
in modperl 2. I found documentation on how it works in modperl 1 (
http://perl.apache.org/docs/1.0/guide/debug.html#Detecting_Aborted_Connections
- short version: $r->print returns success and $r->connection->aborted
tells whether user hit abort ).

However, I've tested the situation to be quite different in modperl 2:

When the user hits 'stop', the *second* $r->rflush() generates an exception
> Apache2::RequestIO::rflush: (103) Software caused connection
as long as ~ 100ms has passed between the two rflush-es.

Here is my understanding of what happens:

When the user hits 'stop' in the browser, the browser sends a TCP
packet with the FIN flag set. Apache2/modperl doesn't react to that
and $r->connection->aborted still returns false. A subsequent
$r->print and $r->rflush works fine. Apache now sends whatever was
printed to the browser. The browser now sends RST ("Hey, I really want
to kill this connection!") to Apache and after that, $r->print still
succeeds (returns true), but $r->rflush dies (because now the client
has closed the socket hard). "Second" $r->rflush really means "the
first $r->rflush after Apache received the client's 'RST' ", so if I
issue many $r->print("foo"); $r->rflush() in quick succession they all
pass. Around 100ms needs to pass between $r->rflush-es for the second
$r->rflush to fail. If tested this with Firefox and Chromium.

So: After the user hits 'stop', the second $r->rflush (requiring a
delay) generates an exception that can be used to determine that the
user has hit 'stop'.

Have I understood this correctly? Is there any way I can get Apache to
react to the first TCP FIN, and have $r->connection->aborted return
true at that point? Or otherwise detect reliably when the user has hit
'stop' in modperl 2 without having to wait an additional
server-client-server roundtrip time?

Using standard Debian squeeze packages:
> dpkg-query -W | grep apache2
apache2	2.2.16-6+squeeze7
apache2-mpm-worker	2.2.16-6+squeeze7
apache2-utils	2.2.16-6+squeeze7
apache2.2-bin	2.2.16-6+squeeze7
apache2.2-common	2.2.16-6+squeeze7
libapache2-mod-perl2	2.0.4-7
libapache2-reload-perl	0.10-2

Apache config:
<Directory /var/www/foo>
	Options +ExecCGI
        AddDefaultCharset On
	<Files flush.cgi>
		SetHandler perl-script
		PerlResponseHandler ModPerl::Registry
		PerlOptions +ParseHeaders
	</Files>
</Directory>

flush.cgi:
#!/usr/bin/perl -w
use strict;

use Apache2::ServerUtil;
use Apache2::RequestUtil;
use Apache2::RequestRec;
use Apache2::Connection;

use Time::HiRes qw(sleep);

sub doCGI {
    my $r = Apache2::RequestUtil->request;
    my $c = $r->connection();
    $r->print("Content-type: text/html\n\n");
    $r->print("Starting up $$<br>\n");
    LOOP_I: foreach my $i (0..3) {

        # User hits STOP in browser here for some $i

        foreach my $j (0..1) {
            my $printRetval = $r->print("$i / $j<br>\n");
            $r->log_error(sprintf "%d / %d pre-flush  : %s print %s",
                          $i, $j,
                          ($c->aborted ? "aborted" : "not aborted"),
                          ( $printRetval ? 'succeeded' : 'failed')
                         );

            # This always dies when $j == 1. Why 1 and not 0?
            # This is the first place we notice when the user has hit STOP in
            # the browser.
            eval {
                $r->rflush;
            };

            my $err = $@;
            $r->log_error(sprintf "%d / %d post-flush : %s flush %s",
                          $i, $j,
                          ( $c->aborted ? "aborted" : "not aborted" ),
                          ( $err ? 'failed' : 'succeeded')
                         );
            if ($err) {
                $r->log_error("exiting loops");
                last LOOP_I;
            }

            # Why sleep 0.1? 0.01 is mostly enough, but 0.001 is always too
            # little. What determines this number?
            sleep 0.1;
        }
        sleep 5;
    }
};
doCGI();

A wireshark trace of the traffic between browser and server can be
found here: http://ge.tt/6XGcPvI/v/0?c

Thanks for reading this far.

Peter
-- 
Peter Valdemar Mørch
http://www.morch.com

Mime
View raw message