Skip to content

Commit

Permalink
Add additional debugging HTTP headers if no Route was found
Browse files Browse the repository at this point in the history
  • Loading branch information
cundd committed Nov 8, 2019
1 parent 63e0082 commit ab26785
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 52 deletions.
16 changes: 15 additions & 1 deletion Classes/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Cundd\Rest\Log\LoggerInterface;
use Cundd\Rest\Router\ResultConverter;
use Cundd\Rest\Router\RouterInterface;
use Cundd\Rest\Utility\DebugUtility;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
Expand Down Expand Up @@ -94,9 +95,11 @@ public function processRequest(ServerRequestInterface $request): ResponseInterfa
*/
public function dispatch(RestRequestInterface $request): ResponseInterface
{
$response = $this->dispatchInternal($request);

return $this->addCorsHeaders(
$request,
$this->addAdditionalHeaders($this->dispatchInternal($request))
$this->addAdditionalHeaders($this->addDebugHeaders($request, $response))
);
}

Expand Down Expand Up @@ -308,4 +311,15 @@ private function addHeaders(

return $response;
}

private function addDebugHeaders(RestRequestInterface $request, ResponseInterface $response): ResponseInterface
{
if (!DebugUtility::allowDebugInformation()) {
return $response;
}

return $response
->withAddedHeader(Header::CUNDD_REST_RESOURCE_TYPE, (string)$request->getResourceType())
->withAddedHeader(Header::CUNDD_REST_PATH, (string)$request->getPath());
}
}
31 changes: 3 additions & 28 deletions Classes/ErrorHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

namespace Cundd\Rest;

use Cundd\Rest\Utility\DebugUtility;

/**
* Error handler to capture fatal errors
*/
Expand All @@ -23,19 +25,7 @@ public static function registerHandler()
*/
public static function getShowDebugInformation()
{
if ('' !== (string)getenv('TEST_MODE')) {
return false;
}
if (php_sapi_name() === 'cli') {
return true;
}
$clientAddress = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
$devIpMask = static::getDevIpMask();
if (in_array('*', $devIpMask)) {
return true;
}

return in_array($clientAddress, $devIpMask);
return DebugUtility::allowDebugInformation();
}

/**
Expand All @@ -54,21 +44,6 @@ public static function checkForFatalError()
}
}

/**
* @return string[]
*/
private static function getDevIpMask()
{
if (isset($GLOBALS['TYPO3_CONF_VARS'])
&& isset($GLOBALS['TYPO3_CONF_VARS']['SYS'])
&& isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'])
) {
return explode(',', $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']);
}

return [];
}

/**
* Print the error information
*
Expand Down
23 changes: 21 additions & 2 deletions Classes/Http/Header.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,28 @@ abstract class Header
public const CORS_METHODS = 'Access-Control-Allow-Methods';
public const CORS_CREDENTIALS = 'Access-Control-Allow-Credentials';

// This header will be sent if the response has been cached by the REST extension
/**
* This header will be sent if the response has been cached by the REST extension
*/
public const CUNDD_REST_CACHED = 'X-Cundd-Rest-Cached';

// This header can be set to prevent the REST extension from caching a response
/**
* This header can be set to prevent the REST extension from caching a response
*/
public const CUNDD_REST_NO_CACHE = 'X-Cundd-Rest-No-Cache';

/**
* Header to send debug information about the Resource Type
*/
public const CUNDD_REST_RESOURCE_TYPE = 'X-Cundd-Rest-Resource-Type';

/**
* Header to send debug information about the path
*/
public const CUNDD_REST_PATH = 'X-Cundd-Rest-Path';

/**
* Header to send debug information about suggested routes
*/
public const CUNDD_REST_ROUTER_ALTERNATIVE_ROUTES = 'X-Cundd-Rest-Router-Alternative-Routes';
}
54 changes: 52 additions & 2 deletions Classes/Router/Exception/NotFoundException.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,60 @@

namespace Cundd\Rest\Router\Exception;

use Cundd\Rest\Router\RouteInterface;
use Cundd\Rest\Utility\DebugUtility;
use RuntimeException;
use Throwable;
use function sprintf;

/**
* An exception to signal a Not Found route
* An exception to signal that a Route was Not Found
*/
class NotFoundException extends \RuntimeException
class NotFoundException extends RuntimeException
{
/**
* @var RouteInterface[]
*/
private $alternativeRoutes = [];

/**
* NotFoundException constructor
*
* @param string $message
* @param int $code
* @param Throwable|null $previous
* @param RouteInterface[] $alternativeRoutes
*/
public function __construct($message = "", $code = 0, Throwable $previous = null, array $alternativeRoutes = [])
{
parent::__construct($message, $code, $previous);
$this->alternativeRoutes = $alternativeRoutes;
}

/**
* Build a new NotFound Exception
*
* @param string $route
* @param string $method
* @param array $alternativeRoutes
* @return static
*/
public static function exceptionWithAlternatives(string $route, string $method, array $alternativeRoutes)
{
$message = DebugUtility::allowDebugInformation()
? sprintf('Route "%s" not found for method "%s"', $route, $method)
: '';

return new static($message, 0, null, $alternativeRoutes);
}

/**
* Return the suggestions for alternative routes
*
* @return RouteInterface[]
*/
public function getAlternativeRoutes(): array
{
return $this->alternativeRoutes;
}
}
78 changes: 66 additions & 12 deletions Classes/Router/ResultConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@
namespace Cundd\Rest\Router;

