Re: On register_shutdown_function: What might be the problem?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I tested the link (http://lerdorf.com/red.php) using browser (firefox 1.0), it worked as expected, the page of redirect2.html displayed within 2 seconds. I put the exact code to my testing envirionment (2 places), as this one: http://liang.ns2user.info/php/red.php , and the page shows up in about 13 seconds using the SAME browser.

Observation 1.
============
Same client(web browser on same mechine), same code on different runing environment make difference.

I use perl code (to get page from http://lerdorf.com/red.php)
----------------------------------------------------------
#!/usr/bin/perl -w

use strict;
use LWP::Simple;
use LWP::UserAgent;

# get input from command line
my ($url) = @ARGV;

# set default URL if none is given
if ( !$url ){
   $url = 'http://liang.ns2user.info/liang/y.php';
   print STDOUT qq{Using $url as url to fetch\n};
}
# get the html page from the web, into a string

my $ua = LWP::UserAgent->new(keep_alive=>1, timeout=>180);
my $req = HTTP::Request->new( GET => $url );
my $res = $ua->request( $req );

if(!$res->is_success){
 my $error = $res->message();
print STDOUT qq{ERRO: with pid of $$ has ended for the following error: $error};
 exit;
}

my $htmlPage = $res->content;
print $htmlPage."\n";
exit;


----------------------------------------------------------
It takes about 13 seconds to get:
~~~~~~~~~~~~~~~~~~~~~~~~~~
marvin:~/liang.ns2user.info/perl> ./http_req.pl http://lerdorf.com/red.php
You are now on redirect2.html
~~~~~~~~~~~~~~~~~~~~~~~~~~

Observation 2.
=============
Different clients (browser and perl LWP::UserAgent) request same code on the same mechane result differently.

Question:
========
Could it be because of the configuration of linux/apache/php on the server side instead of client side make the difference?

(2 of my testing environments are red hat linux with apache 2.0 and one is php5.04 another is 4.3.10.)?

The php configuration is: http://liang.ns2user.info/php/info.php. I have no read permission of those httpd.conf files so do not know how apache configured.

Any hint? Thank you.

Liang



That's a client-side issue then, because it is certainly sent.  Trying
your exact script:


<?php
ignore_user_abort(true);
header("Location: redirect2.html");
echo "foo\n"; flush();
for($i=0;$i<10;$i++) { echo $i; sleep(1); }
$fp = fopen("/tmp/foo.txt","a");
fputs($fp,$i);
fclose($fp);
?>

It's at http://lerdorf.com/red.php if you want to test it yourself.

3:53pm colo:/var/www/lerdorf.com> telnet localhost 80
Trying 127.0.0.1...
Connected to colo.
Escape character is '^]'.
GET /red.php HTTP/1.0

HTTP/1.1 302 Found
Date: Tue, 26 Jul 2005 22:53:40 GMT
Server: Apache/1.3.33 (Debian GNU/Linux) PHP/4.4.1-dev
X-Powered-By: PHP/4.4.1-dev
Location: redirect2.html
Connection: close
Content-Type: text/html; charset=utf-8

foo
<10 second delay here>
Connection closed by foreign host.

-Rasmus

Liang ZHONG wrote:
> Thank you for replying. Sorry for being long again.
> I tried your suggestion of this:
>
> <?php
> ignore_user_abort(true);
> header("Location: redirect2.html");
> echo "foo\n"; flush();
> for($i=0;$i<10;$i++) { echo $i; sleep(1); }
> $fp = fopen("/tmp/foo.txt","a");
> fputs($fp,$i);
> fclose($fp);
> ?>
>
> The browser did not get the redirect page until the new $i(10) has been
> appended to file foo.txt.
>
> I also tried
> -----------------------------------------------
> ob_start();
> header("Location: http://liang.ns2user.info/php/y.html";);
> echo "foo\n";
> ob_end_flush();
> -----------------------------------------------
>
> And does not work.
>
> I think it might be the case of what you suggest:
>
>> If the
>> client sticks around after seeing the redirect and doesn't redirect
>> until after the server has closed the connection, then there is no way
>> to force a close.
>
>
> So may I draw the conclusions:
> 1. Server in php can not actively close the connection to make the
> client see the redirect page immediately after the header has been set?
> 2. The connection is not closed even when the main php end and the
> shutdown function is running?
>
> If above conclusions are not true, could be somewhere that might be
> wrong due to the apache-php configuration or what?
>
> If those are true, I think I have to re-design the project flow control
> and it will take sometime while I have already finished the code
> assuming no above problem. Also I think this might result an awkward
> design for the project. (I mean jsp will be easier to achieve a
> relatively more elegant design).
>
> I use several other program langurages for many years, but new to php,
> so I probably made some assumption base on my experiences of other
> langurages but not true. The project I am working now to add
> enhancements is written on php. I try to keep the main design of it. I
> did not expect it would be difficult to achieve this:
>
> 1. write an xml page
> 2. let http client get this xml page in time
> 3. continue do something time consuming
>
> now I have difficulty in 2 and 3. My client is normally perl
> LWP::UserAgent, and sometimes browser. I wonder how to let the client
> get the page in time before I can continue to finish the rest of the
> work. Otherwise the client will timeout and get no result.
>
> Please help! Thank you.
>
> Liang
>
>>
>> I really didn't follow all that.  But this stuff is not that complex.
>> header() sets a header to be sent when output goes out.  header() does
>> not send an actual header at that point.  If you don't send any output,
>> then the headers won't go out until the script terminates.  If you have
>> any sort of output buffering enabled, then even if you think you are
>> sending something, you may only be buffering it, and in that case again
>> the headers won't go out until the request is done.
>>
>> Also, ignore_user_abort() controls whether or not your script will be
>> terminated when we are able to detect that the user has aborted. A user
>> abort is defined as nobody being around to read the data we are sending
>> out.  If you don't send anything, we can't detect if the browser has
>> gone away.  Generally browsers will redirect as soon as they see a
>> Location: redirect header, but that could be client-speficic.  If the
>> client sticks around after seeing the redirect and doesn't redirect
>> until after the server has closed the connection, then there is no way
>> to force a close.  If it does redirect and close its end of the
>> connection, then if you set ignore_user_abort(false) your script will be
>> terminated as soon as it tries to send something further and your
>> shutdown function will be called at that point.
>>
>> -Rasmus
>>
>> Liang ZHONG wrote:
>> > Hi Rasmus,
>> >
>> > This may be a little bit long, sorry for taking your time.
>> >
>> > It still does not work as expected. I tried some experiment, and found
>> > that if I called some function or write some code line other then
>> > calling header(), the register_shutdown_function and other part of
>> codes
>> > work as expected . For example:
>> > <?php
>> > set_time_limit(5);
>> > function f(){
>> > set_time_limit(10);
>> > //doing something time consuming
>> > }
>> >
>> > some_function();
>> >
>> > ?>
>> > The time limit of 5 will be the limit of the some_function() and the 10
>> > will be the limit of function f() respectively.
>> >
>> > Code example:
>> >
>> >
>> -----------------------------------------------------------------------------------------------------------------------
>>
>> >
>> > <?php
>> > set_time_limit(1);
>> > ignore_user_abort(true);
>> >
>> > function say_goodbye() {
>> >        $st = connection_status();
>> >        print "Status 1: ".$st."\n";
>> >        set_time_limit(10);
>> >        $st = connection_status();
>> >        print "Status 2: ".$st."\n";
>> >
>> >        $count=20000000;
>> >        for($i=0; $i<$count; $i++){    }
>> >            print "End!\n";
>> > exec("touch /home/.nappy/liang/liang.ns2user.info/php/bbb");
>> >    }
>> >
>> >    register_shutdown_function("say_goodbye");
>> >    print "Sleeping...\n";
>> >
>> >    $count=10000000;
>> >    for($i=0; $i<$count; $i++){    }
>> >
>> >    print "Done!\n";
>> >
>> > ?>
>> >
>> ------------------------------------------------------------------------------------------------------------------------
>>
>> >
>> > -bash-2.05b$ curl -N liang.ns2user.info/php/v.php
>> > Sleeping...
>> > <br />
>> > <b>Fatal error</b>:  Maximum execution time of 1 second exceeded in
>> > <b>/home/.nappy/liang/liang.ns2user.info/php/v.php</b> on line
>> > <b>30</b><br />
>> > Status 1: 2
>> > Status 2: 2
>> > End!
>> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> > if I change the time limit from 10 to 5 in function f()
>> >
>> > -bash-2.05b$ curl -N liang.ns2user.info/php/v.php
>> > Sleeping...
>> > <br />
>> > <b>Fatal error</b>:  Maximum execution time of 1 second exceeded in
>> > <b>/home/.nappy/liang/liang.ns2user.info/php/v.php</b> on line
>> > <b>30</b><br />
>> > Status 1: 2
>> > Status 2: 2
>> > <br />
>> > <b>Fatal error</b>:  Maximum execution time of 5 seconds exceeded in
>> > <b>/home/.nappy/liang/liang.ns2user.info/php/v.php</b> on line
>> > <b>14</b><br />
>> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> > Change both the time limit to 10 will result:
>> >
>> > -bash-2.05b$ curl -N liang.ns2user.info/php/v.php
>> > Sleeping...
>> > Done!
>> >
>> > Status 1: 0
>> > Status 2: 0
>> > End!
>> > ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> >
>> > But if the some_function is header() then the rule above does not work.
>> > It seems the function f()'s time limit also rule the header(). Only
>> > after the registered shutdown function finishes runing normally or by
>> > hit the expire time limit, will the header() return page to
>> browser/http
>> > user agent. Example code with suggestion from Rasmus as:
>> >
>> >
>> ------------------------------------------------------------------------------------------------------------------
>>
>> >
>> > <?php
>> >
>> > set_time_limit(5);
>> >
>> > function f(){
>> > set_time_limit(100);
>> > $count=500000000;
>> > for($i=0; $i<$count; $i++){
>> >   //sit here and loop for a bit so we can have time to hit Stop...
>> >   echo "a \n"; flush();
>> > }
>> > echo "end";
>> > exec("touch /tmp/aaa");
>> > }
>> >
>> > register_shutdown_function('f');
>> >
>> > ignore_user_abort(true);
>> > header("Content-type: text/plain");
>> > header("Location: y.html");
>> >
>> > echo "foo\n"; flush();
>> > for($i=0;$i<10;$i++) { echo $i; sleep(1); }
>> > $fp = fopen("/tmp/foo.txt","a");
>> > fputs($fp,$i);
>> > fclose($fp);
>> > ?>
>> >
>> >
>> ------------------------------------------------------------------------------------------------------------------
>>
>> >
>> > After the file /tmp/foo.txt has been created, before the file /tmp/aaa
>> > being created, the y.html will not get to the browser or perl program
>> > using LWP::UserAgent. I think it is no way to close the connection
>> > actively by the php program to deliver the page sooner to end the http
>> > request, even I add lines and make code like this does not work:
>> >
>> > ----------------------------------------------------------------
>> > header("Content-type: text/plain");
>> > header("Cache-Control: no-store, no-cache");
>> > header("Location: y.html");
>> > header("Connection: close");
>> > ----------------------------------------------------------------
>> >
>> > My project act as a broker for user agent to a library database server
>> > called z39.50. Upon the request from user agent, my program need to
>> > connect a z39.50 server, getting data back 1 by 1, transforming to
>> > sepcial xml format and sending back to the request party. When the data
>> > repository is huge (sometimes up to million records), I have to get
>> > partial data transform and send back to user agent (normally a piece of
>> > perl code, called harvester) with a resumption token. The harvester
>> will
>> > in a while loop send out another http request with the last resumption >> > token and fetch data of next part, until finish all data fetching. The
>> > time to connect the database and do the query is a constant
>> overhead. So
>> > it is not a good design that program need to connect the database and
>> > query upon each request with or with out resumption token. So my design >> > is to connect to database and query only when the first initial request >> > comes, and reponse back with partical data using header() and continue >> > getting back data from z39 server. Upon next request, the program will
>> > only need to make sure the data needed has already stored in the
>> > harddrive and transform them and send them back.
>> > Since the user harvester agent normally has a 180 second timeout, it is
>> > necessary to respond within that period of time.
>> >
>> > I really need your suggestion. Thank you very much again.
>> >
>> >
>> > Liang
>> >
>> >
>> >
>> >
>> >> Try somthing lik
>> >
>> >
>> > e this:
>> >
>> >>
>> >> <?php
>> >> ignore_user_abort(true);
>> >> header("Location: redirect2.html");
>> >> echo "foo\n"; flush();
>> >> for($i=0;$i<10;$i++) { echo $i; sleep(1); }
>> >> $fp = fopen("/tmp/foo.txt","a");
>> >> fputs($fp,$i);
>> >> fclose($fp);
>> >> ?>
>> >>
>> >>
>> >> Liang ZHONG wrote:
>> >> > Sorry, does not seem to work here. The code below takes minutes
>> to show
>> >> > up in browser.
>> >> >
>> >> > Any more suggestion?
>> >> >
>> >>
>> ---------------------------------------------------------------------------------------
>>
>> >>
>> >> >
>> >> > <?php
>> >> >
>> >> > set_time_limit(5);
>> >> >
>> >> > function f(){
>> >> > set_time_limit(100);
>> >> > $count=500000000;
>> >> > for($i=0; $i<$count; $i++){ }
>> >> > echo "end";
>> >> > exec("touch /home/.nappy/liang/liang.ns2user.info/php/aaa");
>> >> > }
>> >> >
>> >> > register_shutdown_function('f');
>> >> >
>> >> > ignore_user_abort(true);
>> >> > header("Content-type: text/plain");
>> >> > header("Location: y.html");
>> >> >
>> >> > $count=50000;
>> >> > for($i=0; $i<$count; $i++){ echo " \n";  }
>> >> > flush();
>> >> > ?>
>> >> >
>> >>
>> ---------------------------------------------------------------------------------------
>>
>> >>
>> >> >
>> >> >
>> >> > Liang
>> >> >
>> >> >>
>> >> >> If you don't flush some output after setting the header() then the >> >> >> headers won't go out until the end of the request. So do something
>> >> like:
>> >> >>
>> >> >> ignore_user_abort(true);
>> >> >> header("Location: http://whatever";);
>> >> >> echo "foo\n"; flush();
>> >> >>
>> >> >> Then whatever comes after this should run and the browser is long
>> >> gone.
>> >> >>
>> >> >> -Rasmus
>> >> >>
>> >> >>
>> >> >> Liang ZHONG wrote:
>> >> >> > I think I did not express myself clearly.
>> >> >> >
>> >> >> > What I want is to be able to redirect user an existing page (let
>> >> them
>> >> >> > get it immediately), and to close the connection actively, NOT
>> >> >> passively
>> >> >> > by user abort, at last, to run the function in background.
>> >> >> >
>> >> >> > But the redirecting using function header() with location has a
>> >> problem
>> >> >> > that header() always does return the page to user after the
>> entire
>> >> >> > program, including registered showdown function finish running,
>> >> >> which is
>> >> >> > against the will. I put a time consuming task into a function
>> that
>> >> >> > registered to be a shutdown function and hoping it runs after the
>> >> user
>> >> >> > has got the redirected page and the connection has been
>> closed. But
>> >> >> > experiements (using browsers, curl command line tool as well as
>> >> >> > perl::LWP code) show that the user got the redirected page only
>> >> after
>> >> >> > the shutdown function finished, which is against the
>> description of
>> >> >> > register_shutdown_function at php website.
>> >> >> >
>> >> >> > It seems only header() function use to redirect page has this
>> >> problem
>> >> >> > (not executed until register_shutdown_function finished) while
>> other
>> >> >> > functions like print()/echo(), exec() have not.
>> >> >> >
>> >> >> > The code looks like:
>> >> >> >
>> >> >>
>> >>
>> ---------------------------------------------------------------------------------------------
>>
>> >>
>> >> >>
>> >> >> >
>> >> >> > <?php
>> >> >> > set_time_limit(1);
>> >> >> >
>> >> >> > function f(){
>> >> >> > set_time_limit(20);
>> >> >> > $count=50000000;
>> >> >> > for($i=0; $i<$count; $i++){ }
>> >> >> > echo "end";
>> >> >> > exec("touch /home/.nappy/liang/liang.ns2user.info/php/aaa");
>> >> >> > }
>> >> >> >
>> >> >> > register_shutdown_function('f');
>> >> >> >
>> >> >> > header("Content-type: text/plain");
>> >> >> > header("Location: y.html");
>> >> >> > ?>
>> >> >> >
>> >> >>
>> >>
>> ---------------------------------------------------------------------------------------------
>>
>> >>
>> >> >>
>> >> >> >
>> >> >> >
>> >> >> > http client who sends the request to the php program will only
>> >> get the
>> >> >> > page back as response after function f finsihes (file aaa
>> created).
>> >> >> > Changing the $count will make a lot difference.
>> >> >> >
>> >> >> > My BIGGEST question is:
>> >> >> > How to make user get the redirect page immediately after the
>> >> >> header() is
>> >> >> > called, and not until function f() ends, while making sure
>> that the
>> >> >> > function f() will finally fully (and might slowly) execute?
>> >> >> >
>> >> >> > Thank you very much for kindly replying.
>> >> >> >
>> >> >> > With high respect,
>> >> >> >
>> >> >> > Liang
>> >> >> >
>> >> >> >
>> >> >> >>
>> >> >> >> Liang ZHONG wrote:
>> >> >> >> > My Question is:
>> >> >> >> > What is the correct way to keep the function running after I
>> >> >> >> redirect an
>> >> >> >> > existing page to http client (which I want the client get
>> >> >> immediately)
>> >> >> >> > and then immediately close the connection?
>> >> >> >>
>> >> >> >> ignore_user_abort(true);
>> >> >> >>
>> >> >> >> -Rasmus
>> >> >> >>
>> >> >> >> --
>> >> >> >> PHP General Mailing List (http://www.php.net/)
>> >> >> >> To unsubscribe, visit: http://www.php.net/unsub.php
>> >> >> >>
>> >> >> >
>> >> >>
>> >> >> --
>> >> >> PHP General Mailing List (http://www.php.net/)
>> >> >> To unsubscribe, visit: http://www.php.net/unsub.php
>> >> >>
>> >> >
>> >>
>> >
>>
>


--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php


[Index of Archives]     [PHP Home]     [Apache Users]     [PHP on Windows]     [Kernel Newbies]     [PHP Install]     [PHP Classes]     [Pear]     [Postgresql]     [Postgresql PHP]     [PHP on Windows]     [PHP Database Programming]     [PHP SOAP]

  Powered by Linux