Skip to content
This repository has been archived by the owner on Jan 17, 2022. It is now read-only.

Commit

Permalink
refactor(swoole): Simplify booting services at runtime
Browse files Browse the repository at this point in the history
Swoole server can affect configuration, and it has to be created at runtime due to host/port
binding, after successful server boot, some services also can be reconfigured to allow simple
configuration substitution via cli
  • Loading branch information
k911 committed Sep 23, 2018
1 parent c5a0c27 commit 5187396
Show file tree
Hide file tree
Showing 24 changed files with 741 additions and 300 deletions.
10 changes: 1 addition & 9 deletions Bridge/Doctrine/ORM/EntityManagerHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace App\Bundle\SwooleBundle\Bridge\Doctrine\ORM;

use App\Bundle\SwooleBundle\Server\RequestHandlerInterface;
use App\Bundle\SwooleBundle\Server\RequestHandler\RequestHandlerInterface;
use Doctrine\ORM\EntityManagerInterface;
use Swoole\Http\Request;
use Swoole\Http\Response;
Expand All @@ -22,14 +22,6 @@ public function __construct(RequestHandlerInterface $decorated, EntityManagerInt
$this->connection = $entityManager->getConnection();
}

/**
* {@inheritdoc}
*/
public function boot(array $runtimeConfiguration = []): void
{
$this->decorated->boot($runtimeConfiguration);
}

/**
* {@inheritdoc}
*/
Expand Down
27 changes: 27 additions & 0 deletions Bridge/Symfony/HttpFoundation/SetRequestRuntimeConfiguration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace App\Bundle\SwooleBundle\Bridge\Symfony\HttpFoundation;

use App\Bundle\SwooleBundle\Server\Runtime\BootableInterface;
use Symfony\Component\HttpFoundation\Request;

/**
* Sets symfony's request runtime configuration.
*/
final class SetRequestRuntimeConfiguration implements BootableInterface
{
/**
* {@inheritdoc}
*/
public function boot(array $runtimeConfiguration = []): void
{
if (\array_key_exists('trustedHosts', $runtimeConfiguration)) {
Request::setTrustedHosts($runtimeConfiguration['trustedHosts']);
}
if (\array_key_exists('trustedProxies', $runtimeConfiguration)) {
Request::setTrustedProxies($runtimeConfiguration['trustedProxies'], $runtimeConfiguration['trustedHeaderSet'] ?? Request::HEADER_X_FORWARDED_ALL);
}
}
}
16 changes: 6 additions & 10 deletions Bridge/Symfony/HttpFoundation/TrustAllProxiesRequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,31 @@

namespace App\Bundle\SwooleBundle\Bridge\Symfony\HttpFoundation;

use App\Bundle\SwooleBundle\Server\RequestHandlerInterface;
use App\Bundle\SwooleBundle\Server\RequestHandler\RequestHandlerInterface;
use App\Bundle\SwooleBundle\Server\Runtime\BootableInterface;
use Swoole\Http\Request as SwooleRequest;
use Swoole\Http\Response as SwooleResponse;
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;

