Response returned only after kernel.terminate even

2019-03-12 20:05发布

问题:

My understanding of kernel.terminate is that it triggers after the response has been returned to the client.

In my testing tough, this does not appear to be the case. If I put a sleep(10) in the function that's called on kernel.terminate. the browser also waits for 10 seconds. The processing seems to be happening before the response is sent.

I have the following in config:

calendar:
    class: Acme\CalendarBundle\Service\CalendarService
    arguments: [ @odm.document_manager, @logger, @security.context, @event_dispatcher ]
    tags:
        - { name: kernel.event_subscriber }

My subscriber class:

class CalendarService implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            'kernel.terminate' => 'onKernelTerminate'
        );
    }

    public function onKernelTerminate()
    {
        sleep(10);
        echo "hello";
    }
}

UPDATE

This appears to be related to Symfony not sending a Content-Length header. If I generate that, the response return properly.

// app_dev.php
...
$kernel = new AppKernel('dev', true);
$kernel->loadClassCache();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);

// --- START EDITS ---
$size = strlen($response->getContent());
$response->headers->set('Content-Length', $size);
$response->headers->set('Connection', 'close');
// ---- END EDITS ----

$response->send();
$kernel->terminate($request, $response);

回答1:

This issue turned out to be very specific to my setup (Nginx, PHP-FCGI, Symfony).

There were a handful of issues in play that caused the issue:

  1. Symfony does not include a Content-Length nor Connection: close header
  2. PHP-FCGI does not support the fastcgi_finish_request function
  3. Nginx buffers the response from PHP-FCGI because Gzip is on

The solution was to switch from PHP-FCGI to PHP-FPM in order to get support for fastcgi_finish_request. Symfony internally calls this before executing the kernel terminate logic thereby definitively closing the connection.

Another way to solve this would be to turn off Gzip on Nginx, but this wasn't really an option for me.