diff --git a/src/Middleware/ErrorFilter.php b/src/Middleware/ErrorFilter.php index bf641d0b..dfcf2662 100644 --- a/src/Middleware/ErrorFilter.php +++ b/src/Middleware/ErrorFilter.php @@ -4,12 +4,16 @@ namespace Horde\Core\Middleware; +use Horde; +use Horde\Http\ResponseFactory; +use Horde\Http\StreamFactory; +use Horde_ErrorHandler; +use Horde_Registry; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; -use Horde_Registry; -use Horde_Application; +use Throwable; /** * ErrorFilter middleware @@ -31,9 +35,58 @@ */ class ErrorFilter implements MiddlewareInterface { + protected Horde_Registry $registry; + protected ResponseFactory $responseFactory; + protected StreamFactory $streamFactory; + + public function __construct( + Horde_Registry $registry, + ResponseFactory $responseFactory, + StreamFactory $streamFactory + ) { + $this->registry = $registry; + $this->responseFactory = $responseFactory; + $this->streamFactory = $streamFactory; + } + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { - // TODO: - return $handler->handle($request); + try { + return $handler->handle($request); + } catch (Throwable $throwable) { + Horde::log($throwable, 'EMERG'); + return $this->getErrorResponse($request, $throwable); + } + } + + protected function getErrorResponse(ServerRequestInterface $request, Throwable $throwable): ResponseInterface + { + $isAdmin = $this->registry->isAdmin(); + $acceptsJson = in_array('application/json', array_map(fn($val) => strtolower($val), $request->getHeader('Accept'))); + if ($acceptsJson){ + return $this->getJsonResponse($throwable, $isAdmin); + } else { + return $this->getHtmlResponse($throwable, $isAdmin); + } + } + + protected function getJsonResponse(Throwable $throwable, bool $isAdmin = false): ResponseInterface + { + $json = json_encode([ + 'message' => $throwable->getMessage(), + 'code' => $throwable->getCode(), + 'trace' => $isAdmin ? $throwable->getTrace() : [], + ]); + $stream = $this->streamFactory->createStream($json); + return $this->responseFactory->createResponse(500, 'Internal Server Error') + ->withBody($stream) + ->withHeader('Content-Type', 'application/json'); + } + + protected function getHtmlResponse(Throwable $throwable, bool $isAdmin = false): ResponseInterface + { + $stream = $this->streamFactory->createStream(Horde_ErrorHandler::getHtmlForError($throwable, $isAdmin)); + return $this->responseFactory->createResponse(500, 'Internal Server Error') + ->withBody($stream); } } diff --git a/src/Middleware/HordeCore.php b/src/Middleware/HordeCore.php index fc4ec53c..8ea22e6b 100644 --- a/src/Middleware/HordeCore.php +++ b/src/Middleware/HordeCore.php @@ -56,13 +56,15 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $injector->setInstance(ResponseFactoryInterface::class, new ResponseFactory()); } - - // Detect correct app $registry = $injector->getInstance('Horde_Registry'); - $request = $request->withAttribute('registry', $registry); + // First middleware should be ErrorFilter to catch all errors + $handler->addMiddleware(new ErrorFilter($registry, new ResponseFactory(), new StreamFactory())); + // Detect correct app $handler->addMiddleware(new AppFinder($registry, new ResponseFactory(), new StreamFactory())); // Find route inside detected app $handler->addMiddleware(new AppRouter($registry, $injector->get('Horde_Routes_Mapper'), $injector)); + + $request = $request->withAttribute('registry', $registry); return $handler->handle($request); } }