use Cundd\Rest\Domain\Model\ResourceType;
use Cundd\Rest\ErrorHandler;
use Cundd\Rest\Http\Header;
use Cundd\Rest\Http\RestRequestInterface;
use Cundd\Rest\ResponseFactoryInterface;
use Cundd\Rest\Router\Exception\NotFoundException;
use Cundd\Rest\Utility\DebugUtility;
use Exception;
use Psr\Http\Message\ResponseInterface;
use function array_map;
use function implode;
use function preg_replace;
use function str_replace;
use function substr;

/**
* Class to convert simple Router results into Response instances and handle exceptions
Expand Down Expand Up @@ -57,17 +64,17 @@ public function dispatch(RestRequestInterface $request)
{
try {
$result = $this->router->dispatch($request);
} catch (\Exception $exception) {
} catch (Exception $exception) {
$result = $exception;
}
if ($result instanceof ResponseInterface) {
return $result;
}

if ($result instanceof NotFoundException) {
return $this->responseFactory->createErrorResponse($result->getMessage() ?: null, 404, $request);
return $this->notFoundToResponse($result, $request);
}
if ($result instanceof \Exception) {
if ($result instanceof Exception) {
return $this->exceptionToResponse($result, $request);
}

Expand Down Expand Up @@ -143,20 +150,19 @@ public function routeDelete($pattern, callable $callback)
return $this;
}


/**
* Convert exceptions that occurred during the dispatching
*
* @param \Exception $exception
* @param Exception $exception
* @param RestRequestInterface $request
* @return ResponseInterface
*/
private function exceptionToResponse(\Exception $exception, RestRequestInterface $request)
private function exceptionToResponse(Exception $exception, RestRequestInterface $request)
{
try {
$exceptionHandler = $this->exceptionHandler;
$exceptionHandler($exception, $request);
} catch (\Exception $handlerError) {
} catch (Exception $handlerError) {
}

if ($this->getShowDebugInformation()) {
Expand All @@ -172,7 +178,7 @@ private function exceptionToResponse(\Exception $exception, RestRequestInterface
* @param $exception
* @return array
*/
private function getDebugTrace(\Exception $exception)
private function getDebugTrace(Exception $exception)
{
return array_map(
function ($step) {
Expand All @@ -181,7 +187,6 @@ function ($step) {
return $step['class'] . $step['type'] . $step['function'] . $arguments;
}
if (isset($step['function'])) {

return $step['function'] . $arguments;
}

Expand All @@ -195,7 +200,7 @@ function ($step) {
* @param $exception
* @return array
*/
private function getDebugDetails(\Exception $exception)
private function getDebugDetails(Exception $exception)
{
return [
'error' => sprintf(
Expand All @@ -208,11 +213,60 @@ private function getDebugDetails(\Exception $exception)
];
}

/**
* Handle cases where no matching Route was found
*
* If debugging information is allowed for the client and alternative Route suggestions where provided by the
* Router, they will be sent as additional header
*
* @param NotFoundException $result
* @param RestRequestInterface $request
* @return ResponseInterface
*/
private function notFoundToResponse(NotFoundException $result, RestRequestInterface $request): ResponseInterface
{
$response = $this->responseFactory->createErrorResponse(
$result->getMessage() ?: null,
404,
$request
);
if (!$this->getShowDebugInformation()) {
return $response;
}

if (!$result->getAlternativeRoutes()) {
return $response->withHeader(Header::CUNDD_REST_ROUTER_ALTERNATIVE_ROUTES, 'no suggestions');
}

$alternativePatterns = implode(
', ',
array_map(
function (RouteInterface $r) {
return $r->getPattern();
},
$result->getAlternativeRoutes()
)
);

$alternativePatterns = str_replace(["\r", "\n"], '', $alternativePatterns);
$alternativePatterns = (string)preg_replace('/[^\x20-\x7e]/', '', $alternativePatterns);

$maxByteLength = 1024;
if (strlen($alternativePatterns) > $maxByteLength) {
$alternativePatterns = substr($alternativePatterns, 0, $maxByteLength - 3) . '...';
}

return $response->withHeader(
Header::CUNDD_REST_ROUTER_ALTERNATIVE_ROUTES,
$alternativePatterns
);
}

/**
* @return bool
*/
private function getShowDebugInformation()
{
return ErrorHandler::getShowDebugInformation();
return DebugUtility::allowDebugInformation();
}
}
Loading

0 comments on commit ab26785

Please sign in to comment.