Bug #103215
openPageContentErrorHandler attaches error output to current response's output instead of replacing it.
0%
Description
When a non-cacheable plugin throws a PropagateResponseException with the response of TYPO3\CMS\Frontend\Controller\ErrorController->pageNotFoundAction(), this response gets attached to the output of the originally requested page.
Background:
Since TYPO3 v12 TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler uses a sub-request to render and fetch the content of the configured 404 page. In earlier versions of TYPO3 this was done with a curl request. TYPO3 v11 introduced an experimental and optional feature to use a sub-request (feature: #94402), which was set as standard behaviour within TYPO3 v12 (https://forge.typo3.org/issues/98396).
Problem:
Though the sub-request to fetch the 404 content is done as a new request, the underlying TYPO3\CMS\Core\Page\PageRenderer is a singleton. So, it still holds the already rendered (HTML) output of the original request in its property $bodyContent. During the process of rendering the sub-request, the content of the 404 page is then only added to this content. What is even more annoying: This faulty 404 content will be cached and then delivered whenever a 404 error occurs.
Proposed solution:
Before sending the sub-request, PageContentErrorHandler should clear PageRenderer->bodyContent to make sure only the output of the 404 page is rendered. Perhaps like so:
protected function sendSubRequest(ServerRequestInterface $request, int $pageId, ServerRequestInterface $originalRequest): ResponseInterface
{
$site = $request->getAttribute('site');
if (!$site instanceof Site) {
$site = $this->siteFinder->getSiteByPageId($pageId);
$request = $request->withAttribute('site', $site);
}
$request = $request->withAttribute('originalRequest', $originalRequest);
// Reset already rendered content of the original request/response
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
$pageRenderer->setBodyContent('');
return $this->application->handle($request);
}
Another possible solution would be to reset the entire singleton instance of PageRenderer via GeneralUtility::removeSingletonInstance().
To reproduce this problem:
1. Have a non-cacheable (!) Extbase plugin that throws an Exception like this (FQDN only for not being ambiguous):
public function pageNotFoundAction(): ResponseInterface
{
$errorController = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\Controller\ErrorController\ErrorController::class);
$response = $errorController->pageNotFoundAction(
$GLOBALS['TYPO3_REQUEST'],
'Content unavailable',
[
'code' => \TYPO3\CMS\Frontend\Page\PageAccessFailureReasons\PageAccessFailureReasons::PAGE_NOT_FOUND,
]
);
throw new \TYPO3\CMS\Core\Http\PropagateResponseException\PropagateResponseException($response);
return $this->htmlResponse();
}
2. Place that plugin on a page
3. Inside your site config have a page based error handling for 404 errors like so
errorHandling:
-
errorCode: 404
errorHandler: Page
errorContentSource: 't3://page?uid=xxxx'
3. Make sure the entire TYPO3 cache has been cleared
4. Request the page containing the plugin with a client