#26494 [NoF->Opn]: mod_php4 fails Timeout setting while writing to a dead client on Apache 1.3

From: Date: Wed, 22 Dec 2004 22:59:10 +0000
Subject: #26494 [NoF->Opn]: mod_php4 fails Timeout setting while writing to a dead client on Apache 1.3
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-70937@lists.php.net to get a copy of this message
ID: 26494 User updated by: hexer at studentcenter dot org Reported By: hexer at studentcenter dot org -Status: No Feedback +Status: Open Bug Type: Apache related Operating System: Linux PHP Version: 4.3.4 New Comment: Hi, For the impatient, I have included a patch to solve this issue below. We can't use the default installation of php on our site because the dead client connections can hang up to hours and god up the precious httpd slots, as well as keep database connections open in php pages that close them only at the end of execution. Read on for the explanation of what the patch does and why. Apparently mod_php does not correctly follow the apache's TimeOut specification. From apache docs: The TimeOut directive currently defines the amount of time Apache will wait for three things: 1. The total amount of time it takes to receive a GET request. 2. The amount of time between receipt of TCP packets on a POST or PUT request. 3 .The amount of time between ACKs on transmissions of TCP packets in responses. The apache API provides a handful of functions that deal with tracking timeout they are: ap_hard_timeout(), ap_soft_timeout(), ap_reset_timeout() and ap_kill_timeout() The ap_hard_timeout() and soft_timeout starts or restarts countdown until the apache gets an ALRM letting it know that the TimeOut has been reached. ap_kill_timeout() will unset the timer and disable the timeout trigger from being received. There are two types of timeouts, hard and soft. Soft timeouts simply close the socket and let the module do the cleanup work while hard timeouts force things to exit less cleanly. Using hard timeouts in mod_php will break the php's ignore_user_abort option in certain scenarious (such as dead client) so we have to use soft timeouts. In the default php installation, mod_php calls hard_timeout() before the main entry point into the php engine. This implementation does not follow the apache's spec. Theoretically this would force a script to exit after TimeOut value has expired regardless of it's max_execution_time. But in practice this does not happen because TimeOut signal framework is broken somewhere along the php execution. What we need is a change to the implementation so that apache only gets set to timeout between sends to the client but not during other php executions. The way it is done in this patch is by setting the timer before the sends, then unsetting the timer at the end of sends so that the server does not get a timeout during the portions of the script execution where no output is sent to the client. A good way to test compliance to the TimeOut, ignore_user_abort and max_execution_time is to make a test script that prints something out, sleeps for > TimeOut seconds, prints something else, etc. If the module is compliant it should: 1. detect dead connection and close it 2. ignore TimeOut during execution of non output-related instructions 3. exit after max_execution_time 4. continue execution after connection is closed if ignore_user_abort option is set. The following patch fixes TimeOut and makes mod_php compliant with apache's description. It has been tested on a server with average traffic of 64 requests/sec. We've been using this patch for a few months without any noticable performance impact. Tested with php 4.3.10 compiled-in with apache 1.3.33 Our conf string for php: ./configure --with-mysql \ --with-xml \ --with-apache=../apache_1.3.33 \ --enable-track-vars \ --with-gd \ --enable-discard-path \ --enable-bcmath \ --enable-gd-native-tt \ --with-freetype-dir=/usr/local \ --with-png-dir=/usr/local \ --with-jpeg-dir=/usr/local \ --with-zlib=/usr/local \ --enable-apc Conf string for apache: ./configure \ --activate-module=src/modules/php4/libphp4.a \ --enable-module=php4 \ --enable-module=rewrite \ --enable-module=so \ --prefix=/usr/local/apache --- mod_php4.c 2004-07-21 12:25:28.000000000 -0400 +++ /usr/local/src/php-4.3.10/sapi/apache/mod_php4.c 2004-12-22 16:57:43.000000000 -0500 @@ -99,7 +99,9 @@ int ret=0; if (SG(server_context)) { - ret = rwrite(str, str_length, (request_rec *) SG(server_context)); + ap_soft_timeout("php send body", (request_rec *) SG(server_context)); + ret = rwrite(str, str_length, (request_rec *) SG(server_context)); + ap_kill_timeout((request_rec *) SG(server_context)); } if (ret != str_length) { php_handle_aborted_connection(); @@ -142,9 +144,9 @@ handler = signal(SIGPIPE, SIG_IGN); while (total_read_bytes<count_bytes) { - hard_timeout("Read POST information", r); /* start timeout timer */ + ap_hard_timeout("Read POST information", r); /* start timeout timer */ read_bytes = get_client_block(r, buffer+total_read_bytes, count_bytes-total_read_bytes); - reset_timeout(r); + ap_kill_timeout(r); if (read_bytes<=0) { break; } @@ -608,8 +610,6 @@ scripts may change this later in the request. */ r->content_type = php_apache_get_default_mimetype(r TSRMLS_CC); - /* Init timeout */ - hard_timeout("send", r); SG(server_context) = r; @@ -622,7 +622,6 @@ /* Done, restore umask, turn off timeout, close file and return */ php_restore_umask(); - kill_timeout(r); } zend_end_try(); return OK; Previous Comments: ------------------------------------------------------------------------ [2004-12-10 17:34:55] dimitrij at schlund dot de Hi, we have same problem too. It is very critical if you use LAMP. This kills any MySQL Server if the php script has a connection to this server and stocks. Dimitrij ------------------------------------------------------------------------ [2003-12-07 12:00:08] sniper@php.net No feedback was provided. The bug is being suspended because we assume that you are no longer experiencing the problem. If this is not the case and you are able to provide the information that was requested earlier, please do so and change the status of the bug back to "Open". Thank you. ------------------------------------------------------------------------ [2003-12-02 21:52:43] iliaa@php.net I am having some difficulty replacating the problem and have not been able to get it to happen YET. Meanwhile could you please see if the problem occurs with PHP is compiled with mysql support? ------------------------------------------------------------------------ [2003-12-02 15:06:49] hexer at studentcenter dot org it seems that somewhere along the line, SIG_ALRM handler gets rerouted or disabled in one of the php extensions (I'm highly suspecting mysql) since apache's timeout relies on SIG_ALRM anything within php that disables it will break the timeout mechanism. The stuck processes abort normally when calling the handler directly from GDB, however when receiving SIG_ALRM nothing happens. the following is the config string for php ./configure --with-mysql \ --with-xml \ --with-apache=../apache_1.3.29 \ --enable-track-vars \ --with-gd \ --enable-discard-path \ --enable-bcmath \ --enable-gd-native-tt \ --with-freetype-dir=/usr/local \ --with-png-dir=/usr/local \ --with-jpeg-dir=/usr/local \ --with-zlib=/usr/local \ --enable-apc from all the included extensions mysql seems to be the only one with references to SIG_ALRM. I will continue looking but currently the only solution that worked for me is setting the timeout on send's. please let me know if you were able to reproduce this. ------------------------------------------------------------------------ [2003-12-02 13:22:07] iliaa@php.net The timeouts are already being set around the entire PHP processing block look at mod_php4.c line 611 and lower. You suggestion would force a timeout setting around every single write, making things extremely slow, current code already should handle the situation you describe. ------------------------------------------------------------------------ The remainder of the comments for this report are too long. To view the rest of the comments, please view the bug report online at http://bugs.php.net/26494 -- Edit this bug report at http://bugs.php.net/?id=26494&edit=1

« previous php.bugs (#70937) next »