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