phpapachehttp2nghttp2

HTTP2 and continuing PHP execution


When running PHP, and you want it to immediately return HTML to the browser, close the connection (ish), and then continue processing...

The following works when the connection is HTTP/1.1, but does not when using Apache 2.4.25, with mod_http2 enabled, and you have a browser that supports HTTP/2 (e.g. Firefox 52 or Chrome 57).

What happens is the Connection: close header is not sent.

<?php

    function http_connection_close($output_html = '') {

        apache_setenv('no-gzip', 1); // Disable mod_gzip or mod_deflate

        ignore_user_abort(true);

        // Close session (if open)

        while (ob_get_level() > 0) {
            $output_html = ob_get_clean() . $output_html;
        }

        $output_html = str_pad($output_html, 1023); // Prompt server to send packet.
        $output_html .= "\n"; // For when the client is using fgets()

        header('Connection: close');
        header('Content-Length: ' . strlen($output_html));

        echo $output_html;

        flush();

    }

    http_connection_close('<html>...</html>');

    // Do stuff...

?>

For similar approaches to this problem, see:

  1. close a connection early
  2. Continue processing after closing connection
  3. Continue php script after connection close

And as to why the connection header is removed, the documentation for the nghttp2 library (as used by Apache) states:

https://github.com/nghttp2/nghttp2/blob/master/doc/programmers-guide.rst

HTTP/2 prohibits connection-specific header fields. The 
following header fields must not appear: "Connection"...

So if we cannot tell the browser to close the connection via this header, how do we get this to work?

Or is there another way of telling the browser that it has everything for the HTML response, and that it shouldn't keep waiting for more data to arrive.


Solution

  • How to return HTTP response to the user and resume PHP processing

    This answer works only when web server communicates to PHP over FastCGI protocol.

    To send the reply to user (web server) and resume processing in the background, without involving OS calls, invoke the fastcgi_finish_request() function.

    Example:

    <?php
    
    echo '<h1>This is a heading</h1>'; // Output sent 
    
    fastcgi_finish_request(); // "Hang up" with web-server, the user receives what was echoed
    
    while(true)
    {
        // Do a long task here
        // while(true) is used to indicate this might be a long-running piece of code
    }
    

    What to look out for

    If all available php-fpm child processes are busy, then your users will experience hanging page. Use with caution.

    nginx and apache servers both know how to deal with FastCGI protocol so there should be no requirement to swap out apache server for nginx.