Problem/Motivation
From #3275864: Update to Symfony 6.1.1. Looks like a PHP 8.1 compatibility issue being exposed by the Symfony update rather than anything to do with Symfony specifically.
Addition to symfony/http-foundation/Response::send()
BigPipeResponses are getting a Content-Length header that's based on the size before the replacement magic happened, which leads Responses being cut-off before their "true" end, leaving us with incomplete/incorrect HTML.
Testing Drupal\Tests\big_pipe\Functional\BigPipeTest
1) Drupal\Tests\big_pipe\Functional\BigPipeTest::testBigPipe
Undefined array key 0
/var/www/html/core/modules/big_pipe/tests/src/Functional/BigPipeTest.php:390
/var/www/html/core/modules/big_pipe/tests/src/Functional/BigPipeTest.php:185
/var/www/html/vendor/phpunit/phpunit/src/Framework/TestResult.php:726
2) Drupal\Tests\big_pipe\Functional\BigPipeTest::testBigPipeMultiOccurrencePlaceholders
Behat\Mink\Exception\ResponseTextException: The text "The count is 1." was not found anywhere in the text of the current page.
Knockdown-effect:
ctype_alpha(): Argument of type bool will be interpreted as string in the future
1x in DemoUmamiProfileTest::testUser from Drupal\Tests\demo_umami\Functional
1x: ctype_alpha(): Argument of type bool will be interpreted as string in the future
1x in BreadcrumbTest::testBreadCrumbs from Drupal\Tests\system\Functional\Menu
1x: ctype_alpha(): Argument of type bool will be interpreted as string in the future
1x in NodeTranslationUITest::testPublishedStatusNoFields from Drupal\Tests\node\Functional
https://www.drupal.org/pift-ci-job/2364329
Steps to reproduce
Proposed resolution
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Comments
Comment #2
catchComment #3
catchComment #4
spokjeComment #5
spokjeObjectsResponses in theMirrorBig Pipe AreCloserBigger Than They Appear: Addition to symfony/http-foundation/Response::send()BigPipeResponses are getting a
Content-Lengthheader that's based on the size before the replacement magic happened, which leads Responses being cut-off before their "true" end.Attached patch works(1) (Down from 27 to 22 failures (EDIT: Yes, 22 and boo! to random JS test failures)), doesn't break the current SF6.0 setup(2), but feels dirty and more of a workaround than actually solving anything.
Since the
Content-Lengthheader is set insymfony/http-foundation/Response::send()and that's being at the very end of the chain, I think we could do something like wrapping it in a\Drupal\Core\DrupalKernel::sendfunction where we strip theContent-Lengthheader if the Response is a\Drupal\big_pipe\Render\BigPipeResponse, and swap the $response->send(); here with our custom function.Or maybe this is something that should be addressed upstream in
symfony/http-foundation.Anyway: I'll happily leave decisions like that to the Bigger Brains around here. Putting this on NR to attract those Brains.
(1) 3275864+3276186.patch: The diff of the MR in #3275864: Update to Symfony 6.1.1 as on 2022/04/23, on top of the changes in 3276186.patch, so the
\Drupal\big_pipe\Render\BigPipeResponse::populateBasedOnOriginalHtmlResponsechange.(2) 3276186.patch:Solely the
\Drupal\big_pipe\Render\BigPipeResponse::populateBasedOnOriginalHtmlResponsechange.Comment #6
spokjeComment #7
spokjeComment #8
spokjeComment #9
catchGood spot, bumping this to critical since it's a proper regression rather than a new deprecation or similar.
The issue we have is that big_pipe implements 'chunking' without actually using
Transfer-Encoding: chunked(Drupal\big_pipe\BigPipe even claims that it uses the header). But do we need to do a lot of work to useTransfer-Encoding: chunkedor is it just a case of setting the header? I guess there's an easy way to find out so uploading a patch. There's a further issue though that http/2 doesn't support Transfer-Encoding: chunked although I'm not clear what this means for responses that send it.If we can't use the proper header, then #6 actually seems cleaner than doing this in DrupalKernel - it shouldn't need to know about BigPipe. The follow-up would end up being #2577631: Allow HtmlResponse to use a flexible emitter or something like it to centralise some of the logic.
Feels like this is worth an upstream issue to at least tell them what happened.
Comment #10
spokjeI think I tried this (or something similar) locally and that failed tests as well.
Anyway: Locally and _think_ in one sentence, so we need a proper patch in here, thanks for that.
Agreed.
Agreed, I think we''ll wait on #3276186-9: [upstream] BigPipe breaks with symfony/http-foundation 6.1 return from The Land of TestBot before doing so?
Comment #12
spokje@catch So (what seems to be) the correct way doesn't work.
I think we need to open an upstream issue, and I think if you did it, the issue would have a bit more gravitas (yes, I picked up a new word and want to use it more in conversation...). Also I think your Big Brain would jolt it down a bit more to the point than my incoherent ramblings.
Could you open it?
Comment #13
andypostMaybe it's related to PHP https://github.com/php/php-src/pull/8353
Comment #14
catchHere's the patch from #6 with a link to the upstream issue I just opened https://github.com/symfony/symfony/issues/46449
Comment #16
spokjeRrrrrandom JS testfailure. Back to NR
Comment #17
mondrakeSymfony 6.1 is out.
Comment #18
mondrakeLooks like an upstream patch may take a while, other users reported similar issues, the Symfony team is looking for a test app that reproduces the issue. Maybe we can get this in to get going, the @todo is there, will need a follow up to apply the upstream fix once available.
Comment #19
catchSuggested by Nicolas Grekas in the upstream issue, this might be a better workaround - just override ::send(), it's only a couple of lines without the code they've added. Keeping the @todo because I'm not sure we want to do this forever.
No interdiff because it's just a completely different couple line fix with no overlap.
Comment #20
mondrakePHPStan :)
Comment #21
mondrakeAlso, maybe it's better open a followup in our queue and reference that in the @todo
Comment #22
catchThis should pass phpcs/phpstan now. Also opened the follow-up in our queue.
Comment #23
mondrakeThanks, RTBC. See also https://drupal.slack.com/archives/C014CT1CN1M/p1653913440894539
Comment #24
andypostinteresting how it will affect swoole
Comment #25
catchIt shouldn't affect it at all, because the code is a direct 1-1 copy from the Symfony method. The only thing I did was remove the bit adding the content length header.
Comment #26
andypostI see, thanks! +1 rtbc
Comment #27
wim leersWow, what a huge BC break! :O
Indeed, it does, and I can observe it being set locally, at least according to Chrome. Just stepped through it with xdebug and indeed,
$responseas received bypublic function sendContent(BigPipeResponse $response)does not have that header set!gives
🤯 Source?
I don't like that we're repeating all this … but fortunately https://github.com/symfony/symfony/issues/46449 was fixed 23 minutes ago, by reverting the upstream change that caused this 👍
I wonder if it makes more sense to wait for a new Symfony 6.1 patch release? I'm fine with moving ahead here and reverting all of this in #3283145: Try to use HTTP trailers or a real streaming response in Big Pipe. of course 😄
Comment #28
catchThis has been reverted upstream due to the bc break: https://github.com/symfony/symfony/pull/46523
So I think we can forget about all the patches here, and wait for a new Symfony tag.
Comment #29
catch@Wim Leers
My source for http/2 was https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding
Also found this 2013 listserve thread, which seems inconclusive https://lists.w3.org/Archives/Public/ietf-http-wg/2014JulSep/1676.html
Neither really explain what "doesn't support" means - i.e. what happens to a response using chunked transfer encoding when it meets http/2.
fwiw I tried just setting the header in #3276186-10: [upstream] BigPipe breaks with symfony/http-foundation 6.1 and that definitely doesn't work. Which means if we want to use it, we need to implement the actual encoding or find something that does it for us (i.e. encode the content length at the beginning of each chunk).
My overall feeling is that I think it'd be worth us trying chunked transfer encoding, and assume it'll work (i.e. be a no-op) if someone's using http/2.
I was thinking we should move this discussion to a follow-up, and already opened a placeholder. But since we don't need to commit anything here, maybe it'd be easier to repurpose this issue with a new title and issue summary for the docs/behaviour mismatch.
Comment #30
spokjeAFAICT: This was resolved with SF 6.1.1, so I'm closing this as outdated.