PHP doesn't detect connection abort at all
Asked Answered
R

1

5

I have read and deeply understood these: http://www.php.net/manual/en/features.connection-handling.php http://www.php.net/manual/en/function.register-shutdown-function.php

However, I have tested both PHP 5.1.6 and 5.3 and things DON'T work as described there. What I observe is:

  • connection_status() always return true, even after the client has closed the connection.
  • execution of the script keeps going on after the client has closed the connection, even though ignore_user_abort is 0
  • a function registered with register_shutdown_function() is not run until the script reaches ends. The script is NOT interrupted (and hence the function not called) when the client aborts the connection.

So basically PHP just doesn't detect the client's disconnection AT ALL.

Note that this is NOT as if ignore_user_abort was set to 1: if that was the case then connection_status() would return 1 even though the script would keep running and the shutdown function would not be called until the end. That is not the case.

ini_get("ignore_user_abort") returns 0, as expected.

Is this a bug in PHP, or may this be due to some Apache setting?

How do I get PHP to work as described in the abovementioned documentation?

Test script:

<?php

function myShutdown() {
    error_log("myShutdown ".connection_status()." ".ini_get("ignore_user_abort"));
}

register_shutdown_function(myShutdown);

echo "Hi!";
error_log(" *** test/test *** ");
for ($i=0; $i<10; $i++) {
    sleep(1);
    error_log(".");
    echo ".";
}
?>

Steps to reproduce: - visit the url of the script - abort the connection on the client before 10 seconds have elapsed (e.g. hit the stop button in the browser)

Expected/Desired behavior: The logs should show less than 10 dots, and at the end "myShutdown 1 0" (if you watch the log in real time, the myShutDown should appear immediately when the client disconnects)

Observed/current behavior: The logs show always exactly 10 dots, and at the end "myShutdown 0 0" (if you watch it in realtime, it goes on for 10 seconds no matter when the client disconnects).

Rectal answered 13/4, 2013 at 18:5 Comment(5)
Why are you using a version of PHP no longer supported ?Flesh
Are you running php as an apache module?Shinny
@Shinny yes, as an apache moduleRectal
@Flesh I'm seeing the same in both 5.3, which definitely is supported, and older 5.1. Why I don't upgrade 5.1, basically because I'm on CentOS 5, which I can't upgrade to CentOS6, and there php 5.1 can't be upgraded to 5.3/4, but cannot be installed alongside either (a completely f***ed up design decision in RHEL packaging which CentOS inherits) so I'm stuck with that version.Rectal
@Flesh sorry I notice you commented before I edited: I later tested on PHP 5.3 too and it shows the exact same behavior.Rectal
S
5

First, I also failed to get it to work, using the basic ubuntu 12.04 LAMP installation (php5.3). But I've some information and hope that it is helpful. Any comments or edits appreciated! :)


I see two problems with your code. The first is a syntax error. You are missing the single quotes around myShutdown when calling register_shutdown_function(). Change the line to:

register_shutdown_function('myShutdown');

The second problem I see is the missing flush() call after echos. The documentation says:

PHP will not detect that the user has aborted the connection until an attempt is made to send information to the client. Simply using an echo statement does not guarantee that information is sent, see flush().

But even flush() will not help in any case. From the documentation of flush():

flush() may not be able to override the buffering scheme of your web server and it has no effect on any client-side buffering in the browser. It also doesn't affect PHP's userspace output buffering mechanism. This means you will have to call both ob_flush() and flush() to flush the ob output buffers if you are using those.

Several servers, especially on Win32, will still buffer the output from your script until it terminates before transmitting the results to the browser.

Server modules for Apache like mod_gzip may do buffering of their own that will cause flush() to not result in data being sent immediately to the client.

Even the browser may buffer its input before displaying it. Netscape, for example, buffers text until it receives an end-of-line or the beginning of a tag, and it won't render tables until the tag of the outermost table is seen.

Some versions of Microsoft Internet Explorer will only start to display the page after they have received 256 bytes of output, so you may need to send extra whitespace before flushing to get those browsers to display the page.

In the comments of the that page there is an advice to set several headers and apache configs:

apache_setenv('no-gzip', 1); 
ini_set('zlib.output_compression', 0); 
ini_set('implicit_flush', 1); 

however, even this didn't work for me. I've investigated this using wiresharek, although the web server sends content ('Hi') after 0.0037 seconds, the web browser was buffering the page.

Shinny answered 13/4, 2013 at 20:37 Comment(9)
You should remove the part about the missing single quotes. rehister_shutdown_function() accepts a callable, and a function is a callable. Otherwise I would get an error btw, which is not the caseRectal
Note that you'll get: Notice: Use of undefined constant myShutdown - assumed 'myShutdown' in .... You should increase your error_reporting levelShinny
I see! output buffering must be the problem. I guess apache buffers it even if gzipping is turned off and regardless of PHP's implicit flush, that's probably why even your attempted workaround doesn't work.Rectal
Either the web servers buffer or the browser. I'm not sure at the moment. Will have to use wireshark for this. But note, that I'll be AFK for a while (likely tomorrow)Shinny
I've tested it, and in my case it is the browser which is buffering the output. The webserver sends content ('Hi') after 0.0037 secondsShinny
Browser buffering can't interfere in the test, if you watch the error_logRectal
This worked for me!!! in .htaccess: SetEnv no-gzip dont-vary - and even without the implicit_flush provided that the blocks of output are big enough (e.g. many "....." instead of one "." in the example above)Rectal
cool! you made id work! I wasn't really sure if there is a chance.. Currently coming home from a party. Will test tomorrow! :)Shinny
It's worth noting that if ignore_user_abort=true, and you cause a flush from within a shutdown function, that function will immediate stop running and PHP will move on to the next shutdown function. See: #6381770Adherence

© 2022 - 2024 — McMap. All rights reserved.