diff --git a/typo3/sysext/core/Classes/Error/PageErrorHandler/PageContentErrorHandler.php b/typo3/sysext/core/Classes/Error/PageErrorHandler/PageContentErrorHandler.php index 7935b59054724dc79bf1476b73bbb076d5a23599..c58510c83ffdc17a2680e99a344f13b9748075cd 100644 --- a/typo3/sysext/core/Classes/Error/PageErrorHandler/PageContentErrorHandler.php +++ b/typo3/sysext/core/Classes/Error/PageErrorHandler/PageContentErrorHandler.php @@ -18,14 +18,19 @@ namespace TYPO3\CMS\Core\Error\PageErrorHandler; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use TYPO3\CMS\Core\Cache\CacheManager; +use TYPO3\CMS\Core\DependencyInjection\FailsafeContainer; use TYPO3\CMS\Core\Exception\SiteNotFoundException; use TYPO3\CMS\Core\Http\HtmlResponse; +use TYPO3\CMS\Core\Http\MiddlewareDispatcher; +use TYPO3\CMS\Core\Http\MiddlewareStackResolver; use TYPO3\CMS\Core\LinkHandling\LinkService; use TYPO3\CMS\Core\Routing\InvalidRouteArgumentsException; +use TYPO3\CMS\Core\Service\DependencyOrderingService; use TYPO3\CMS\Core\Site\Entity\Site; -use TYPO3\CMS\Core\Site\Entity\SiteLanguage; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Frontend\Http\RequestHandler; /** * Renders the content of a page to be displayed (also in relation to language etc) @@ -68,8 +73,20 @@ class PageContentErrorHandler implements PageErrorHandlerInterface */ public function handlePageError(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface { + $linkService = GeneralUtility::makeInstance(LinkService::class); + $urlParams = $linkService->resolve((string)$this->errorHandlerConfiguration['errorContentSource']); + + if ($urlParams['type'] !== 'page' && $urlParams['type'] !== 'url') { + throw new \InvalidArgumentException('PageContentErrorHandler can only handle TYPO3 urls of types "page" or "url"', 1522826609); + } + + if ($urlParams['type'] === 'page') { + $response = $this->buildSubRequest($request, (int)$urlParams['pageuid']); + return $response->withStatus($this->statusCode); + } + + $resolvedUrl = $urlParams['url']; try { - $resolvedUrl = $this->resolveUrl($request, $this->errorHandlerConfiguration['errorContentSource']); $content = null; $report = []; @@ -82,59 +99,67 @@ class PageContentErrorHandler implements PageErrorHandlerInterface } catch (InvalidRouteArgumentsException | SiteNotFoundException $e) { $content = 'Invalid error handler configuration: ' . $this->errorHandlerConfiguration['errorContentSource']; } + return new HtmlResponse($content, $this->statusCode); } /** - * Resolve the URL (currently only page and external URL are supported) - * * @param ServerRequestInterface $request - * @param string $typoLinkUrl - * @return string + * @param int $pageId + * @return ResponseInterface * @throws SiteNotFoundException - * @throws InvalidRouteArgumentsException + * @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException + * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException + * @throws \TYPO3\CMS\Core\Exception + * @throws \RuntimeException */ - protected function resolveUrl(ServerRequestInterface $request, string $typoLinkUrl): string + protected function buildSubRequest(ServerRequestInterface $request, int $pageId): ResponseInterface { - $linkService = GeneralUtility::makeInstance(LinkService::class); - $urlParams = $linkService->resolve($typoLinkUrl); - if ($urlParams['type'] !== 'page' && $urlParams['type'] !== 'url') { - throw new \InvalidArgumentException('PageContentErrorHandler can only handle TYPO3 urls of types "page" or "url"', 1522826609); - } - if ($urlParams['type'] === 'url') { - return $urlParams['url']; - } - $site = $request->getAttribute('site', null); if (!$site instanceof Site) { - $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId((int)$urlParams['pageuid']); + $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($pageId); + $request = $request->withAttribute('site', $site); } - $language = $request->getAttribute('language', null); - if (!$language instanceof SiteLanguage || !$language->isEnabled()) { - $language = $site->getDefaultLanguage(); + + if (!$this->pageExistsAndInRootline($pageId, $site->getRootPageId())) { + throw new \RuntimeException('Page does not exist or is not in rootline.', 1582448967); } - // Build Url - $uri = $site->getRouter()->generateUri( - (int)$urlParams['pageuid'], - ['_language' => $language] + $request = $request->withQueryParams(['id' => $pageId]); + $dispatcher = $this->buildDispatcher(); + return $dispatcher->handle($request); + } + + /** + * @return MiddlewareDispatcher + * @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException + * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException + * @throws \TYPO3\CMS\Core\Exception + */ + protected function buildDispatcher() + { + $requestHandler = GeneralUtility::makeInstance(RequestHandler::class); + $resolver = new MiddlewareStackResolver( + GeneralUtility::makeInstance(FailsafeContainer::class), + GeneralUtility::makeInstance(DependencyOrderingService::class), + GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_core') ); - // Fallback to the current URL if the site is not having a proper scheme and host - $currentUri = $request->getUri(); - if (empty($uri->getScheme())) { - $uri = $uri->withScheme($currentUri->getScheme()); - } - if (empty($uri->getUserInfo())) { - $uri = $uri->withUserInfo($currentUri->getUserInfo()); - } - if (empty($uri->getHost())) { - $uri = $uri->withHost($currentUri->getHost()); - } - if ($uri->getPort() === null) { - $uri = $uri->withPort($currentUri->getPort()); - } + $middlewares = $resolver->resolve('frontend'); + return new MiddlewareDispatcher($requestHandler, $middlewares); + } - return (string)$uri; + /** + * @param int $pageId + * @param int $rootPageId + * @return bool + */ + protected function pageExistsAndInRootline(int $pageId, int $rootPageId): bool + { + try { + return GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($pageId)->getRootPageId() === $rootPageId; + } catch (SiteNotFoundException $e) { + return false; + } } } diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-90505-MakePageContentErrorHandlerResolveInternalPagesWithSubRequests.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-90505-MakePageContentErrorHandlerResolveInternalPagesWithSubRequests.rst new file mode 100644 index 0000000000000000000000000000000000000000..abd83d06d198f8c618046d0a56ce18bf08a8031b --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-90505-MakePageContentErrorHandlerResolveInternalPagesWithSubRequests.rst @@ -0,0 +1,23 @@ +.. include:: ../../Includes.txt + +=========================================================================================== +Feature: #90505 - Allow PageContentErrorHandler to resolve internal pages with sub requests +=========================================================================================== + +See :issue:`90505` + +Description +=========== + +The PageContentErrorHandler provided by the core can take in either a URL or a page uid for resolving an error page in the frontend. In both cases, the class would then start a Guzzle/cURL request to fetch the error page content. +This has now been changed for internal pages, where a page uid has been given. In this case, the PageContentErrorHandler will now dispatch an internal SubRequest instead, to avoid an unnecessary cURL call. + + +Impact +====== + +In staging environments, the website would often be access protected with basic auth options (for example a .htpasswd auth file on Apache Webservers). +In such a case, error pages with the default PageContentErrorHandler would have failed before, as the internal cURL call for fetching the error page was lacking these required basic auth options. +For internal pages, a sub request is now used, bypassing the need for an external cURL call. + +.. index:: Frontend, ext:core