Problem/Motivation
If you take Drupal 8 out-of-the box you will see that the Content-Length header is never being sent to the user.
The symfony component that is delivering the page content is doing this:
$this->sendHeaders();
$this->sendContent();
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
} elseif ('cli' !== PHP_SAPI) {
static::closeOutputBuffers(0, true);
}
Both Kernel termination events and any registered shutdown functions will execute after this. Not only core but contrib rely on defering code execution with register_shutdown_function() so that it will not slow down the request.
A more or less universal rule for any platform running PHP vía fastCgi is that the connection to the client will be closed:
- a) If fastcgi_finish_request() is called explicitly.
- b) If the Content-Length header is sent, and content matching the Content-Length header is flushed to the output.
See the issues regarding terminating the connection to the client and then being able to run code in the comments here:
http://php.net/manual/en/features.connection-handling.php
http://php.net/manual/en/function.register-shutdown-function.php
As it is now, only case (a) is being covered. So on some platforms/setups the response is being ransomed by shutdown functions and kernel termination events.
Just as a way of demonstrating this situation, if you add a sleep() call after sending the response in Index.php, you shold not expect your request to be ransomed by this:
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
sleep(5);
$kernel->terminate($request, $response);
But... response is being delayed 5 seconds in the example before being sent to the user! Adding the Content-Length header will make the output to be inmediately sent to the user after calling $response->end():
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->headers->add(array('Content-Length' => strlen($response->getContent())));
$response->send();
sleep(5);
Proposed resolution
Probably making delivery handlers explictly set the Content-Length header.
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #26 | interdiff_25-26.txt | 705 bytes | pooja saraah |
| #26 | 2579775-26.patch | 2.49 KB | pooja saraah |
| #25 | reroll_diff_20-25.txt | 2.93 KB | ravi.shankar |
| #25 | 2579775-25.patch | 2.51 KB | ravi.shankar |
| #20 | content_length_header-2579775-20.patch | 3.05 KB | pobster |
Issue fork drupal-2579775
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
david_garcia commentedComment #5
wim leersI can't believe this. You're totally right. I was certain you were wrong, and I was looking for where this was being set, but could not find it… because it never gets set!
We should definitely do this. But we need to make sure that we don't set this for streamed responses. Attached patch does this.
However, this is not a bug, it's a task. Nothing is broken. This merely improves (front-end) performance.
Comment #6
david_garcia commentedThanks for taking on this. Tested and verified that works as advertised.
This makes a huge difference for non streamed responses. I.E. in the supercache module cache tags are persistend in a kernel terminate event, so the actual performance gains where "masked" by this "bug".
To me this is more an issue with the FastCgi implementations, ALL environments should have a fastcgi_finish_request() but we've got what we've got.....
After patching you need to rebuild, otherwise the new event subscriber will not get registered.
Comment #7
david_garcia commentedToo fast... looks like something broke with bigpipe, after enabling the module I am getting fiddler to complain about HTTP protocol violations where content length sent by server is greater than what was specified in content-length header.
Comment #8
david_garcia commentedThe issue was me having an outdated version of bigpipe in /modules/ instead of the one in core.
Comment #9
wim leersThe patch is not ready yet. It needs test coverage, and it needs to be cleaned up.
Comment #10
david_garcia commentedGOTCHA: If you send the content-length header, but for any reason, the output sent to the buffer is "smaller" thant the reported content-length header, then you get the exact opposite effect: the request is "ransomed" by fast-cgi indefinitely (until request times out) as it is expecting the missing content to arrive.
I am seeing this in the "extend" (priorly known as modules) page. Maybe due to encoding issues the flushed output does not match reported content-length.
Comment #11
wim leersSounds feasible.
Comment #16
wim leersMarked #3040738: Add the `Content-Length` header to the response as a duplicate of this.
Comment #19
sassafrass commentedHi... I need to access the Content_Length in the HTTP Client Header and tried applying this patch to Drupal core 8.9.10, but, not surprisingly, it failed. Was wondering if this functionality will be added to current and future versions of Drupal, or should I try a work-around? Thanks!
Comment #20
pobster commentedRerolled for latest 8.9.x.
Comment #25
ravi.shankar commentedAdded reroll of patch #20 on Drupal 9.4.x., still needs work for tests.
Comment #26
pooja saraah commentedFixed failed commands on #25
Attached interdiff patch
Comment #28
bradjones1I'm marking this as a duplicate of #3295790: Post-response task running (destructable services) are actually blocking; add test coverage and warn for common misconfiguration which fixes a broader range of post-response termination concerns. Credit should be given there for meaningful contributors to this issue.