diff --git a/.envDefault b/.envDefault
index 8290d4f..1fa763a 100644
--- a/.envDefault
+++ b/.envDefault
@@ -1,3 +1,5 @@
displayError=true
logErrors=true
logErrorDetails=true
+RequestLimit=1
+RequestLimitTimeout=10000
diff --git a/app/ErrorHandler/CustomHtmlErrorRenderer.php b/app/ErrorHandler/CustomHtmlErrorRenderer.php
new file mode 100644
index 0000000..c2238aa
--- /dev/null
+++ b/app/ErrorHandler/CustomHtmlErrorRenderer.php
@@ -0,0 +1,108 @@
+The application could not run because of the following error:
';
+ $html .= 'Details
';
+ $html .= $this->renderExceptionFragment($exception);
+ } else {
+ $html = "{$exception->getMessage()}
{$this->getErrorDescription($exception)}
";
+ }
+
+ return $this->renderHtmlBody($this->getErrorTitle($exception), $html);
+ }
+
+ /**
+ * @param Throwable $exception
+ * @return string
+ */
+ private function renderExceptionFragment(Throwable $exception): string
+ {
+ $html = sprintf('Type: %s
', get_class($exception));
+
+ $code = $exception->getCode();
+ if ($code !== null) {
+ $html .= sprintf('Code: %s
', $code);
+ }
+
+ $message = $exception->getMessage();
+ if ($message !== null) {
+ $html .= sprintf('Message: %s
', htmlentities($message));
+ }
+
+ $file = $exception->getFile();
+ if ($file !== null) {
+ $html .= sprintf('File: %s
', $file);
+ }
+
+ $line = $exception->getLine();
+ if ($line !== null) {
+ $html .= sprintf('Line: %s
', $line);
+ }
+
+ $trace = $exception->getTraceAsString();
+ if ($trace !== null) {
+ $html .= 'Trace
';
+ $html .= sprintf('%s
', htmlentities($trace));
+ }
+
+ return $html;
+ }
+
+ /**
+ * @param string $title
+ * @param string $html
+ * @return string
+ */
+ public function renderHtmlBody(string $title = '', string $html = ''): string
+ {
+ return sprintf(
+ '' .
+ ' ' .
+ " " .
+ ' %s' .
+ ' ' .
+ ' ' .
+ ' ' .
+ ' %s
' .
+ ' %s
' .
+ ' ' .
+ '',
+ $title,
+ $title,
+ $html
+ );
+ }
+}
diff --git a/app/Kernel.php b/app/Kernel.php
index 58726fa..afda506 100644
--- a/app/Kernel.php
+++ b/app/Kernel.php
@@ -2,6 +2,7 @@
namespace App;
+use App\ErrorHandler\CustomHtmlErrorRenderer;
use App\Middlewares\ThrottleMiddleware;
use Dotenv\Dotenv;
use Dotenv\Exception\InvalidPathException;
@@ -51,6 +52,8 @@ class Kernel
env('logErrors', false),
env('logErrorDetails', false)
);
+ $errorHandler = $errorMiddleware->getDefaultErrorHandler();
+ $errorHandler->registerErrorRenderer('text/html', CustomHtmlErrorRenderer::class);
}
private static function loadEnv()
@@ -69,7 +72,7 @@ class Kernel
public static function env(string $key, $default = null)
{
- $value = getenv($key);
+ $value = self::$env[$key] ?? $default;
if ($value === false) {
return $default;
@@ -93,10 +96,6 @@ class Kernel
return;
}
- // if (Str::startsWith($value, '"') && Str::endsWith($value, '"')) {
- // return substr($value, 1, -1);
- // }
-
return $value;
}
diff --git a/app/Middlewares/ThrottleMiddleware.php b/app/Middlewares/ThrottleMiddleware.php
index 0b12206..5dc8384 100644
--- a/app/Middlewares/ThrottleMiddleware.php
+++ b/app/Middlewares/ThrottleMiddleware.php
@@ -5,6 +5,7 @@ namespace App\Middlewares;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use GuzzleHttp\Psr7\Response;
+use Slim\Exception\HttpForbiddenException;
use Stiphle\Throttle\LeakyBucket;
class ThrottleMiddleware
@@ -23,8 +24,12 @@ class ThrottleMiddleware
//! Enable APC in Laragon https://forum.laragon.org/topic/1245/regarding-caching-plugin-availability/4
$storage = new \Stiphle\Storage\Apcu();
$throttle->setStorage($storage);
- // limit to 1 per second
- $throttle->throttle('api.request', 1, 10000);
+ // limit to 1 per second by default
+ $estimate = $throttle->getEstimate('api.request', env('RequestLimit', 1), env('RequestLimitTimeout', 1000));
+ if ($estimate > 1000) {
+ throw new HttpForbiddenException($request, 'Too many requests');
+ }
+ $throttle->throttle('api.request', env('RequestLimit', 1), env('RequestLimitTimeout', 1000));
return $handler->handle($request);
}
}