From 67a06332a646a2c6291bda24a882ae095d513e55 Mon Sep 17 00:00:00 2001 From: Joel Wurtz Date: Wed, 10 Apr 2024 13:10:14 +0200 Subject: [PATCH] feat(http): remove dep on psr http, only rely on amp http client --- composer.json | 15 +++-- src/Assert/AssertWebCaseTrait.php | 12 ++-- src/HttpClient/AmpPsrHttpClient.php | 48 ---------------- src/HttpClient/ApiResponse.php | 70 ++++------------------- src/HttpClient/HttpClientApiCaseTrait.php | 44 ++++++-------- src/HttpClient/HttpClientCaseTrait.php | 22 +++---- src/HttpClient/HttpClientWebCaseTrait.php | 42 ++++++-------- tests/AnotherTestHttp.php | 4 +- tests/FunctionalApiTests.php | 4 +- tests/FunctionalHttpTests.php | 4 +- tests/WebAndApiTests.php | 6 +- 11 files changed, 78 insertions(+), 193 deletions(-) delete mode 100644 src/HttpClient/AmpPsrHttpClient.php diff --git a/composer.json b/composer.json index a8fdc90..fe7d46c 100644 --- a/composer.json +++ b/composer.json @@ -28,25 +28,24 @@ "require": { "php": "^8.2", "amphp/amp": "^3.0", + "amphp/http-client": "^v5.0.1", "amphp/sync": "^2.0", "bovigo/assert": "^7.0", "symfony/console": "^4.4 || ^5.0 || ^6.0", "symfony/finder": "^4.4 || ^5.0 || ^6.0" }, "suggest": { - "amphp/http-client": "To use the web test case trait", - "nyholm/psr7": "To use the web test case trait" + "amphp/http-client": "To use the web test case trait" }, "require-dev": { - "amphp/http-client": "v5.0.0-beta.11", - "friendsofphp/php-cs-fixer": "^v3.16.0", - "nyholm/psr7": "^1.8.0", - "php-http/client-common": "^2.4", - "php-http/message": "^1.3", + "amphp/http-client": "^5.0.1", + "friendsofphp/php-cs-fixer": "^3.16.0", "phpstan/phpstan": "^1.10" }, "conflict": { - "amphp/http-client": "= 5.0" }, "prefer-stable": true, diff --git a/src/Assert/AssertWebCaseTrait.php b/src/Assert/AssertWebCaseTrait.php index 0490336..46d960c 100644 --- a/src/Assert/AssertWebCaseTrait.php +++ b/src/Assert/AssertWebCaseTrait.php @@ -4,25 +4,25 @@ namespace Asynit\Assert; -use Psr\Http\Message\ResponseInterface; +use Amp\Http\HttpResponse; trait AssertWebCaseTrait { use AssertCaseTrait; - public function assertStatusCode($expectedStatus, ResponseInterface $response, $message = null) + public function assertStatusCode($expectedStatus, HttpResponse $response, $message = null) { - $this->assertEquals($expectedStatus, $response->getStatusCode(), $message ?? 'Assert status code is equals to '.$expectedStatus); + $this->assertEquals($expectedStatus, $response->getStatus(), $message ?? 'Assert status code is equals to '.$expectedStatus); } - public function assertContentType($expected, ResponseInterface $response, $message = null) + public function assertContentType($expected, HttpResponse $response, $message = null) { - $contentType = $response->getHeaderLine('Content-Type'); + $contentType = $response->getHeader('Content-Type'); $this->assertContains($expected, $contentType, $message ?? 'Assert content type is "'.$expected.'"'); } - public function assertHtml(ResponseInterface $response, $message = null) + public function assertHtml(HttpResponse $response, $message = null) { $this->assertContentType('text/html', $response, $message); } diff --git a/src/HttpClient/AmpPsrHttpClient.php b/src/HttpClient/AmpPsrHttpClient.php deleted file mode 100644 index 6c9cd3a..0000000 --- a/src/HttpClient/AmpPsrHttpClient.php +++ /dev/null @@ -1,48 +0,0 @@ -getUri(), - $request->getMethod(), - $request->getBody()->getContents(), - ); - - foreach ($request->getHeaders() as $name => $values) { - $ampRequest->addHeader($name, $values); - } - - $ampResponse = $this->client->request($ampRequest); - - $response = $this->responseFactory->createResponse( - $ampResponse->getStatus(), - ); - - foreach ($ampResponse->getHeaders() as $name => $values) { - $response = $response->withHeader($name, $values); - } - - $body = $this->streamFactory->createStream($ampResponse->getBody()->buffer()); - - return $response->withBody($body); - } -} diff --git a/src/HttpClient/ApiResponse.php b/src/HttpClient/ApiResponse.php index a2e8144..ab6578e 100644 --- a/src/HttpClient/ApiResponse.php +++ b/src/HttpClient/ApiResponse.php @@ -2,32 +2,29 @@ namespace Asynit\HttpClient; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\StreamInterface; +use Amp\ByteStream\Payload; +use Amp\Http\Client\Response; +use Amp\Http\HttpResponse; /** * @implements \ArrayAccess */ -class ApiResponse implements \ArrayAccess, ResponseInterface +class ApiResponse extends HttpResponse implements \ArrayAccess { /** * @var array|null */ private array|null $data = null; - public function __construct(private ResponseInterface $response) + public function __construct(private readonly Response $response) { - } - - public function getStatusCode(): int - { - return $this->response->getStatusCode(); + parent::__construct($response->getStatus(), $response->getReason()); } private function ensureBodyIsRead(bool $associative = true): void { if (null === $this->data) { - $this->data = json_decode($this->response->getBody(), $associative, flags: JSON_THROW_ON_ERROR); + $this->data = json_decode($this->response->getBody()->read(), $associative, flags: JSON_THROW_ON_ERROR); } } @@ -70,68 +67,23 @@ public function offsetUnset(mixed $offset): void unset($this->data[$offset]); } - public function getProtocolVersion() - { - return $this->response->getProtocolVersion(); - } - - public function withProtocolVersion(string $version): self - { - return new self($this->response->withProtocolVersion($version)); - } - - public function getHeaders() + public function getHeaders(): array { return $this->response->getHeaders(); } - public function hasHeader(string $name) + public function hasHeader(string $name): bool { return $this->response->hasHeader($name); } - public function getHeader(string $name) + public function getHeader(string $name): null|string { return $this->response->getHeader($name); } - public function getHeaderLine(string $name) - { - return $this->response->getHeaderLine($name); - } - - public function withHeader(string $name, $value): self - { - return new self($this->response->withHeader($name, $value)); - } - - public function withAddedHeader(string $name, $value): self - { - return new self($this->response->withAddedHeader($name, $value)); - } - - public function withoutHeader(string $name): self - { - return new self($this->response->withoutHeader($name)); - } - - public function getBody() + public function getBody(): Payload { return $this->response->getBody(); } - - public function withBody(StreamInterface $body): self - { - return new self($this->response->withBody($body)); - } - - public function withStatus(int $code, string $reasonPhrase = ''): self - { - return new self($this->response->withStatus($code, $reasonPhrase)); - } - - public function getReasonPhrase() - { - return $this->response->getReasonPhrase(); - } } diff --git a/src/HttpClient/HttpClientApiCaseTrait.php b/src/HttpClient/HttpClientApiCaseTrait.php index 4b6aba3..51e4c79 100644 --- a/src/HttpClient/HttpClientApiCaseTrait.php +++ b/src/HttpClient/HttpClientApiCaseTrait.php @@ -2,7 +2,7 @@ namespace Asynit\HttpClient; -use Psr\Http\Message\RequestInterface; +use Amp\Http\Client\Request; trait HttpClientApiCaseTrait { @@ -13,54 +13,48 @@ protected function getApiContentType(): string return 'application/json'; } - final protected function get(string $uri, array|null $json = null, array $headers = [], ?string $version = null): ApiResponse + final protected function get(string $uri, array|null $json = null, array $headers = []): ApiResponse { - return new ApiResponse($this->sendRequest($this->createApiRequest('GET', $uri, $headers, $json, $version))); + return new ApiResponse($this->sendRequest($this->createApiRequest('GET', $uri, $headers, $json))); } - final protected function post(string $uri, array|null $json = null, array $headers = [], ?string $version = null): ApiResponse + final protected function post(string $uri, array|null $json = null, array $headers = []): ApiResponse { - return new ApiResponse($this->sendRequest($this->createApiRequest('POST', $uri, $headers, $json, $version))); + return new ApiResponse($this->sendRequest($this->createApiRequest('POST', $uri, $headers, $json))); } - final protected function patch(string $uri, array|null $json = null, array $headers = [], ?string $version = null): ApiResponse + final protected function patch(string $uri, array|null $json = null, array $headers = []): ApiResponse { - return new ApiResponse($this->sendRequest($this->createApiRequest('PATCH', $uri, $headers, $json, $version))); + return new ApiResponse($this->sendRequest($this->createApiRequest('PATCH', $uri, $headers, $json))); } - final protected function put(string $uri, array|null $json = null, array $headers = [], ?string $version = null): ApiResponse + final protected function put(string $uri, array|null $json = null, array $headers = []): ApiResponse { - return new ApiResponse($this->sendRequest($this->createApiRequest('PUT', $uri, $headers, $json, $version))); + return new ApiResponse($this->sendRequest($this->createApiRequest('PUT', $uri, $headers, $json))); } - final protected function delete(string $uri, array|null $json = null, array $headers = [], ?string $version = null): ApiResponse + final protected function delete(string $uri, array|null $json = null, array $headers = []): ApiResponse { - return new ApiResponse($this->sendRequest($this->createApiRequest('DELETE', $uri, $headers, $json, $version))); + return new ApiResponse($this->sendRequest($this->createApiRequest('DELETE', $uri, $headers, $json))); } - final protected function options(string $uri, array|null $json = null, array $headers = [], ?string $version = null): ApiResponse + final protected function options(string $uri, array|null $json = null, array $headers = []): ApiResponse { - return new ApiResponse($this->sendRequest($this->createApiRequest('OPTIONS', $uri, $headers, $json, $version))); + return new ApiResponse($this->sendRequest($this->createApiRequest('OPTIONS', $uri, $headers, $json))); } - private function createApiRequest(string $method, string $uri, array $headers = [], array|null $json = null, ?string $version = null): RequestInterface + private function createApiRequest(string $method, string $uri, array $headers = [], array|null $json = null): Request { - $request = $this->httpFactory->createRequest($method, $uri); - $request = $request->withHeader('Content-Type', $this->getApiContentType()); - $request = $request->withHeader('Accept', $this->getApiContentType()); + $request = new Request($uri, $method); + $request->addHeader('Content-Type', $this->getApiContentType()); + $request->addHeader('Accept', $this->getApiContentType()); foreach ($headers as $name => $value) { - $request = $request->withHeader($name, $value); + $request->addHeader($name, $value); } if (null !== $json) { - $body = $this->httpFactory->createStream(json_encode($json, flags: JSON_THROW_ON_ERROR)); - - $request = $request->withBody($body); - } - - if (null !== $version) { - $request = $request->withProtocolVersion($version); + $request->setBody(json_encode($json, flags: JSON_THROW_ON_ERROR)); } return $request; diff --git a/src/HttpClient/HttpClientCaseTrait.php b/src/HttpClient/HttpClientCaseTrait.php index dfa5a68..b6208b5 100644 --- a/src/HttpClient/HttpClientCaseTrait.php +++ b/src/HttpClient/HttpClientCaseTrait.php @@ -4,27 +4,24 @@ use Amp\Http\Client\Connection\DefaultConnectionFactory; use Amp\Http\Client\Connection\UnlimitedConnectionPool; +use Amp\Http\Client\HttpClient; use Amp\Http\Client\HttpClientBuilder; +use Amp\Http\Client\Request; +use Amp\Http\HttpResponse; use Amp\Socket\ClientTlsContext; use Amp\Socket\ConnectContext; use Asynit\Assert\AssertWebCaseTrait; use Asynit\Attribute\OnCreate; -use Nyholm\Psr7\Factory\Psr17Factory; -use Psr\Http\Client\ClientInterface; -use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; trait HttpClientCaseTrait { use AssertWebCaseTrait; - private ClientInterface|null $httpClient = null; - - private Psr17Factory|null $httpFactory = null; + private HttpClient|null $httpClient = null; protected $allowSelfSignedCertificate = false; - protected function createHttpClient(bool $allowSelfSignedCertificate = false): ClientInterface + protected function createHttpClient(bool $allowSelfSignedCertificate = false): HttpClient { $tlsContext = new ClientTlsContext(''); @@ -37,24 +34,21 @@ protected function createHttpClient(bool $allowSelfSignedCertificate = false): C $builder = new HttpClientBuilder(); $builder = $builder->usingPool(new UnlimitedConnectionPool(new DefaultConnectionFactory(null, $connectContext))); - $client = $builder->build(); - $factory = new Psr17Factory(); - return new AmpPsrHttpClient($client, $factory, $factory); + return $builder->build(); } #[OnCreate] final public function setUpHttpClient(): void { $this->httpClient = $this->createHttpClient($this->allowSelfSignedCertificate); - $this->httpFactory = new Psr17Factory(); } /** * Allow to test a rejection or a resolution of an async call. */ - final protected function sendRequest(RequestInterface $request): ResponseInterface + final protected function sendRequest(Request $request): HttpResponse { - return $this->httpClient->sendRequest($request); + return $this->httpClient->request($request); } } diff --git a/src/HttpClient/HttpClientWebCaseTrait.php b/src/HttpClient/HttpClientWebCaseTrait.php index c00ebfa..260df39 100644 --- a/src/HttpClient/HttpClientWebCaseTrait.php +++ b/src/HttpClient/HttpClientWebCaseTrait.php @@ -2,59 +2,53 @@ namespace Asynit\HttpClient; -use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; +use Amp\Http\Client\Request; +use Amp\Http\HttpResponse; trait HttpClientWebCaseTrait { use HttpClientCaseTrait; - final protected function get(string $uri, array $headers = [], $body = null, ?string $version = null): ResponseInterface + final protected function get(string $uri, array $headers = [], $body = null): HttpResponse { - return $this->sendRequest($this->createRequest('GET', $uri, $headers, $body, $version)); + return $this->sendRequest($this->createRequest('GET', $uri, $headers, $body)); } - final protected function post(string $uri, array $headers = [], $body = null, ?string $version = null): ResponseInterface + final protected function post(string $uri, array $headers = [], $body = null): HttpResponse { - return $this->sendRequest($this->createRequest('POST', $uri, $headers, $body, $version)); + return $this->sendRequest($this->createRequest('POST', $uri, $headers, $body)); } - final protected function patch(string $uri, array $headers = [], $body = null, ?string $version = null): ResponseInterface + final protected function patch(string $uri, array $headers = [], $body = null): HttpResponse { - return $this->sendRequest($this->createRequest('PATCH', $uri, $headers, $body, $version)); + return $this->sendRequest($this->createRequest('PATCH', $uri, $headers, $body)); } - final protected function put(string $uri, array $headers = [], $body = null, ?string $version = null): ResponseInterface + final protected function put(string $uri, array $headers = [], $body = null): HttpResponse { - return $this->sendRequest($this->createRequest('PUT', $uri, $headers, $body, $version)); + return $this->sendRequest($this->createRequest('PUT', $uri, $headers, $body)); } - final protected function delete(string $uri, array $headers = [], $body = null, ?string $version = null): ResponseInterface + final protected function delete(string $uri, array $headers = [], $body = null): HttpResponse { - return $this->sendRequest($this->createRequest('DELETE', $uri, $headers, $body, $version)); + return $this->sendRequest($this->createRequest('DELETE', $uri, $headers, $body)); } - final protected function options(string $uri, array $headers = [], $body = null, ?string $version = null): ResponseInterface + final protected function options(string $uri, array $headers = [], $body = null): HttpResponse { - return $this->sendRequest($this->createRequest('OPTIONS', $uri, $headers, $body, $version)); + return $this->sendRequest($this->createRequest('OPTIONS', $uri, $headers, $body)); } - private function createRequest(string $method, string $uri, array $headers = [], $body = null, ?string $version = null): RequestInterface + private function createRequest(string $method, string $uri, array $headers = [], $body = null): Request { - $request = $this->httpFactory->createRequest($method, $uri); + $request = new Request($uri, $method); foreach ($headers as $name => $value) { - $request = $request->withHeader($name, $value); + $request->addHeader($name, $value); } if (null !== $body) { - $body = $this->httpFactory->createStream($body); - - $request = $request->withBody($body); - } - - if (null !== $version) { - $request = $request->withProtocolVersion($version); + $request->setBody($body); } return $request; diff --git a/tests/AnotherTestHttp.php b/tests/AnotherTestHttp.php index 7e753f4..d1acf86 100644 --- a/tests/AnotherTestHttp.php +++ b/tests/AnotherTestHttp.php @@ -2,8 +2,8 @@ namespace Asynit\Tests; +use Amp\Http\HttpResponse; use Asynit\HttpClient\HttpClientWebCaseTrait; -use Psr\Http\Message\ResponseInterface; class AnotherTestHttp { @@ -13,7 +13,7 @@ public function test_from_another_file() { $response = $this->get('http://127.0.0.1:8081/'); - $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertInstanceOf(HttpResponse::class, $response); $this->assertStatusCode(200, $response); return __METHOD__; diff --git a/tests/FunctionalApiTests.php b/tests/FunctionalApiTests.php index 3d70eb6..e93df2c 100644 --- a/tests/FunctionalApiTests.php +++ b/tests/FunctionalApiTests.php @@ -3,8 +3,8 @@ namespace Asynit\Tests; use Asynit\Attribute\TestCase; +use Asynit\HttpClient\ApiResponse; use Asynit\HttpClient\HttpClientApiCaseTrait; -use Psr\Http\Message\ResponseInterface; #[TestCase] class FunctionalApiTests @@ -15,7 +15,7 @@ public function testJson() { $response = $this->get($this->createUri('/get')); - $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertInstanceOf(ApiResponse::class, $response); $this->assertStatusCode(200, $response); $this->assertSame('application/json', $response['headers']['Content-Type']); } diff --git a/tests/FunctionalHttpTests.php b/tests/FunctionalHttpTests.php index ef8cc11..34acdfc 100644 --- a/tests/FunctionalHttpTests.php +++ b/tests/FunctionalHttpTests.php @@ -2,10 +2,10 @@ namespace Asynit\Tests; +use Amp\Http\HttpResponse; use Asynit\Attribute\Depend; use Asynit\Attribute\TestCase; use Asynit\HttpClient\HttpClientWebCaseTrait; -use Psr\Http\Message\ResponseInterface; #[TestCase] class FunctionalHttpTests @@ -21,7 +21,7 @@ public function testGet() { $response = $this->get($this->createUri('/')); - $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertInstanceOf(HttpResponse::class, $response); $this->assertStatusCode(200, $response); return 'foo'; diff --git a/tests/WebAndApiTests.php b/tests/WebAndApiTests.php index 830d847..2cce93a 100644 --- a/tests/WebAndApiTests.php +++ b/tests/WebAndApiTests.php @@ -2,6 +2,7 @@ namespace Asynit\Tests; +use Amp\Http\HttpResponse; use Asynit\Attribute\TestCase; use Asynit\HttpClient\ApiResponse; use Asynit\HttpClient\HttpClientApiCaseTrait; @@ -30,12 +31,11 @@ public function testJsonWeb() { $response = $this->get($this->createUri('/get')); - $this->assertInstanceOf(ResponseInterface::class, $response); + $this->assertInstanceOf(HttpResponse::class, $response); $this->assertStatusCode(200, $response); - $content = json_decode($response->getBody()->getContents(), true); + $content = json_decode($response->getBody(), true); $this->assertArrayNotHasKey('Content-Type', $content['headers']); - } public function testJsonApi()