Skip to content

Commit

Permalink
Configure HTTP server options via parameters
Browse files Browse the repository at this point in the history
Let the application configure the HTTP server options (for example the bodySizeLimit) via the parameters section in esb.yml.
  • Loading branch information
youwe-petervanderwal committed Feb 23, 2021
1 parent 522bde2 commit eb70d21
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 6 deletions.
2 changes: 2 additions & 0 deletions esb.yml.sample
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
parameters:
beanstalkd: tcp://127.0.0.1:11300 # Beanstalkd connection URI
http_server_port: 34981 # HTTP Server Port
http_server_options: # The HTTP server options, see \Amp\Http\Server\Options for
bodySizeLimit: 131072 # all available options and default values
logger_mail_to: [email protected] # Email address where to send significant events mail notifications
logger_mail_from: "From Name <[email protected]>" # From name/address for significant events mail notifications
console_port: 8080 # Web console port
Expand Down
1 change: 1 addition & 0 deletions services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ services:
Webgriffe\Esb\Service\HttpProducersServer:
arguments:
$port: '%http_server_port%'
$options: '%http_server_options%'

Monolog\Handler\StreamHandler:
class: \Monolog\Handler\StreamHandler
Expand Down
1 change: 1 addition & 0 deletions services_test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
parameters:
beanstalkd: '%env(string:ESB_BEANSTALKD_URL)%'
http_server_port: '%env(int:ESB_HTTP_SERVER_PORT)%'
http_server_options: ~
logger_mail_to: "[email protected]"
logger_mail_from: "From Name <[email protected]>"
console_port: '%env(int:ESB_CONSOLE_PORT)%'
Expand Down
69 changes: 66 additions & 3 deletions src/Service/HttpProducersServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Webgriffe\Esb\Service;

use Amp\CallableMaker;
use Amp\Http\Server\Options;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler\CallableRequestHandler;
use Amp\Http\Server\Response;
Expand All @@ -25,10 +26,33 @@ class HttpProducersServer
{
use CallableMaker;

/**
* All available options with setter methods
*/
private const AVAILABLE_OPTIONS = [
'debug' => ['true' => 'withDebugMode', 'false' => 'withoutDebugMode'],
'connectionLimit' => ['setter' => 'withConnectionLimit'],
'connectionsPerIpLimit' => ['setter' => 'withConnectionsPerIpLimit'],
'connectionTimeout' => ['setter' => 'withConnectionTimeout'],
'concurrentStreamLimit' => ['setter' => 'withConcurrentStreamLimit'],
'framesPerSecondLimit' => ['setter' => 'withFramesPerSecondLimit'],
'minimumAverageFrameSize' => ['setter' => 'withMinimumAverageFrameSize'],
'allowedMethods' => ['setter' => 'withAllowedMethods'],
'bodySizeLimit' => ['setter' => 'withBodySizeLimit'],
'headerSizeLimit' => ['setter' => 'withHeaderSizeLimit'],
'chunkSize' => ['setter' => 'withChunkSize'],
'compression' => ['true' => 'withCompression', 'false' => 'withoutCompression'],
'allowHttp2Upgrade' => ['true' => 'withHttp2Upgrade', 'false' => 'withoutHttp2Upgrade'],
];

/**
* @var int
*/
private $port;
/**
* @var mixed[]|null
*/
private $options;
/**
* @var LoggerInterface
*/
Expand All @@ -42,9 +66,15 @@ class HttpProducersServer
*/
private $httpServer;

public function __construct(int $port, LoggerInterface $logger)
/**
* @param int $port
* @param mixed[]|null $options
* @param LoggerInterface $logger
*/
public function __construct(int $port, ?array $options, LoggerInterface $logger)
{
$this->port = $port;
$this->options = $options;
$this->logger = $logger;
}

Expand All @@ -59,10 +89,11 @@ public function start(): Promise
Socket\listen("[::]:{$this->port}"),
];

$this->httpServer = new \Amp\Http\Server\Server(
$this->httpServer = new Server(
$sockets,
new CallableRequestHandler($this->callableFromInstanceMethod('requestHandler')),
new NullLogger()
new NullLogger(),
$this->getServerOptions()
);

yield $this->httpServer->start();
Expand Down Expand Up @@ -129,4 +160,36 @@ private function matchProducerInstance(Request $request)
}
return false;
}

