Problem/Motivation
This blocks BigPipe for Drupal 8.
HtmlResponseSubscriber
should react to all requests: both master and sub requests. Otherwise, attachments would not be processed for those embedded HTML responses.
For context: the BigPipe module provides two alternative placeholder replacement strategies: a JS-based one and one that does not need JS. For the one that does not need JS, we need to be able to render a placeholder into its HTML markup (not a problem), and prefix it with <link rel="stylesheet" …>
and <script …>
markup to load its critical assets (CSS + header JS). For that, we need HtmlResponseAttachmentsProcessor
to act on subrequests.
Proposed resolution
Ensure HtmlResponseAttachmentsProcessor
is called always; not just for the master request.
Remaining tasks
Review & RTBC.
User interface changes
None.
API changes
None.
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
#38 | 2603788-38.patch | 5.59 KB | Wim Leers |
Comments
Comment #2
Wim LeersComment #4
Wim LeersReproduced the failure. And it was a quite tricky one to debug.
Turns out this actually uncovers a bug in HEAD. 4xx pages are rendered using subrequests. This is once again related to #2230121: Remove exit() from FormBuilder and indicates how #2206909: Regression: Form submit redirects from 403/404 pages are no longer possible didn't actually fix everything: it fixed the
?destination=
case, but not the "redirect caused by a form" case. Which is what we're seeing here.In HEAD, the 4xx page's placeholders are rendered in the master request. When placeholders are replaced, this triggers
\Drupal\Core\Form\FormSubmitter::redirectForm()
which doesreturn new RedirectResponse($url, Response::HTTP_SEE_OTHER);
. This goes up through the layers of building a form until it gets tothrow new EnforcedResponseException($response);
. This is then the response that is returned.However, with the patch, the 4xx page's placeholders are already rendered in the sub request. When placeholders are replaced, this triggers the same process as above. But then after the sub request's response is returned,
\Drupal\Core\EventSubscriber\DefaultExceptionHtmlSubscriber::makeSubrequest()
indiscriminately sets the 4xx status code. This makes sense for a 200 response. But not for a redirect.Schematically:
makeSubrequest()
gets back a 200 responsemakeSubrequest()
gets back a 303 response, because placeholders are already replacedAFAICT the failed test here is exactly the test coverage that we wanted.
Comment #5
Wim LeersDiscussed with @dawehner in IRC:
Done.
Comment #7
Wim LeersWim--
Comment #8
dawehnerThere we go, a more dedicated test.
Comment #9
Wim LeersTest looks great to me. RTBC'ing the test. @dawehner has implicitly RTBC'd the few silly/simple LOC I wrote.
Thanks, @dawehner.
Comment #11
Wim LeersWTF? #8 definitely used to be red for the fail patch and green for the pass patch. It seems testbot is having problems. Re-testing them.
Comment #13
marvin_B8 CreditAttribution: marvin_B8 as a volunteer and at comm-press commentedComment #14
catchAdding #2595695: 4xx handling using subrequests: no longer able to vary by URL to related issues.
Not sure if we actually want to support using subrequests like this at all?
Comment #15
Wim LeersOkay, so #8's fail patch *passes* because it doesn't include the actual test!
The reason it was first failing, but now not anymore, is because there was a DrupalCI bug/problem. They fixed it, and OVERWROTE the existing test results. Which is why it now shows up green.
In other words: an intricate set of PRECISELY the right bad conditions!
Kudos to @Mixologic for trying to figure out wtf happened on the testbot side. Without him I'd still be scratching my head.
Rerolling #8 with proper fail/pass patches now. Identical to #8, now just with a proper FAIL patch.
Comment #16
Wim LeersFor completeness:
Comment #18
Wim LeersBack to RTBC per #9.
Comment #21
Wim LeersThat's weird; the green patch of #15 came back red this time. It looks like a testbot problem, so re-testing. Let's see what happens.
Comment #23
Wim LeersThere we go, a red #15 fail-patch, a green #15 regular patch.
Back to RTBC per #9.
Comment #25
catchStill not sure about this given #14 - do we want to defer the 403/404 fixes to that issue?
Comment #28
Wim Leers#14:
If by this you mean
, then I fully agree.If you mean something else, could you please clarify?
Comment #29
Wim LeersIOW: I think @catch means:
, I agree that'd be great.Comment #30
Wim Leers@catch confirmed in IRC that #28/#29 is what he meant.
Comment #31
Wim LeersNote that #2595695-23: 4xx handling using subrequests: no longer able to vary by URL as of five seconds ago now has a working patch, but it's AFAICT utterly impossible to get rid of subrequests. See that comment.
Therefore, moving back to RTBC, since this patch will hence still be necessary.
Comment #33
effulgentsia CreditAttribution: effulgentsia at Acquia commentedI have some concerns about this issue, both about doing it during RC, and about doing it at all.
First of all, I find it hard to reason about whether it makes sense theoretically to process attachments during subrequests or not, because I find the entire concept of subrequests hard to reason about. Sometimes they're used for exceptions, though I guess that's getting cleaned up in #2595695: 4xx handling using subrequests: no longer able to vary by URL. Sometimes they're used as a type of internal redirect/proxy, such as in
CommentController::commentPermalink()
. #2469431: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts is another interesting use of them. And I'm guessing there's many other ways to use them. Because these are all pretty different ways to use them, I struggle at having a coherent understanding of what should and should not be done during subrequests.Also,
HtmlResponseAttachmentsProcessor::processAttachments()
does a few different things, including placeholders, assets, and headers. I suspect that some of those things make sense to do for all subrequests, but I'm not convinced that all of those things are.Just looking at one example, this patch would make processAttachments() run during the subrequest within commentPermalink(), which would make hook_js_settings_alter() run at that time, which would result in
drupalSettings.path.currentPath
on the/comment/1
page being set to/node/1
. While that might be harmless, I don't think it's logically what we want. And therefore, I'm concerned that there would be other side effects from this patch that are not what we would want. By the way, this specific bug already exists in HEAD for a separate reason, so I opened #2613044: Requests are pushed onto the request stack twice, popped once to fix that. The point is once that issue is fixed, this patch would reintroduce that drupalSettings bug (if we consider it a bug, which I suppose is debatable).Meanwhile, is this issue really a blocker for #2469431: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts? Could that issue / contrib project instead implement an additional subscriber that runs processAttachments() for the subrequests that that code creates? That way, we don't introduce side effects for code that uses subrequests for different purposes, such as the comment permalinks.
Setting this to "needs review" for more discussion on these points, but if others feel this patch is the logically correct thing to do, I'm willing to be convinced.
Comment #35
catchFor that page specifically, I think it actually is what we want - the comment/cid page is supposed to be exactly the same as the node/1 page in terms of functionality (and has rel=canonical to indicate they're the same).
Comment #36
Wim LeersTo answer the overall question:
Yes, it could. But I don't see why it should.
To simplify future archeology: #2469431-181: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts is the first iteration of BigPipe that updated
HtmlResponseSubscriber
also apply to subrequests.To answer the concrete points put forward:
I can somewhat see that. But, really, it's very simple IMO: subrequests are just the same like any other request. They should get responses just like any other request. It's just that for some practical reason, it makes sense for a certain (master) request to execute some subrequest(s). In the case of the comment permalink, we essentially do an internal redirect, and use the subrequest's response to answer the master request. In the case of BigPipe, we do something somewhat less orthodox: we take the subrequest's response and parse out the parts we want for the purpose of having an advanced render pipeline (
$response->getContent()
for the rendered HTML and$response->getAttachments()
to get its final (processed) attachments).Which then do not make sense to you?
Comment #37
effulgentsia CreditAttribution: effulgentsia at Acquia commentedThanks for #35 and #36. That makes me more comfortable with this going in, even during RC, so tagging accordingly (since @catch also agrees). There are two things I'd like to see though:
isMasterRequest()
check to also be removed fromHtmlResponsePlaceholderStrategySubscriber
. Because that subscriber's getSubscribedEvents() method is commented as needing to run before HtmlResponseSubscriber, but if it waits until the master request, then it ends up running after, in the case of examples such as the comment permalink one.isMasterRequest()
in the other subscribers. Since #36 argues that "subrequests are just the same like any other request", we should make sure to only treat them differently when there's a clear reason to, and then we need to document that reason.Comment #38
Wim Leers#37.1: Interesting observation! Interestingly, you uncovered a BigPipe bug in the process: in its current form (#2469431-191: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts), it does not work for placeholders attached to the response for a subrequest! So the comment form for
/comment/1
is not delivered via BigPipe.More detailed research:
Done in this reroll, as requested.
#37.2: Totally agreed! Follow-up created: #2613550: Rationalize & document use of isMasterRequest()-checks in event subscribers.
Comment #39
effulgentsia CreditAttribution: effulgentsia at Acquia commentedThanks!
Comment #40
catchCommitted/pushed to 8.0.x, thanks!
Comment #41
Wim LeersYAY!!!!!!!!