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