/**
* Parse the $this->options array into an Options instance
* @return Options
*/
private function getServerOptions(): Options
{
$options = new Options();
if ($this->options === null) {
return $options;
}

foreach (static::AVAILABLE_OPTIONS as $optionName => $optionData) {
$optionValue = $this->options[$optionName] ?? null;
if ($optionValue === null) {
continue;
}

if (isset($optionData['setter'])) {
// $options = $options->withChunkSize($optionValue);
$options = $options->{$optionData['setter']}($optionValue);
} elseif ($optionValue) {
// $options = $options->withDebugMode();
$options = $options->{$optionData['true']}();
} else {
// $options = $options->withoutDebugMode();
$options = $options->{$optionData['false']}();
}
}

return $options;
}
}
49 changes: 46 additions & 3 deletions tests/Integration/HttpRequestProducerAndWorkerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
use Amp\Artax\DefaultClient;
use Amp\Artax\Request;
use Amp\Artax\Response;
use Amp\Http\Server\Options;
use Amp\Loop;
use Amp\Promise;
use Amp\Socket\ClientSocket;
use Amp\Socket\ConnectException;
use Monolog\Logger;
use org\bovigo\vfs\vfsStream;
use ReflectionObject;
use Webgriffe\Esb\DummyFilesystemWorker;
use Webgriffe\Esb\DummyHttpRequestProducer;
use Webgriffe\Esb\KernelTestCase;
use Webgriffe\Esb\Service\HttpProducersServer;
use Webgriffe\Esb\TestUtils;
use function Amp\call;
use function Amp\File\exists;
Expand All @@ -28,9 +31,8 @@ class HttpRequestProducerAndWorkerTest extends KernelTestCase

private const FLOW_CODE = 'http_producer_flow';

public function setUp()
private function setUpKernel(array $additionalParameters = [])
{
parent::setUp();
$this->workerFile = vfsStream::url('root/worker.data');
self::createKernel(
[
Expand All @@ -44,14 +46,17 @@ public function setUp()
'producer' => ['service' => DummyHttpRequestProducer::class],
'worker' => ['service' => DummyFilesystemWorker::class],
]
]
],
'parameters' => $additionalParameters,
]
);
$this->httpPort = self::$kernel->getContainer()->getParameter('http_server_port');
}

public function testHttpRequestProducerAndWorker()
{
$this->setUpKernel();

Loop::delay(100, function () {
yield $this->waitForConnectionAvailable("tcp://127.0.0.1:{$this->httpPort}");
$payload = json_encode(['jobs' => ['job1', 'job2', 'job3']]);
Expand Down Expand Up @@ -89,6 +94,8 @@ public function testHttpRequestProducerAndWorker()

public function testHttpRequestProducerWithWrongUriShouldReturn404()
{
$this->setUpKernel();

Loop::delay(100, function () {
yield $this->waitForConnectionAvailable("tcp://127.0.0.1:{$this->httpPort}");
$payload = json_encode(['jobs' => ['job1', 'job2', 'job3']]);
Expand All @@ -108,6 +115,25 @@ public function testHttpRequestProducerWithWrongUriShouldReturn404()
$this->assertReadyJobsCountInTube(0, self::FLOW_CODE);
}

public function testHttpKernelSettings()
{
$this->setUpKernel(['http_server_options' => ['bodySizeLimit' => 42]]);

Loop::delay(100, function () {
yield $this->waitForConnectionAvailable("tcp://127.0.0.1:{$this->httpPort}");

$httpProducersServer = self::$kernel->getContainer()->get(HttpProducersServer::class);
$httpServer = $this->getObjectProperty($httpProducersServer, 'httpServer');
/** @var Options $serverOptions */
$serverOptions = $this->getObjectProperty($httpServer, 'options');
$this->assertSame(42, $serverOptions->getBodySizeLimit());

Loop::stop();
});

self::$kernel->boot();
}

private function waitForConnectionAvailable(string $uri): Promise
{
return call(function () use ($uri) {
Expand All @@ -122,4 +148,21 @@ private function waitForConnectionAvailable(string $uri): Promise
$connection->close();
});
}

/**
* @param object $object
* @param string $propertyName
* @return mixed
* @throws \ReflectionException
*/
private function getObjectProperty(object $object, string $propertyName)
{
$reflectionProperty = (new ReflectionObject($object))->getProperty($propertyName);
$reflectionProperty->setAccessible(true);
try {
return $reflectionProperty->getValue($object);
} finally {
$reflectionProperty->setAccessible(false);
}
}
}

0 comments on commit eb70d21

Please sign in to comment.