final class TrustAllProxiesRequestHandler implements RequestHandlerInterface
final class TrustAllProxiesRequestHandler implements RequestHandlerInterface, BootableInterface
{
private $decorated;
private $trustAllProxies;

public function __construct(RequestHandlerInterface $decorated)
public function __construct(RequestHandlerInterface $decorated, bool $trustAllProxies = false)
{
$this->decorated = $decorated;
$this->trustAllProxies = false;
$this->trustAllProxies = $trustAllProxies;
}

/**
* {@inheritdoc}
*/
public function boot(array $runtimeConfiguration = []): void
{
if (isset($runtimeConfiguration['trustedProxies']) && \in_array('*', $runtimeConfiguration['trustedProxies'], true)) {
if (isset($runtimeConfiguration['trustAllProxies']) && true === $runtimeConfiguration['trustAllProxies']) {
$this->trustAllProxies = true;
$runtimeConfiguration['trustedProxies'] = \array_filter($runtimeConfiguration['trustedProxies'], function (string $trustedProxy) {
return '*' !== $trustedProxy;
});
}

$this->decorated->boot($runtimeConfiguration);
}

/**
Expand Down
10 changes: 1 addition & 9 deletions Bridge/Symfony/HttpKernel/DebugHttpKernelRequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace App\Bundle\SwooleBundle\Bridge\Symfony\HttpKernel;

use App\Bundle\SwooleBundle\Functions\ServerUtils;
use App\Bundle\SwooleBundle\Server\RequestHandlerInterface;
use App\Bundle\SwooleBundle\Server\RequestHandler\RequestHandlerInterface;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Symfony\Component\DependencyInjection\ContainerInterface;
Expand All @@ -24,14 +24,6 @@ public function __construct(RequestHandlerInterface $decorated, KernelInterface
$this->kernel = $kernel;
}

/**
* {@inheritdoc}
*/
public function boot(array $runtimeConfiguration = []): void
{
$this->decorated->boot($runtimeConfiguration);
}

/**
* {@inheritdoc}
*/
Expand Down
13 changes: 3 additions & 10 deletions Bridge/Symfony/HttpKernel/HttpKernelRequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

use App\Bundle\SwooleBundle\Bridge\Symfony\HttpFoundation\RequestFactoryInterface;
use App\Bundle\SwooleBundle\Bridge\Symfony\HttpFoundation\ResponseProcessorInterface;
use App\Bundle\SwooleBundle\Server\RequestHandlerInterface;
use App\Bundle\SwooleBundle\Server\RequestHandler\RequestHandlerInterface;
use App\Bundle\SwooleBundle\Server\Runtime\BootableInterface;
use Swoole\Http\Request as SwooleRequest;
use Swoole\Http\Response as SwooleResponse;
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;

final class HttpKernelRequestHandler implements RequestHandlerInterface
final class HttpKernelRequestHandler implements RequestHandlerInterface, BootableInterface
{
private $kernel;
private $requestFactory;
Expand All @@ -31,13 +31,6 @@ public function __construct(KernelInterface $kernel, RequestFactoryInterface $re
*/
public function boot(array $runtimeConfiguration = []): void
{
if (\array_key_exists('trustedHosts', $runtimeConfiguration)) {
SymfonyRequest::setTrustedHosts($runtimeConfiguration['trustedHosts']);
}
if (\array_key_exists('trustedProxies', $runtimeConfiguration)) {
SymfonyRequest::setTrustedProxies($runtimeConfiguration['trustedProxies'], $runtimeConfiguration['trustedHeaderSet'] ?? SymfonyRequest::HEADER_X_FORWARDED_ALL);
}

$this->kernel->boot();
}

Expand Down
135 changes: 89 additions & 46 deletions Command/ServerProfileCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
use App\Bundle\SwooleBundle\Functions\ServerUtils;
use App\Bundle\SwooleBundle\Server\HttpServer;
use App\Bundle\SwooleBundle\Server\HttpServerConfiguration;
use App\Bundle\SwooleBundle\Server\RequestHandlerInterface;
use App\Bundle\SwooleBundle\Server\HttpServerFactory;
use App\Bundle\SwooleBundle\Server\Runtime\BootManager;
use Composer\XdebugHandler\XdebugHandler;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
Expand All @@ -20,23 +21,31 @@

final class ServerProfileCommand extends Command
{
private $kernel;
private $server;
private $configuration;
private $driver;
private $serverFactory;
private $serverConfiguration;
private $kernel;
private $bootManager;

/**
* @param KernelInterface $kernel
* @param HttpServer $server
* @param HttpServerConfiguration $configuration
* @param RequestHandlerInterface $driver
* @param HttpServerFactory $serverFactory
* @param HttpServerConfiguration $serverConfiguration
* @param KernelInterface $kernel
* @param BootManager $bootManager
*/
public function __construct(KernelInterface $kernel, HttpServer $server, HttpServerConfiguration $configuration, RequestHandlerInterface $driver)
{
$this->kernel = $kernel;
public function __construct(
HttpServer $server,
HttpServerFactory $serverFactory,
HttpServerConfiguration $serverConfiguration,
KernelInterface $kernel,
BootManager $bootManager
) {
$this->server = $server;
$this->driver = $driver;
$this->configuration = $configuration;
$this->serverFactory = $serverFactory;
$this->serverConfiguration = $serverConfiguration;
$this->kernel = $kernel;
$this->bootManager = $bootManager;

parent::__construct();
}
Expand All @@ -48,7 +57,7 @@ public function __construct(KernelInterface $kernel, HttpServer $server, HttpSer
*/
private function getDefaultPublicDir(): string
{
return $this->configuration->hasPublicDir() ? $this->configuration->getPublicDir() : \dirname($this->kernel->getRootDir()).'/public';
return $this->serverConfiguration->hasPublicDir() ? $this->serverConfiguration->getPublicDir() : \dirname($this->kernel->getRootDir()).'/public';
}

/**
Expand Down Expand Up @@ -79,64 +88,98 @@ protected function configure(): void
*/
protected function execute(InputInterface $input, OutputInterface $output): void
{
$xdebug = new XdebugHandler('swoole');
$xdebug->check();
unset($xdebug);
$this->ensureXdebugDisabled();

$io = new SymfonyStyle($input, $output);

$this->configuration->changeSocket(
(string) ($input->getOption('host') ?? $this->configuration->getHost()),
(int) ($input->getOption('port') ?? $this->configuration->getPort())
);
$socket = $this->serverConfiguration->getDefaultSocket();
$socket = $socket
->withPort((int) ($input->getOption('port') ?? $socket->port()))
->withHost((string) ($input->getOption('host') ?? $socket->host()));

$this->serverConfiguration->changeDefaultSocket($socket);

if (\filter_var($input->getOption('serve-static'), FILTER_VALIDATE_BOOLEAN)) {
$this->configuration->enableServingStaticFiles($input->getOption('public-dir'));
$this->serverConfiguration->enableServingStaticFiles($input->getOption('public-dir'));
}

$requestLimit = (int) $input->getArgument('requests');
if ($requestLimit <= 0) {
throw new InvalidArgumentException('Request limit must be greater than 0');
}

$trustedHosts = ServerUtils::decodeStringAsSet($_SERVER['APP_TRUSTED_HOSTS']);
$trustedProxies = ServerUtils::decodeStringAsSet($_SERVER['APP_TRUSTED_PROXIES']);

$this->driver->boot([
$runtimeConfiguration = [
'symfonyStyle' => $io,
'requestLimit' => $requestLimit,
'trustedHosts' => $trustedHosts,
'trustedProxies' => $trustedProxies,
]);

$this->server->setup($this->configuration);

$rows = [
['env', $this->kernel->getEnvironment()],
['debug', \var_export($this->kernel->isDebug(), true)],
['worker_count', $this->configuration->getWorkerCount()],
['memory_limit', ServerUtils::formatBytes(ServerUtils::getMaxMemory())],
['request_limit', $requestLimit > 0 ? $requestLimit : -1],
['trusted_hosts', \implode(', ', $trustedHosts)],
['trusted_proxies', \implode(', ', $trustedProxies)],
'trustedHosts' => ServerUtils::decodeStringAsSet($_SERVER['APP_TRUSTED_HOSTS']),
'trustedProxies' => ServerUtils::decodeStringAsSet($_SERVER['APP_TRUSTED_PROXIES']),
];

if ($this->configuration->hasPublicDir()) {
$rows[] = ['public_dir', $this->configuration->getPublicDir()];
if (\in_array('*', $runtimeConfiguration['trustedProxies'], true)) {
$runtimeConfiguration['trustAllProxies'] = true;
$runtimeConfiguration['trustedProxies'] = \array_filter($runtimeConfiguration['trustedProxies'], function (string $trustedProxy): bool {
return '*' !== $trustedProxy;
});
}

$io->success(\sprintf('Swoole HTTP Server started on http://%s:%d', $this->configuration->getHost(), $this->configuration->getPort()));
$io->table(['Configuration', 'Values'], $rows);
$this->server->attach($this->serverFactory->make(
$this->serverConfiguration->getDefaultSocket(),
$this->serverConfiguration->getRunningMode()
));
$this->bootManager->boot($runtimeConfiguration);

$io->success(\sprintf('Swoole HTTP Server started on http://%s', $this->serverConfiguration->getDefaultSocket()->addressPort()));
$this->printServerConfiguration($io, $runtimeConfiguration);

// TODO: Remove or improve before release
if ($this->kernel->isDebug()) {
dump($this->configuration);
dump($this->configuration->getSwooleSettings());
dump($this->serverConfiguration, $this->serverConfiguration->getSwooleSettings());
}

if ($this->server->start($this->driver)) {
if ($this->server->start()) {
$io->success('Swoole HTTP Server has been successfully shutdown.');
} else {
$io->error('Failure during starting Swoole HTTP Server.');
}
}

/**
* Xdebug must be disabled when using swoole due to possibility of core dump.
*/
private function ensureXdebugDisabled(): void
{
$xdebug = new XdebugHandler('swoole');
$xdebug->check();
unset($xdebug);
}

/**
* @param SymfonyStyle $io
* @param array $runtimeConfiguration
*
* @throws \Assert\AssertionFailedException
*/
private function printServerConfiguration(SymfonyStyle $io, array $runtimeConfiguration): void
{
$rows = [
['env', $this->kernel->getEnvironment()],
['debug', \var_export($this->kernel->isDebug(), true)],
['worker_count', $this->serverConfiguration->getWorkerCount()],
['memory_limit', ServerUtils::formatBytes(ServerUtils::getMaxMemory())],
['request_limit', $runtimeConfiguration['requestLimit'] > 0 ? $runtimeConfiguration['requestLimit'] : -1],
['trusted_hosts', \implode(', ', $runtimeConfiguration['trustedHosts'])],
];

if (isset($runtimeConfiguration['trustAllProxies'])) {
$rows[] = ['trusted_proxies', '*'];
} else {
$rows[] = ['trusted_proxies', \implode(', ', $runtimeConfiguration['trustedProxies'])];
}

if ($this->serverConfiguration->hasPublicDir()) {
$rows[] = ['public_dir', $this->serverConfiguration->getPublicDir()];
}

$io->table(['Configuration', 'Values'], $rows);
}
}
Loading

0 comments on commit 5187396

Please sign in to comment.