From 36a05c4b8daffe26609bf92cd3f1db33320ce60b Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 20 Dec 2023 12:19:20 +0000 Subject: [PATCH 1/5] Adds support for multiple FrankenPHP Octane processes --- src/Commands/StartFrankenPhpCommand.php | 14 ++++++++++ src/Commands/stubs/Caddyfile | 2 ++ src/FrankenPhp/ServerProcessInspector.php | 33 +++++++++++++++++------ 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/Commands/StartFrankenPhpCommand.php b/src/Commands/StartFrankenPhpCommand.php index 041559b2c..3deded062 100644 --- a/src/Commands/StartFrankenPhpCommand.php +++ b/src/Commands/StartFrankenPhpCommand.php @@ -87,6 +87,7 @@ public function handle(ServerProcessInspector $inspector, ServerStateFile $serve 'APP_PUBLIC_PATH' => public_path(), 'LARAVEL_OCTANE' => 1, 'MAX_REQUESTS' => $this->option('max-requests'), + 'CADDY_SERVER_ADMIN_PORT' => $this->adminPort(), 'CADDY_SERVER_LOG_LEVEL' => $this->option('log-level') ?: (app()->environment('local') ? 'INFO' : 'WARN'), 'CADDY_SERVER_LOGGER' => 'json', 'CADDY_SERVER_SERVER_NAME' => $serverName, @@ -149,6 +150,18 @@ protected function buildMercureConfig() return "$config\n\t\t}"; } + /** + * Get the port the admin URL should be available on. + * + * @return int + */ + protected function adminPort() + { + $defaultPort = 2019; + + return $defaultPort + ($this->getPort() - 8000); + } + /** * Write the FrankenPHP server state file. * @@ -161,6 +174,7 @@ protected function writeServerStateFile( 'appName' => config('app.name', 'Laravel'), 'host' => $this->getHost(), 'port' => $this->getPort(), + 'adminPort' => $this->adminPort(), 'workers' => $this->workerCount(), 'maxRequests' => $this->option('max-requests'), 'octaneConfig' => config('octane'), diff --git a/src/Commands/stubs/Caddyfile b/src/Commands/stubs/Caddyfile index bb25bfd80..15cc81021 100644 --- a/src/Commands/stubs/Caddyfile +++ b/src/Commands/stubs/Caddyfile @@ -1,6 +1,8 @@ { {$CADDY_GLOBAL_OPTIONS} + admin localhost:{$CADDY_SERVER_ADMIN_PORT} + frankenphp { worker {$APP_PUBLIC_PATH}/frankenphp-worker.php {$CADDY_SERVER_WORKER_COUNT} } diff --git a/src/FrankenPhp/ServerProcessInspector.php b/src/FrankenPhp/ServerProcessInspector.php index e3d039cd0..528c71769 100644 --- a/src/FrankenPhp/ServerProcessInspector.php +++ b/src/FrankenPhp/ServerProcessInspector.php @@ -9,10 +9,9 @@ class ServerProcessInspector implements ServerProcessInspectorContract { - private const ADMIN_URL = 'http://localhost:2019'; - - private const FRANKENPHP_CONFIG_URL = self::ADMIN_URL.'/config/apps/frankenphp'; - + /** + * Create a new server process inspector instance. + */ public function __construct( protected ServerStateFile $serverStateFile, ) { @@ -24,7 +23,7 @@ public function __construct( public function serverIsRunning(): bool { try { - return Http::get(self::FRANKENPHP_CONFIG_URL)->successful(); + return Http::get($this->adminConfigUrl())->successful(); } catch (ConnectionException $_) { return false; } @@ -36,9 +35,9 @@ public function serverIsRunning(): bool public function reloadServer(): void { try { - Http::withBody(Http::get(self::FRANKENPHP_CONFIG_URL)->body(), 'application/json') + Http::withBody(Http::get($this->adminConfigUrl())->body(), 'application/json') ->withHeaders(['Cache-Control' => 'must-revalidate']) - ->patch(self::FRANKENPHP_CONFIG_URL); + ->patch($this->adminConfigUrl()); } catch (ConnectionException $_) { // } @@ -50,9 +49,27 @@ public function reloadServer(): void public function stopServer(): bool { try { - return Http::post(self::ADMIN_URL.'/stop')->successful(); + return Http::post($this->adminUrl().'/stop')->successful(); } catch (ConnectionException $_) { return false; } } + + /** + * Get the URL to the FrankenPHP admin panel. + */ + protected function adminUrl(): string + { + $adminPort = $this->serverStateFile->read()['state']['adminPort'] ?? 2019; + + return "http://localhost:{$adminPort}"; + } + + /** + * Get the URL to the FrankenPHP admin panel's config endpoint. + */ + protected function adminConfigUrl(): string + { + return "{$this->adminUrl()}/config/apps/frankenphp"; + } } From 297cc94867c85e8b88f97dc85a97c5b3368c0b66 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 20 Dec 2023 12:38:45 +0000 Subject: [PATCH 2/5] Improves `serverIsRunning` --- src/FrankenPhp/ServerProcessInspector.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/FrankenPhp/ServerProcessInspector.php b/src/FrankenPhp/ServerProcessInspector.php index 528c71769..798a1151a 100644 --- a/src/FrankenPhp/ServerProcessInspector.php +++ b/src/FrankenPhp/ServerProcessInspector.php @@ -22,6 +22,10 @@ public function __construct( */ public function serverIsRunning(): bool { + if (is_null($this->serverStateFile->read()['masterProcessId'] ?? null)) { + return false; + } + try { return Http::get($this->adminConfigUrl())->successful(); } catch (ConnectionException $_) { From 123603bb390ca50fb44dff67cbe0ce778b5db581 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 20 Dec 2023 13:04:40 +0000 Subject: [PATCH 3/5] Checks if port is already in use --- src/Commands/StartFrankenPhpCommand.php | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Commands/StartFrankenPhpCommand.php b/src/Commands/StartFrankenPhpCommand.php index 3deded062..2095e0b6b 100644 --- a/src/Commands/StartFrankenPhpCommand.php +++ b/src/Commands/StartFrankenPhpCommand.php @@ -73,6 +73,12 @@ public function handle(ServerProcessInspector $inspector, ServerStateFile $serve $host = $this->option('host'); $port = $this->getPort(); + if ($this->isHostAvailable() === false) { + $this->error("Unable to start server. Port {$port} is already in use."); + + return 1; + } + $serverName = $this->option('https') ? "https://$host:$port" : "http://:$port"; @@ -102,6 +108,26 @@ public function handle(ServerProcessInspector $inspector, ServerStateFile $serve return $this->runServer($server, $inspector, 'frankenphp'); } + /** + * Checks if the host is available. + * + * @return bool + */ + protected function isHostAvailable() + { + $host = $this->getHost(); + $port = $this->getPort(); + + if ($host !== '127.0.0.1') { + return true; + } + + $connection = @fsockopen($host, $port); + $isAvailable = ! is_resource($connection); + + return tap($isAvailable, fn () => $isAvailable || @fclose($connection)); + } + /** * Get the path to the FrankenPHP configuration file. * From c821e6b429277131c9ff8f4f7f530e258d6e4c5a Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 20 Dec 2023 13:14:58 +0000 Subject: [PATCH 4/5] Improves checking if ports are available --- src/Commands/StartFrankenPhpCommand.php | 31 ++++++++++++++----------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Commands/StartFrankenPhpCommand.php b/src/Commands/StartFrankenPhpCommand.php index 2095e0b6b..5e2603b3f 100644 --- a/src/Commands/StartFrankenPhpCommand.php +++ b/src/Commands/StartFrankenPhpCommand.php @@ -55,6 +55,7 @@ class StartFrankenPhpCommand extends Command implements SignalableCommandInterfa public function handle(ServerProcessInspector $inspector, ServerStateFile $serverStateFile) { $this->ensureFrankenPhpWorkerIsInstalled(); + $this->ensureHostsAreAvailable(); $frankenphpBinary = $this->ensureFrankenPhpBinaryIsInstalled(); @@ -73,12 +74,6 @@ public function handle(ServerProcessInspector $inspector, ServerStateFile $serve $host = $this->option('host'); $port = $this->getPort(); - if ($this->isHostAvailable() === false) { - $this->error("Unable to start server. Port {$port} is already in use."); - - return 1; - } - $serverName = $this->option('https') ? "https://$host:$port" : "http://:$port"; @@ -109,23 +104,31 @@ public function handle(ServerProcessInspector $inspector, ServerStateFile $serve } /** - * Checks if the host is available. + * Ensures server and admin localhost ports are available. * - * @return bool + * @return void */ - protected function isHostAvailable() + protected function ensureHostsAreAvailable() { $host = $this->getHost(); - $port = $this->getPort(); + + $serverPort = $this->getPort(); + $adminPort = $this->adminPort(); if ($host !== '127.0.0.1') { - return true; + return; } - $connection = @fsockopen($host, $port); - $isAvailable = ! is_resource($connection); + foreach ([$serverPort, $adminPort] as $port) { + $connection = @fsockopen($host, $port); + $isAvailable = ! is_resource($connection); + + if (! $isAvailable) { + @fclose($connection); - return tap($isAvailable, fn () => $isAvailable || @fclose($connection)); + throw new InvalidArgumentException("Unable to start server. Port {$port} is already in use."); + } + } } /** From cb393b64cfc192da9acd4d7b16de5ad949b160de Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 20 Dec 2023 08:34:42 -0600 Subject: [PATCH 5/5] formatting --- src/Commands/StartFrankenPhpCommand.php | 26 +++++++++++------------ src/FrankenPhp/ServerProcessInspector.php | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Commands/StartFrankenPhpCommand.php b/src/Commands/StartFrankenPhpCommand.php index 5e2603b3f..37bd0b257 100644 --- a/src/Commands/StartFrankenPhpCommand.php +++ b/src/Commands/StartFrankenPhpCommand.php @@ -104,7 +104,7 @@ public function handle(ServerProcessInspector $inspector, ServerStateFile $serve } /** - * Ensures server and admin localhost ports are available. + * Ensures the server and admin localhost ports are available. * * @return void */ @@ -179,18 +179,6 @@ protected function buildMercureConfig() return "$config\n\t\t}"; } - /** - * Get the port the admin URL should be available on. - * - * @return int - */ - protected function adminPort() - { - $defaultPort = 2019; - - return $defaultPort + ($this->getPort() - 8000); - } - /** * Write the FrankenPHP server state file. * @@ -210,6 +198,18 @@ protected function writeServerStateFile( ]); } + /** + * Get the port the admin URL should be available on. + * + * @return int + */ + protected function adminPort() + { + $defaultPort = 2019; + + return $defaultPort + ($this->getPort() - 8000); + } + /** * Get the number of workers that should be started. * diff --git a/src/FrankenPhp/ServerProcessInspector.php b/src/FrankenPhp/ServerProcessInspector.php index 798a1151a..8b2273c3d 100644 --- a/src/FrankenPhp/ServerProcessInspector.php +++ b/src/FrankenPhp/ServerProcessInspector.php @@ -70,7 +70,7 @@ protected function adminUrl(): string } /** - * Get the URL to the FrankenPHP admin panel's config endpoint. + * Get the URL to the FrankenPHP admin panel's configuration endpoint. */ protected function adminConfigUrl(): string {