Skip to content

Commit

Permalink
feat(http): add option to configure http client, better usage with in…
Browse files Browse the repository at this point in the history
…terceptor
  • Loading branch information
joelwurtz committed Apr 12, 2024
1 parent d3e6597 commit bdc1d6d
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 30 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
## Changes

## 0.13.0 - 10/04/2024
## 0.13.0 - 12/04/2024

* Add commaand line argument to configure default http client configuration
* Fixed allow self signed certificate not used
* **[BC BREAK]** HTTP test case now rely exclusively on amp http client (no more psr7 or psr18)
* Fix assertions count
* Add a new attribute to configure HttpClient (allow to set timeout)
Expand Down
1 change: 1 addition & 0 deletions src/Attribute/HttpClientConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
public function __construct(
public float $timeout = 10,
public int $retry = 0,
public bool $allowSelfSignedCertificate = false,
) {
}
}
11 changes: 10 additions & 1 deletion src/Command/AsynitCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Asynit\Command;

use Asynit\Attribute\HttpClientConfiguration;
use Asynit\Output\OutputFactory;
use Asynit\Parser\TestPoolBuilder;
use Asynit\Parser\TestsFinder;
Expand All @@ -27,6 +28,8 @@ protected function configure(): void
->addOption('host', null, InputOption::VALUE_REQUIRED, 'Base host to use', null)
->addOption('allow-self-signed-certificate', null, InputOption::VALUE_NONE, 'Allow self signed ssl certificate')
->addOption('concurrency', null, InputOption::VALUE_REQUIRED, 'Max number of parallels requests', 10)
->addOption('timeout', null, InputOption::VALUE_REQUIRED, 'Default timeout for http request', 10)
->addOption('retry', null, InputOption::VALUE_REQUIRED, 'Default retry number for http request', 0)
->addOption('bootstrap', null, InputOption::VALUE_REQUIRED, 'A PHP file to include before anything else', $this->defaultBootstrapFilename)
->addOption('order', null, InputOption::VALUE_NONE, 'Output tests execution order')
;
Expand All @@ -47,8 +50,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int

list($chainOutput, $countOutput) = (new OutputFactory($input->getOption('order')))->buildOutput(\count($testMethods));

$defaultHttpConfiguration = new HttpClientConfiguration(
timeout: $input->getOption('timeout'),
retry: $input->getOption('retry'),
allowSelfSignedCertificate: $input->hasOption('allow-self-signed-certificate'),
);

$builder = new TestPoolBuilder();
$runner = new PoolRunner(new TestWorkflow($chainOutput), $input->getOption('concurrency'));
$runner = new PoolRunner($defaultHttpConfiguration, new TestWorkflow($chainOutput), $input->getOption('concurrency'));

// Build a list of tests from the directory
$pool = $builder->build($testMethods);
Expand Down
27 changes: 27 additions & 0 deletions src/HttpClient/ConfigurationInterceptor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Asynit\HttpClient;

use Amp\Cancellation;
use Amp\Http\Client\ApplicationInterceptor;
use Amp\Http\Client\DelegateHttpClient;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Asynit\Attribute\HttpClientConfiguration;

final readonly class ConfigurationInterceptor implements ApplicationInterceptor
{
public function __construct(private HttpClientConfiguration $configuration)
{
}

public function request(Request $request, Cancellation $cancellation, DelegateHttpClient $httpClient): Response
{
$request->setInactivityTimeout($this->configuration->timeout);
$request->setTcpConnectTimeout($this->configuration->timeout);
$request->setTlsHandshakeTimeout($this->configuration->timeout);
$request->setTransferTimeout($this->configuration->timeout);

return $httpClient->request($request, $cancellation);
}
}
33 changes: 8 additions & 25 deletions src/HttpClient/HttpClientCaseTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,11 @@ trait HttpClientCaseTrait

private ?HttpClient $httpClient = null;

private ?\Closure $configureRequest = null;

protected $allowSelfSignedCertificate = false;

protected function createHttpClient(bool $allowSelfSignedCertificate = false, HttpClientConfiguration $httpClientConfiguration = new HttpClientConfiguration()): HttpClient
protected function createHttpClient(HttpClientConfiguration $httpClientConfiguration = new HttpClientConfiguration()): HttpClient
{
$tlsContext = new ClientTlsContext('');

if ($allowSelfSignedCertificate) {
if ($httpClientConfiguration->allowSelfSignedCertificate) {
$tlsContext = $tlsContext->withoutPeerVerification();
}

Expand All @@ -39,43 +35,30 @@ protected function createHttpClient(bool $allowSelfSignedCertificate = false, Ht
$builder = new HttpClientBuilder();
$builder = $builder->retry($httpClientConfiguration->retry);
$builder = $builder->usingPool(new UnlimitedConnectionPool(new DefaultConnectionFactory(null, $connectContext)));
$builder = $builder->intercept(new ConfigurationInterceptor($httpClientConfiguration));

return $builder->build();
}

#[OnCreate]
final public function setUpHttpClient(): void
final public function setUpHttpClient(HttpClientConfiguration $httpClientConfiguration): void
{
$reflection = new \ReflectionClass($this);

$httpClientConfiguration = $reflection->getAttributes(HttpClientConfiguration::class);
$httpClientConfigurationAttribute = $reflection->getAttributes(HttpClientConfiguration::class);

if (!$httpClientConfiguration) {
$httpClientConfiguration = new HttpClientConfiguration();
} else {
$httpClientConfiguration = $httpClientConfiguration[0]->newInstance();
if ($httpClientConfigurationAttribute) {
$httpClientConfiguration = $httpClientConfigurationAttribute[0]->newInstance();
}

$this->httpClient = $this->createHttpClient($this->allowSelfSignedCertificate, $httpClientConfiguration);

$this->configureRequest = function (Request $request) use ($httpClientConfiguration) {
$request->setInactivityTimeout($httpClientConfiguration->timeout);
$request->setTcpConnectTimeout($httpClientConfiguration->timeout);
$request->setTlsHandshakeTimeout($httpClientConfiguration->timeout);
$request->setTransferTimeout($httpClientConfiguration->timeout);
};
$this->httpClient = $this->createHttpClient($httpClientConfiguration);
}

/**
* Allow to test a rejection or a resolution of an async call.
*/
final protected function sendRequest(Request $request): Response
{
if (null !== $this->configureRequest) {
$configureRequest = $this->configureRequest;
$configureRequest($request);
}

return $this->httpClient->request($request);
}
}
10 changes: 7 additions & 3 deletions src/Runner/PoolRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Amp\Future;
use Amp\Sync\LocalSemaphore;
use Amp\Sync\Semaphore;
use Asynit\Attribute\HttpClientConfiguration;
use Asynit\Attribute\OnCreate;
use Asynit\Pool;
use Asynit\Test;
Expand All @@ -22,8 +23,11 @@ class PoolRunner
/**
* @param positive-int $concurrency
*/
public function __construct(private TestWorkflow $workflow, int $concurrency = 10)
{
public function __construct(
private HttpClientConfiguration $defaultHttpConfiguration,
private TestWorkflow $workflow,
int $concurrency = 10
) {
$this->semaphore = new LocalSemaphore($concurrency);
}

Expand Down Expand Up @@ -102,7 +106,7 @@ private function getTestCase(Test $test): object
continue;
}

$testCase->{$reflectionMethod->getName()}();
$testCase->{$reflectionMethod->getName()}($this->defaultHttpConfiguration);
}

$this->testCases[$reflectionClass->getName()] = $testCase;
Expand Down

0 comments on commit bdc1d6d

Please sign in to comment.