From dc98f70a06d856d162f75868ea2f79732a993fc3 Mon Sep 17 00:00:00 2001 From: Christopher Vagnetoft Date: Wed, 13 Mar 2024 01:54:28 +0100 Subject: [PATCH] Request limiting, config improvements * Added middleware to limit concurrent request and request body size * The default configuration now has the defaults --- bin/mercureactd | 10 +--------- mercureactd.conf.dist | 7 +++++++ src/Configuration.php | 13 ++++++++++++- src/Http/Server.php | 14 +++++++++++++- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/bin/mercureactd b/bin/mercureactd index 96d797e..cc1af68 100755 --- a/bin/mercureactd +++ b/bin/mercureactd @@ -50,15 +50,12 @@ if (isset($opts['C'])) { file_put_contents($file, <<setListenAddress('127.0.0.1:8888') - ->setAllowOriginHeader("*") - ->setContentSecurityPolicyHeader("default-src * 'self' http: 'unsafe-eval' 'unsafe-inline'; connect-src * 'self'") - ->setAllowAnonymousSubscribe(true) - ->setJwtSecret("!ChangeThisMercureHubJWTSecretKey!"); + $config = Configuration::createDefault(); } $verbose = isset($opts['v']); diff --git a/mercureactd.conf.dist b/mercureactd.conf.dist index 96557d4..e73df54 100644 --- a/mercureactd.conf.dist +++ b/mercureactd.conf.dist @@ -19,6 +19,13 @@ server: local_cert: ~ local_pk: ~ passphrase: ~ + + # Limits + # max_concurent (int) - how many requests that can be handled at once + # max_request_body (int) - max request body size + limits: + max_concurrent: 20 + max_request_body: 102400 publish: # Assign a UUID to published messages even if one is already set in the message diff --git a/src/Configuration.php b/src/Configuration.php index b54d859..0523882 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -15,7 +15,18 @@ class Configuration public static function createDefault(): Configuration { - return new Configuration(); + return new Configuration([ + "publish.overwrite_ids" => false, + "publish.reject_duplicates" => false, + "server.address" => "127.0.0.1:9000", + "server.enable_api" => true, + "server.limits.max_concurrent" => 100, + "server.limits.max_request_body" => 102400, + "server.cors.allow_origin" => "*", + "server.cors.csp" => "default-src * 'self' http: 'unsafe-eval' 'unsafe-inline'; connect-src * 'self'", + "subscribe.allow_anonymous" => true, + "security.jwt_secret" => "!ChangeThisMercureHubJWTSecretKey!", + ]); } public static function fromFile(string $file): Configuration diff --git a/src/Http/Server.php b/src/Http/Server.php index 117015b..177db9d 100644 --- a/src/Http/Server.php +++ b/src/Http/Server.php @@ -17,6 +17,8 @@ use Psr\Log\NullLogger; use React\EventLoop\Loop; use React\EventLoop\LoopInterface; use React\Http\HttpServer; +use React\Http\Middleware\LimitConcurrentRequestsMiddleware; +use React\Http\Middleware\RequestBodyBufferMiddleware; use React\Socket\SecureServer; use React\Socket\ServerInterface; use SplObjectStorage; @@ -90,7 +92,15 @@ class Server */ private function createHttpServer(): HttpServer { - $stack = [ + $stack = []; + + $maxConcurrent = $this->config->get("server.limits.max_concurrent", 100); + $maxRequestBody = $this->config->get("server.limits.max_request_body", 102400); + + $stack[] = new LimitConcurrentRequestsMiddleware($maxConcurrent); + $stack[] = new RequestBodyBufferMiddleware($maxRequestBody); + + $stack = [ ...$stack, $this->responseMiddleware = new ResponseMiddleware( config: $this->config, logger: $this->logger->withName("http"), @@ -99,6 +109,7 @@ class Server config: $this->config ), ]; + if ($this->config->getEnableWebSockets()) { $stack = [ ...$stack, $this->webSocketHandler = new WebSocketHandler( @@ -109,6 +120,7 @@ class Server ]; $this->logger->warning("The WebSocket support is incomplete and insecure, but enabling it as requested."); } + $stack = [ ...$stack, $this->mercureHandler = new MercureHandler( config: $this->config,