From ab7328b40b592ac84df021b575145ea08c649e0e Mon Sep 17 00:00:00 2001 From: Juan Pablo Vial Date: Wed, 25 Jun 2025 18:07:08 -0400 Subject: [PATCH] FastCGI --- cli/.env.sample | 14 ++-- cli/common/Alias/Application.php | 1 + cli/composer.json | 1 + cli/setup/setups/client.php | 13 +++- cli/setup/setups/services.php | 4 +- cli/src/Command/Queue.php | 35 ++++++---- cli/src/Exception/Client/FastCGI.php | 55 ++++++++++++++++ cli/src/Service/FastCGI.php | 99 ++++++++++++++++++++++++++++ 8 files changed, 202 insertions(+), 20 deletions(-) create mode 100644 cli/src/Exception/Client/FastCGI.php create mode 100644 cli/src/Service/FastCGI.php diff --git a/cli/.env.sample b/cli/.env.sample index 4a4961f..1d7f883 100644 --- a/cli/.env.sample +++ b/cli/.env.sample @@ -1,9 +1,15 @@ #ENVIRONMENT= -TZ= - +TZ=America/Santiago +#ENVIRONMENT= APP_NAME=incoviba_cli API_URL=http://proxy/api +#API_USERNAME= +#API_PASSWORD= -API_USERNAME= -API_PASSWORD= +#REDIS_HOST=redis +#REDIS_PORT=6379 + +#SOCKET_HOST=web +#SOCKET_PORT=9000 +#SOCKET_ROOT=/code/public/index.php diff --git a/cli/common/Alias/Application.php b/cli/common/Alias/Application.php index 2b2308f..98ae0a1 100644 --- a/cli/common/Alias/Application.php +++ b/cli/common/Alias/Application.php @@ -10,6 +10,7 @@ class Application extends Console\Application { public function __construct(protected ContainerInterface $container, string $name = 'UNKNOWN', string $version = 'UNKNOWN') { + $name = 'incoviba_cli'; if ($this->container->has('APP_NAME')) { $name = $this->container->get('APP_NAME'); } diff --git a/cli/composer.json b/cli/composer.json index 10f9450..a54ff9b 100644 --- a/cli/composer.json +++ b/cli/composer.json @@ -4,6 +4,7 @@ "require": { "dragonmantank/cron-expression": "^3.4", "guzzlehttp/guzzle": "^7.8", + "hollodotme/fast-cgi-client": "^3.1", "monolog/monolog": "^3.5", "php-di/php-di": "^7.0", "predis/predis": "^3.0", diff --git a/cli/setup/setups/client.php b/cli/setup/setups/client.php index 42b3da5..2d84b25 100644 --- a/cli/setup/setups/client.php +++ b/cli/setup/setups/client.php @@ -4,7 +4,7 @@ use Psr\Container\ContainerInterface; return [ Incoviba\Service\Login::class => function(ContainerInterface $container) { $client = new GuzzleHttp\Client([ - 'base_uri' => $container->get('API_URL'), + 'base_uri' => $container->has('API_URL') ? $container->get('API_URL') : 'http://proxy/api', 'headers' => [ 'Authorization' => [ 'Bearer ' . md5($container->get('API_KEY')) @@ -36,8 +36,15 @@ return [ }, Psr\Http\Client\ClientInterface::class => function(ContainerInterface $container) { return new GuzzleHttp\Client([ - 'base_uri' => $container->get('API_URL'), + 'base_uri' => $container->has('API_URL') ? $container->get('API_URL') : 'http://proxy/api', 'handler' => $container->get(GuzzleHttp\HandlerStack::class), ]); - } + }, + Incoviba\Service\FastCGI::class => function(ContainerInterface $container) { + return new Incoviba\Service\FastCGI( + $container->has('SOCKET_HOST') ? $container->get('SOCKET_HOST') : 'web', + $container->has('SOCKET_PORT') ? $container->get('SOCKET_PORT') : 9000, + $container->has('SOCKET_ROOT') ? $container->get('SOCKET_ROOT') : '/code/public/index.php' + ); + }, ]; diff --git a/cli/setup/setups/services.php b/cli/setup/setups/services.php index dc19355..2a9bc57 100644 --- a/cli/setup/setups/services.php +++ b/cli/setup/setups/services.php @@ -5,8 +5,8 @@ return [ Predis\ClientInterface::class => function(ContainerInterface $container) { $options = [ 'scheme' => 'tcp', - 'host' => $container->get('REDIS_HOST'), - 'port' => $container->get('REDIS_PORT') + 'host' => $container->has('REDIS_HOST') ? $container->get('REDIS_HOST') : 'redis', + 'port' => $container->has('REDIS_PORT') ? $container->get('REDIS_PORT') : 6379 ]; if ($container->has('REDIS_USER')) { $options['username'] = $container->get('REDIS_USER'); diff --git a/cli/src/Command/Queue.php b/cli/src/Command/Queue.php index 5679547..a643693 100644 --- a/cli/src/Command/Queue.php +++ b/cli/src/Command/Queue.php @@ -7,8 +7,10 @@ use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Client\ClientInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Console; +use Incoviba\Service\FastCGI; use Incoviba\Service\Job; use Incoviba\Common\Alias\Command; +use Incoviba\Exception\Client\FastCGI as FastCGIException; #[Console\Attribute\AsCommand( name: 'queue', @@ -16,10 +18,11 @@ use Incoviba\Common\Alias\Command; )] class Queue extends Command { - public function __construct(ClientInterface $client, LoggerInterface $logger, - protected Job $jobService, + public function __construct(ClientInterface $client, LoggerInterface $logger, + protected Job $jobService, protected DateTimeZone $timezone, - ?string $name = null) + protected FastCGI $fastcgi, + ?string $name = null) { parent::__construct($client, $logger, $name); } @@ -55,20 +58,30 @@ class Queue extends Command $errors ++; } } + $responses = $this->fastcgi->awaitResponses(); + foreach ($responses as $response) { + if ((int) floor($response->getStatusCode() / 100) !== 2) { + $this->logger->error("Error running job", [ + 'status' => $response->getStatusCode(), + 'body' => $response->getBody()->getContents(), + 'headers' => $response->getHeaders(), + ]); + $errors ++; + } + } return $errors === 0 ? Console\Command\Command::SUCCESS : Console\Command\Command::FAILURE; } protected function runJob(Console\Output\OutputInterface $output, int $job_id): int { $uri = "/api/queue/run/{$job_id}"; $output->writeln("GET {$uri}"); - try { - $response = $this->client->get($uri); - } catch (ClientExceptionInterface $exception) { - $this->logger->error($exception); - return Console\Command\Command::FAILURE; - } - $output->writeln("Response Code: {$response->getStatusCode()}"); - return ((int) floor($response->getStatusCode() / 100) === 2) ? Console\Command\Command::SUCCESS : Console\Command\Command::FAILURE; + try { + $this->fastcgi->get($uri); + return self::SUCCESS; + } catch (FastCGIException $exception) { + $this->logger->error($exception->getMessage(), ['uri' => $uri, 'exception' =>$exception]); + return self::FAILURE; + } } } diff --git a/cli/src/Exception/Client/FastCGI.php b/cli/src/Exception/Client/FastCGI.php new file mode 100644 index 0000000..8ee3631 --- /dev/null +++ b/cli/src/Exception/Client/FastCGI.php @@ -0,0 +1,55 @@ +previous !== null) { + $message .= ": {$this->previous->getMessage()}"; + } + return $message; + } + + public function getCode() + { + return $this->previous?->getCode() ?? 500; + } + + public function getFile(): string + { + return $this->previous?->getFile() ?? ''; + } + + public function getLine(): int + { + return $this->previous?->getLine() ?? 0; + } + + public function getTrace(): array + { + return $this->previous?->getTrace() ?? []; + } + + public function getTraceAsString(): string + { + return $this->previous?->getTraceAsString() ?? ''; + } + + public function getPrevious(): ?Throwable + { + return $this->previous; + } + + public function __toString() + { + return $this->getMessage(); + } +} diff --git a/cli/src/Service/FastCGI.php b/cli/src/Service/FastCGI.php new file mode 100644 index 0000000..6cd75c1 --- /dev/null +++ b/cli/src/Service/FastCGI.php @@ -0,0 +1,99 @@ +client = new FCGI\Client(); + } + + public LoggerInterface $logger { + get { + return $this->logger; + } + set(LoggerInterface $logger) { + $this->logger = $logger; + } + } + + protected FCGI\Client $client; + protected FCGI\Interfaces\ConfiguresSocketConnection $socket; + public function connect(): self + { + $this->socket = new FCGI\SocketConnections\NetworkSocket($this->hostname, $this->port, $this->connectionTimeout, $this->readTimeout); + return $this; + } + + protected array $socketIds = []; + + /** + * @throws FastCGIException + */ + public function sendRequest(FCGI\Interfaces\ProvidesRequestData $request): self + { + if (!isset($this->socket)) { + $this->connect(); + } + try { + $this->socketIds []= $this->client->sendAsyncRequest($this->socket, $request); + } catch (FCGI\Exceptions\FastCGIClientException $exception) { + $this->logger->error($exception->getMessage()); + throw new FastCGIException($exception); + } + return $this; + } + + /** + * @return array + */ + public function awaitResponses(): array + { + $responses = []; + while ($this->client->hasUnhandledResponses()) { + try { + $readyResponses = $this->client->readReadyResponses(3000); + } catch (FCGI\Exceptions\FastCGIClientException $exception) { + $this->logger->error($exception->getMessage()); + continue; + } + foreach ($readyResponses as $response) { + $responses []= $response; + } + } + return $responses; + } + + /** + * @param string $uri + * @return FastCGI + * @throws FastCGIException + */ + public function get(string $uri): self + { + $request = new FCGI\Requests\GetRequest($this->documentRoot, ''); + $request->setRequestUri($uri); + return $this->sendRequest($request); + } + + /** + * @param string $uri + * @param ?array $data + * @return FastCGI + * @throws FastCGIException + */ + public function post(string $uri, ?array $data): self + { + $content = new FCGI\RequestContents\JsonData($data ?? []); + $request = FCGI\Requests\PostRequest::newWithRequestContent($this->documentRoot, $content); + $request->setRequestUri($uri); + return $this->sendRequest($request); + } +}