From de3ab7234df2d8de68ab196f55b5cac3ea6a8284 Mon Sep 17 00:00:00 2001 From: Marc Morera Date: Mon, 15 Nov 2021 18:10:18 +0100 Subject: [PATCH] Added connection closing --- .circleci/config.yml | 3 +- src/Application.php | 18 +++++------ src/Console/RunServerCommand.php | 1 - src/Console/ServerCommand.php | 7 +++-- src/Console/WatchServerCommand.php | 3 +- src/Context/ServerContext.php | 10 +++++++ tests/CloseConnectionsTest.php | 48 ++++++++++++++++++++++++++++++ tests/CompressionTest.php | 2 +- tests/FakeKernel.php | 2 +- 9 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 tests/CloseConnectionsTest.php diff --git a/.circleci/config.yml b/.circleci/config.yml index b7fcb84..3ba244a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,6 +40,7 @@ jobs: command: | rm -Rf var/* composer require react/filesystem --no-update + composer require wyrihaximus/react-child-process-pool:1.6.* --no-update composer require seregazhuk/php-watcher:^0.5.2 --no-update composer require drift/event-bus-bundle:^0.1 --no-update composer update -n --prefer-dist @@ -71,4 +72,4 @@ workflows: jobs: - test-php74 - test-php74-with-filesystem - - test-php80 \ No newline at end of file + - test-php80 diff --git a/src/Application.php b/src/Application.php index 1c80c7c..4818380 100644 --- a/src/Application.php +++ b/src/Application.php @@ -31,15 +31,15 @@ use Psr\Http\Message\StreamInterface; use React\EventLoop\LoopInterface; use React\Filesystem\FilesystemInterface; +use React\Http\HttpServer; use React\Http\Message\Response as ReactResponse; use React\Http\Middleware\LimitConcurrentRequestsMiddleware; use React\Http\Middleware\StreamingRequestMiddleware; -use React\Http\Server as HttpServer; use React\Promise\Promise; use React\Promise\PromiseInterface; use function React\Promise\reject; use function React\Promise\resolve; -use React\Socket\Server as SocketServer; +use React\Socket\SocketServer; use React\Stream\ReadableStreamInterface; use React\Stream\ThroughStream; use Throwable; @@ -52,7 +52,6 @@ class Application private LoopInterface $loop; private ServerContext $serverContext; private string $rootPath; - private string $bootstrapPath; private string $kernelAdapter; private OutputPrinter $outputPrinter; private MimeTypeChecker $mimeTypeChecker; @@ -63,7 +62,6 @@ class Application * @param OutputPrinter $outputPrinter * @param MimeTypeChecker $mimeTypeChecker * @param string $rootPath - * @param string $bootstrapPath * * @throws Exception */ @@ -72,14 +70,12 @@ public function __construct( ServerContext $serverContext, OutputPrinter $outputPrinter, MimeTypeChecker $mimeTypeChecker, - string $rootPath, - string $bootstrapPath + string $rootPath ) { $this->loop = $loop; $this->serverContext = $serverContext; $this->outputPrinter = $outputPrinter; $this->rootPath = $rootPath; - $this->bootstrapPath = $bootstrapPath; $this->mimeTypeChecker = $mimeTypeChecker; ErrorHandler::handle(); @@ -111,8 +107,8 @@ public function runServer( $socket = new SocketServer( $this->serverContext->getHost().':'. $this->serverContext->getPort(), - $this->loop, - ['tcp' => ['so_reuseport' => ($this->serverContext->getWorkers() > 1)]] + ['tcp' => ['so_reuseport' => ($this->serverContext->getWorkers() > 1)]], + $this->loop ); $http = new HttpServer( @@ -219,6 +215,10 @@ private function toServerResponse( $response = $response->withoutHeader('x-server-message'); } + if ($this->serverContext->mustCloseConnections()) { + $response = $response->withHeader('Connection', 'close'); + } + $serverResponse = new ServerResponseWithMessage( $response, diff --git a/src/Console/RunServerCommand.php b/src/Console/RunServerCommand.php index 0f52bbb..388f86b 100644 --- a/src/Console/RunServerCommand.php +++ b/src/Console/RunServerCommand.php @@ -56,7 +56,6 @@ protected function executeServerCommand( $outputPrinter, $mimeTypeChecker, $rootPath, - $this->bootstrapPath ); $kernelAdapterNamespace = $serverContext->getAdapter(); diff --git a/src/Console/ServerCommand.php b/src/Console/ServerCommand.php index ca2a9e0..d74f29f 100644 --- a/src/Console/ServerCommand.php +++ b/src/Console/ServerCommand.php @@ -24,7 +24,7 @@ use Drift\Server\Context\ServerContext; use Drift\Server\ServerHeaderPrinter; use Exception; -use React\EventLoop\Factory as EventLoopFactory; +use React\EventLoop\Loop; use React\EventLoop\LoopInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -76,7 +76,8 @@ protected function configure() ->addOption('allowed-loop-stops', null, InputOption::VALUE_OPTIONAL, 'Number of allowed loop stops', 0) ->addOption('workers', null, InputOption::VALUE_OPTIONAL, 'Number of workers. Use -1 to get as many workers as physical thread available for your system. Maximum of 128 workers. Option disabled for watch command.', 1 - ); + ) + ->addOption('close-connections', null, InputOption::VALUE_NONE, 'Close all connections by adding "Connection: Close" header each time.'); /* * If we have the EventBus loaded, we can add listeners as well @@ -115,7 +116,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->configureServerContext($serverContext); $outputPrinter = $this->createOutputPrinter($output, $serverContext->isQuiet(), $serverContext->isAlmostQuiet()); - $loop = EventLoopFactory::create(); + $loop = Loop::get(); if ($serverContext->shouldPrintImportantOutput()) { ServerHeaderPrinter::print( $serverContext, diff --git a/src/Console/WatchServerCommand.php b/src/Console/WatchServerCommand.php index 0c7b6b3..e9d0de8 100644 --- a/src/Console/WatchServerCommand.php +++ b/src/Console/WatchServerCommand.php @@ -69,8 +69,7 @@ protected function executeServerCommand( $serverContext, $outputPrinter, $mimeTypeChecker, - $rootPath, - $this->bootstrapPath + $rootPath ); $argv = $this->argv; diff --git a/src/Context/ServerContext.php b/src/Context/ServerContext.php index 7a2709c..452900c 100644 --- a/src/Context/ServerContext.php +++ b/src/Context/ServerContext.php @@ -42,6 +42,7 @@ final class ServerContext private int $requestBodyBuffer; private int $allowedLoopStops; private int $workers; + private bool $closeConnections; /** * @param InputInterface $input @@ -113,6 +114,7 @@ public static function buildByInput(InputInterface $input): ServerContext $serverContext->limitConcurrentRequests = intval($input->getOption('concurrent-requests')); $serverContext->requestBodyBuffer = intval($input->getOption('request-body-buffer')); + $serverContext->closeConnections = boolval($input->getOption('close-connections')); $serverContext->allowedLoopStops = intval($input->getOption('allowed-loop-stops')); $serverContext->workers = \intval($input->getOption('workers')); if (-1 === $serverContext->workers) { @@ -310,6 +312,14 @@ public function cleanWorkers() $this->workers = 1; } + /** + * @return bool + */ + public function mustCloseConnections(): bool + { + return $this->closeConnections; + } + /** * Build queue architecture from array of strings. * diff --git a/tests/CloseConnectionsTest.php b/tests/CloseConnectionsTest.php new file mode 100644 index 0000000..29fb4ef --- /dev/null +++ b/tests/CloseConnectionsTest.php @@ -0,0 +1,48 @@ + + */ + +declare(strict_types=1); + +namespace Drift\Server\Tests; + +/** + * Class CloseConnectionsTest. + */ +class CloseConnectionsTest extends BaseTest +{ + /** + * @group lol + */ + public function testKeepAlive() + { + list($process, $port, $initialOutput) = $this->buildServer(['--debug']); + list($_, $headers) = Utils::curl("http://127.0.0.1:$port/text"); + $this->waitForChange($process, $initialOutput); + $this->assertEquals('keep-alive', $headers['Connection']); + + $process->stop(); + } + + /** + * @group lol + */ + public function testConnectionCloses() + { + list($process, $port, $initialOutput) = $this->buildServer(['--debug', '--close-connections']); + list($_, $headers) = Utils::curl("http://127.0.0.1:$port/text"); + $this->waitForChange($process, $initialOutput); + $this->assertEquals('close', $headers['Connection']); + + $process->stop(); + } +} diff --git a/tests/CompressionTest.php b/tests/CompressionTest.php index 454c66c..86c803c 100644 --- a/tests/CompressionTest.php +++ b/tests/CompressionTest.php @@ -92,7 +92,7 @@ public function testAlreadyEncodedContent() { list($process, $port) = $this->buildServer(); $response = Utils::curl("http://127.0.0.1:$port/gzip", [ - "Accept-Encoding: gzip", + 'Accept-Encoding: gzip', ]); $this->assertEquals('ReactPHP Response', gzdecode($response[0])); diff --git a/tests/FakeKernel.php b/tests/FakeKernel.php index f394262..48c57e2 100644 --- a/tests/FakeKernel.php +++ b/tests/FakeKernel.php @@ -183,7 +183,7 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ if ('/gzip' === $pathInfo) { return new Response($code, [ - 'Content-Encoding' => 'gzip' + 'Content-Encoding' => 'gzip', ], gzencode('ReactPHP Response')); }