Jay Paroline
Jay Paroline

Reputation: 2527

PHP + gzip: close connection and continue executing

I am responsible for the backend portion of an API, written in PHP, which is primarily used by a Flash client. What happens right now is: the Flash client makes a call, the backend loads the necessary data, does any necessary processing and post processing, logging and caching and then returns the result to the client.

What I would like to have happen is return the data to the client as soon as possible, close the connection, and then do all the stuff that the client doesn't have to care about. This could make the API seem much more responsive. Following the suggestions here:

http://php.net/manual/en/features.connection-handling.php

actually works, except that I have to turn off gzip encoding in order to make it work, which isn't very practical. We use mod_deflate in apache, so a solution that works with that would be ideal, but I would also consider a different method to gzip our content if that is necessary.

It seems like there should be a way to let Apache know "I've sent you all the data I'm going to send," but I can't seem to find anything like that.

For those wondering, yes I can flush the results early, but the Flash client will not process them until the connection is closed.

Upvotes: 5

Views: 1310

Answers (5)

Ben
Ben

Reputation: 1

Today I also met this case, after some tests around, I found this way works:

Two steps:

  1. Make sure the php script output is not with gzip encoding, the solution can refer to this link:

    <IfModule mod_env.c>
    SetEnvIfNoCase Request_URI "\.php$" no-gzip dont-vary
    </IfModule>

Add the above to .htaccess file under the prj web site, then avoid apache gzip it automatically.

  1. As some people said at features.connection-handling.php,

    set_time_limit(0); 
    ignore_user_abort(true);    
    // buffer all upcoming output - make sure we care about compression: 
    if(!ob_start("ob_gzhandler")) 
        ob_start();         
    echo $stringToOutput;    
    // get the size of the output 
    $size = ob_get_length();    
    // send headers to tell the browser to close the connection    
    header("Content-Length: $size"); 
    header('Connection: close');    
    // flush all output 
    ob_end_flush(); 
    ob_flush(); 
    flush();    
    // close current session 
    if (session_id()) session_write_close(); //close connection
    
    // here, do what you want.
    

Upvotes: -1

TeAmEr
TeAmEr

Reputation: 4773

set_time_limit(0);
header("Connection: close");
header("Content-Length: " .(strlen($stream)+256));
ignore_user_abort(true);

echo $stream;
echo(str_repeat(' ',256));
@ob_flush();
@flush();
 @ob_end_flush();

your_long_long_long_long_function_here();

this will tell the user to close the connection once all of $stream is received . but be careful not to echo anything before the header part u know :p

if you are sending binary data (swf) you might need to remove the '+256' and echo(str_repeat(' ',256)); but in this case the code 'might' fail if the data sent is les than 256 bytes .

Upvotes: 0

Eli
Eli

Reputation: 99408

You might try breaking it into two pages.

In the first page, do the necessary processing, then load the second page via curl, and die().

That would cause the first page to complete and close, independent of the second page processing.

ie:

Page 1:

<?php

// Do stuff

// Post or get second page...

// Send Data to client

die();
?>

Page 2:

<?php

// Do other stuff....

?>

See http://www.php.net/curl

Upvotes: 3

Jay Paroline
Jay Paroline

Reputation: 2527

@Theo.T since the comment system mangled the crap out of my code, I'm posting it here:

No luck. The following prints out the extra crap and takes the full execution time to close the connection when using mod_deflate:

function sleepLongTime() { 
    print "you can't see this";
    sleep(30);
}
ob_end_clean();
register_shutdown_function('sleepLongTime');
header("Connection: close\r\n");
ignore_user_abort(true);
ob_start();
echo ('Text user will see');
ob_end_flush();
flush();
ob_end_clean();
die();

Upvotes: 0

Theo.T
Theo.T

Reputation: 9267

There's a kind of hack to do this by placing the code you want to execute after the connection closes within a callback method registered via to register_shutdown_function();

Upvotes: 0

Related Questions