diff --git a/CHANGELOG.md b/CHANGELOG.md index da3e894..886212f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- added additional information in `getInfo()` for `getResponse()` after throw `HttpExceptionInterface` exception + ## 1.0.0 - 2023-05-12 - initial release diff --git a/src/Response/LoggableResponse.php b/src/Response/LoggableResponse.php index 574cbde..f084ace 100644 --- a/src/Response/LoggableResponse.php +++ b/src/Response/LoggableResponse.php @@ -9,6 +9,9 @@ use Liborm85\LoggableHttpClient\LoggableHttpClient; use Psr\Log\LoggerInterface; use Symfony\Component\HttpClient\EventSourceHttpClient; +use Symfony\Component\HttpClient\Exception\ClientException; +use Symfony\Component\HttpClient\Exception\RedirectionException; +use Symfony\Component\HttpClient\Exception\ServerException; use Symfony\Component\HttpClient\Response\StreamableInterface; use Symfony\Component\HttpClient\Response\StreamWrapper; use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; @@ -86,7 +89,15 @@ public function getStatusCode(): int */ public function getHeaders(bool $throw = true): array { - return $this->response->getHeaders($throw); + try { + return $this->response->getHeaders($throw); + } catch (ServerExceptionInterface $ex) { + throw new ServerException($this); + } catch (ClientExceptionInterface $ex) { + throw new ClientException($this); + } catch (RedirectionExceptionInterface $ex) { + throw new RedirectionException($this); + } } /** @@ -104,7 +115,15 @@ public function getContent(bool $throw = true): string $this->logResponseContent(); - return $this->response->getContent($throw); + try { + return $this->response->getContent($throw); + } catch (ServerExceptionInterface $ex) { + throw new ServerException($this); + } catch (ClientExceptionInterface $ex) { + throw new ClientException($this); + } catch (RedirectionExceptionInterface $ex) { + throw new RedirectionException($this); + } } /** @@ -123,7 +142,15 @@ public function toArray(bool $throw = true): array $this->logResponseContent(); - return $this->response->toArray($throw); + try { + return $this->response->toArray($throw); + } catch (ServerExceptionInterface $ex) { + throw new ServerException($this); + } catch (ClientExceptionInterface $ex) { + throw new ClientException($this); + } catch (RedirectionExceptionInterface $ex) { + throw new RedirectionException($this); + } } public function cancel(): void diff --git a/tests/AmpHttpClientTest.php b/tests/AmpHttpClientTest.php index f5dd363..7da5993 100644 --- a/tests/AmpHttpClientTest.php +++ b/tests/AmpHttpClientTest.php @@ -8,6 +8,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\HttpClient\AmpHttpClient; use Symfony\Component\HttpClient\Exception\TimeoutException; +use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\Test\TestHttpServer; @@ -498,6 +499,56 @@ public function testHttp404(): void $this->assertDateTime($responseTimeStart, $responseTimeFinish, $logger->logs[2]['response-time-datetime']); } + public function testHttp404ThrowHttpException(): void + { + $logger = new TestLogger(); + $client = $this->getHttpClient($logger); + + $requestTimeStart = new \DateTimeImmutable(); + $response = $client->request('POST', 'http://127.0.0.1:8057/404', ['body' => 'abc=def']); + + $responseTimeStart = new \DateTimeImmutable(); + try { + $response->getContent(); + } catch (HttpExceptionInterface $ex) { + $this->assertSame(LoggableResponse::class, get_class($ex->getResponse())); + + $this->assertSame('abc=def', (string)$ex->getResponse()->getInfo('request_body')); + $this->assertIsFloat($ex->getResponse()->getInfo('response_time')); + } + $requestTimeFinish = new \DateTimeImmutable(); + $responseTimeFinish = new \DateTimeImmutable(); + + $expected = [ + [ + 'message' => 'Request: "POST http://127.0.0.1:8057/404"', + ], + [ + 'message' => 'Response: "404 http://127.0.0.1:8057/404"', + ], + [ + 'message' => 'Response content: "404 http://127.0.0.1:8057/404"', + 'request-content' => 'abc=def', + 'response-content-json' => [ + 'SERVER_PROTOCOL' => 'HTTP/1.1', + 'SERVER_NAME' => '127.0.0.1', + 'REQUEST_URI' => '/404', + 'REQUEST_METHOD' => 'POST', + 'HTTP_ACCEPT' => '*/*', + 'HTTP_CONTENT_LENGTH' => '7', + 'HTTP_CONTENT_TYPE' => 'application/x-www-form-urlencoded', + 'HTTP_USER_AGENT' => 'Symfony HttpClient (Amp)', + 'HTTP_ACCEPT_ENCODING' => 'gzip', + 'HTTP_HOST' => '127.0.0.1:8057', + ], + ], + ]; + $this->assertSameResponseContentLog($expected, $logger->logs); + + $this->assertDateTime($requestTimeStart, $requestTimeFinish, $logger->logs[2]['request-time-datetime']); + $this->assertDateTime($responseTimeStart, $responseTimeFinish, $logger->logs[2]['response-time-datetime']); + } + public function testTimeout(): void { $logger = new TestLogger(); diff --git a/tests/CurlHttpClientTest.php b/tests/CurlHttpClientTest.php index 121111e..9f99cd2 100644 --- a/tests/CurlHttpClientTest.php +++ b/tests/CurlHttpClientTest.php @@ -8,6 +8,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\HttpClient\CurlHttpClient; use Symfony\Component\HttpClient\Exception\TimeoutException; +use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\Test\TestHttpServer; @@ -504,6 +505,56 @@ public function testHttp404(): void $this->assertDateTime($responseTimeStart, $responseTimeFinish, $logger->logs[2]['response-time-datetime']); } + public function testHttp404ThrowHttpException(): void + { + $logger = new TestLogger(); + $client = $this->getHttpClient($logger); + + $requestTimeStart = new \DateTimeImmutable(); + $response = $client->request('POST', 'http://127.0.0.1:8057/404', ['body' => 'abc=def']); + + $responseTimeStart = new \DateTimeImmutable(); + try { + $response->getContent(); + } catch (HttpExceptionInterface $ex) { + $this->assertSame(LoggableResponse::class, get_class($ex->getResponse())); + + $this->assertSame('abc=def', (string)$ex->getResponse()->getInfo('request_body')); + $this->assertIsFloat($ex->getResponse()->getInfo('response_time')); + } + $requestTimeFinish = new \DateTimeImmutable(); + $responseTimeFinish = new \DateTimeImmutable(); + + $expected = [ + [ + 'message' => 'Request: "POST http://127.0.0.1:8057/404"', + ], + [ + 'message' => 'Response: "404 http://127.0.0.1:8057/404"', + ], + [ + 'message' => 'Response content: "404 http://127.0.0.1:8057/404"', + 'request-content' => 'abc=def', + 'response-content-json' => [ + 'SERVER_PROTOCOL' => 'HTTP/1.1', + 'SERVER_NAME' => '127.0.0.1', + 'REQUEST_URI' => '/404', + 'REQUEST_METHOD' => 'POST', + 'HTTP_HOST' => '127.0.0.1:8057', + 'HTTP_ACCEPT' => '*/*', + 'HTTP_USER_AGENT' => 'Symfony HttpClient (Curl)', + 'HTTP_ACCEPT_ENCODING' => 'gzip', + 'HTTP_CONTENT_LENGTH' => '7', + 'HTTP_CONTENT_TYPE' => 'application/x-www-form-urlencoded', + ], + ], + ]; + $this->assertSameResponseContentLog($expected, $logger->logs); + + $this->assertDateTime($requestTimeStart, $requestTimeFinish, $logger->logs[2]['request-time-datetime']); + $this->assertDateTime($responseTimeStart, $responseTimeFinish, $logger->logs[2]['response-time-datetime']); + } + public function testTimeout(): void { $logger = new TestLogger(); diff --git a/tests/NativeHttpClientTest.php b/tests/NativeHttpClientTest.php index 380f36f..da74bc8 100644 --- a/tests/NativeHttpClientTest.php +++ b/tests/NativeHttpClientTest.php @@ -8,6 +8,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\HttpClient\Exception\TransportException; use Symfony\Component\HttpClient\NativeHttpClient; +use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\Test\TestHttpServer; @@ -505,6 +506,57 @@ public function testHttp404(): void $this->assertDateTime($responseTimeStart, $responseTimeFinish, $logger->logs[2]['response-time-datetime']); } + public function testHttp404ThrowHttpException(): void + { + $logger = new TestLogger(); + $client = $this->getHttpClient($logger); + + $requestTimeStart = new \DateTimeImmutable(); + $response = $client->request('POST', 'http://127.0.0.1:8057/404', ['body' => 'abc=def']); + + $responseTimeStart = new \DateTimeImmutable(); + try { + $response->getContent(); + } catch (HttpExceptionInterface $ex) { + $this->assertSame(LoggableResponse::class, get_class($ex->getResponse())); + + $this->assertSame('abc=def', (string)$ex->getResponse()->getInfo('request_body')); + $this->assertIsFloat($ex->getResponse()->getInfo('response_time')); + } + $requestTimeFinish = new \DateTimeImmutable(); + $responseTimeFinish = new \DateTimeImmutable(); + + $expected = [ + [ + 'message' => 'Request: "POST http://127.0.0.1:8057/404"', + ], + [ + 'message' => 'Response: "404 http://127.0.0.1:8057/404"', + ], + [ + 'message' => 'Response content: "404 http://127.0.0.1:8057/404"', + 'request-content' => 'abc=def', + 'response-content-json' => [ + 'SERVER_PROTOCOL' => 'HTTP/1.1', + 'SERVER_NAME' => '127.0.0.1', + 'REQUEST_URI' => '/404', + 'REQUEST_METHOD' => 'POST', + 'HTTP_CONNECTION' => 'close', + 'HTTP_ACCEPT' => '*/*', + 'HTTP_CONTENT_LENGTH' => '7', + 'HTTP_CONTENT_TYPE' => 'application/x-www-form-urlencoded', + 'HTTP_ACCEPT_ENCODING' => 'gzip', + 'HTTP_USER_AGENT' => 'Symfony HttpClient (Native)', + 'HTTP_HOST' => '127.0.0.1:8057', + ], + ], + ]; + $this->assertSameResponseContentLog($expected, $logger->logs); + + $this->assertDateTime($requestTimeStart, $requestTimeFinish, $logger->logs[2]['request-time-datetime']); + $this->assertDateTime($responseTimeStart, $responseTimeFinish, $logger->logs[2]['response-time-datetime']); + } + public function testTimeout(): void { $logger = new TestLogger(); diff --git a/tests/RetryableHttpClientTest.php b/tests/RetryableHttpClientTest.php index 4068bac..cbf84ee 100644 --- a/tests/RetryableHttpClientTest.php +++ b/tests/RetryableHttpClientTest.php @@ -3,11 +3,13 @@ namespace Liborm85\LoggableHttpClient\Tests; use Liborm85\LoggableHttpClient\LoggableHttpClient; +use Liborm85\LoggableHttpClient\Response\LoggableResponse; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Component\HttpClient\Retry\GenericRetryStrategy; use Symfony\Component\HttpClient\RetryableHttpClient; +use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface; class RetryableHttpClientTest extends TestCase { @@ -68,4 +70,107 @@ public function testRetry(): void $this->assertDateTime($responseTimeStart, $responseTimeFinish, $logger->logs[2]['response-time-datetime']); } + public function testHttp404(): void + { + $logger = new TestLogger(); + + $mockClient = new MockHttpClient( + [ + new MockResponse('mock A', ['http_code' => 500]), + new MockResponse('mock B', ['http_code' => 500]), + new MockResponse('mock C', ['http_code' => 404]), + ] + ); + + $loggableClient = new LoggableHttpClient($mockClient); + $loggableClient->setLogger($logger); + + $client = new RetryableHttpClient( + $loggableClient, + new GenericRetryStrategy([500], 0), + 3 + ); + + $requestTimeStart = new \DateTimeImmutable(); + $response = $client->request('POST', 'https://example.com/foo-bar', ['body' => 'abc=def']); + + $responseTimeStart = new \DateTimeImmutable(); + $body = $response->getContent(false); + $requestTimeFinish = new \DateTimeImmutable(); + $responseTimeFinish = new \DateTimeImmutable(); + + $this->assertSame('mock C', $body); + + $expected = [ + [ + 'message' => 'Response content (canceled): "500 https://example.com/foo-bar"', + 'request-content' => 'abc=def', + 'response-content' => null, + ], + [ + 'message' => 'Response content (canceled): "500 https://example.com/foo-bar"', + 'request-content' => 'abc=def', + 'response-content' => null, + ], + [ + 'message' => 'Response content: "404 https://example.com/foo-bar"', + 'request-content' => 'abc=def', + 'response-content' => 'mock C', + ], + ]; + $this->assertSameResponseContentLog($expected, $logger->logs); + + $this->assertDateTime($requestTimeStart, $requestTimeFinish, $logger->logs[2]['request-time-datetime']); + $this->assertDateTime($responseTimeStart, $responseTimeFinish, $logger->logs[2]['response-time-datetime']); + } + + public function testHttp404ThrowHttpException(): void + { + $logger = new TestLogger(); + + $mockClient = new MockHttpClient( + [ + new MockResponse('mock A', ['http_code' => 500]), + new MockResponse('mock B', ['http_code' => 500]), + new MockResponse('mock C', ['http_code' => 404]), + ] + ); + + $retryableClient = new RetryableHttpClient( + $mockClient, + new GenericRetryStrategy([500], 0), + 3 + ); + + $client = new LoggableHttpClient($retryableClient); + $client->setLogger($logger); + + $requestTimeStart = new \DateTimeImmutable(); + $response = $client->request('POST', 'https://example.com/foo-bar', ['body' => 'abc=def']); + + $responseTimeStart = new \DateTimeImmutable(); + try { + $response->getContent(); + } catch (HttpExceptionInterface $ex) { + $this->assertSame(LoggableResponse::class, get_class($ex->getResponse())); + + $this->assertSame('abc=def', (string)$ex->getResponse()->getInfo('request_body')); + $this->assertIsFloat($ex->getResponse()->getInfo('response_time')); + } + $requestTimeFinish = new \DateTimeImmutable(); + $responseTimeFinish = new \DateTimeImmutable(); + + $expected = [ + [ + 'message' => 'Response content: "404 https://example.com/foo-bar"', + 'request-content' => 'abc=def', + 'response-content' => 'mock C', + ], + ]; + $this->assertSameResponseContentLog($expected, $logger->logs); + + $this->assertDateTime($requestTimeStart, $requestTimeFinish, $logger->logs[0]['request-time-datetime']); + $this->assertDateTime($responseTimeStart, $responseTimeFinish, $logger->logs[0]['response-time-datetime']); + } + }