diff --git a/src/Symfony/Component/HttpClient/CachingHttpClient.php b/src/Symfony/Component/HttpClient/CachingHttpClient.php index 75f6d5d918c26..680a589a86b40 100644 --- a/src/Symfony/Component/HttpClient/CachingHttpClient.php +++ b/src/Symfony/Component/HttpClient/CachingHttpClient.php @@ -20,6 +20,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; use Symfony\Contracts\HttpClient\ResponseStreamInterface; +use Symfony\Contracts\Service\ResetInterface; /** * Adds caching on top of an HTTP client. @@ -30,7 +31,7 @@ * * @author Nicolas Grekas */ -class CachingHttpClient implements HttpClientInterface +class CachingHttpClient implements HttpClientInterface, ResetInterface { use HttpClientTrait; @@ -141,4 +142,11 @@ public function stream($responses, float $timeout = null): ResponseStreamInterfa yield $this->client->stream($clientResponses, $timeout); })()); } + + public function reset() + { + if ($this->client instanceof ResetInterface) { + $this->client->reset(); + } + } } diff --git a/src/Symfony/Component/HttpClient/DecoratorTrait.php b/src/Symfony/Component/HttpClient/DecoratorTrait.php index cc5a2feb4d6bc..790fc32a59aab 100644 --- a/src/Symfony/Component/HttpClient/DecoratorTrait.php +++ b/src/Symfony/Component/HttpClient/DecoratorTrait.php @@ -14,6 +14,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; use Symfony\Contracts\HttpClient\ResponseStreamInterface; +use Symfony\Contracts\Service\ResetInterface; /** * Eases with writing decorators. @@ -55,4 +56,11 @@ public function withOptions(array $options): self return $clone; } + + public function reset() + { + if ($this->client instanceof ResetInterface) { + $this->client->reset(); + } + } } diff --git a/src/Symfony/Component/HttpClient/EventSourceHttpClient.php b/src/Symfony/Component/HttpClient/EventSourceHttpClient.php index 7ac8940b6cada..60e4e821d1ee7 100644 --- a/src/Symfony/Component/HttpClient/EventSourceHttpClient.php +++ b/src/Symfony/Component/HttpClient/EventSourceHttpClient.php @@ -19,12 +19,13 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; +use Symfony\Contracts\Service\ResetInterface; /** * @author Antoine Bluchet * @author Nicolas Grekas */ -final class EventSourceHttpClient implements HttpClientInterface +final class EventSourceHttpClient implements HttpClientInterface, ResetInterface { use AsyncDecoratorTrait, HttpClientTrait { AsyncDecoratorTrait::withOptions insteadof HttpClientTrait; diff --git a/src/Symfony/Component/HttpClient/HttplugClient.php b/src/Symfony/Component/HttpClient/HttplugClient.php index 8f3a8ee3f0631..0ff4fa03e09c4 100644 --- a/src/Symfony/Component/HttpClient/HttplugClient.php +++ b/src/Symfony/Component/HttpClient/HttplugClient.php @@ -39,6 +39,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; +use Symfony\Contracts\Service\ResetInterface; if (!interface_exists(HttplugInterface::class)) { throw new \LogicException('You cannot use "Symfony\Component\HttpClient\HttplugClient" as the "php-http/httplug" package is not installed. Try running "composer require php-http/httplug".'); @@ -56,7 +57,7 @@ * * @author Nicolas Grekas */ -final class HttplugClient implements HttplugInterface, HttpAsyncClient, RequestFactory, StreamFactory, UriFactory +final class HttplugClient implements HttplugInterface, HttpAsyncClient, RequestFactory, StreamFactory, UriFactory, ResetInterface { private $client; private $responseFactory; @@ -238,6 +239,13 @@ public function __destruct() $this->wait(); } + public function reset() + { + if ($this->client instanceof ResetInterface) { + $this->client->reset(); + } + } + private function sendPsr7Request(RequestInterface $request, bool $buffer = null): ResponseInterface { try { diff --git a/src/Symfony/Component/HttpClient/Internal/NativeClientState.php b/src/Symfony/Component/HttpClient/Internal/NativeClientState.php index 4e3684ad30855..20b2727f5958c 100644 --- a/src/Symfony/Component/HttpClient/Internal/NativeClientState.php +++ b/src/Symfony/Component/HttpClient/Internal/NativeClientState.php @@ -37,4 +37,11 @@ public function __construct() { $this->id = random_int(\PHP_INT_MIN, \PHP_INT_MAX); } + + public function reset() + { + $this->responseCount = 0; + $this->dnsCache = []; + $this->hosts = []; + } } diff --git a/src/Symfony/Component/HttpClient/MockHttpClient.php b/src/Symfony/Component/HttpClient/MockHttpClient.php index 361fe29f1519e..fecba0ee56ddb 100644 --- a/src/Symfony/Component/HttpClient/MockHttpClient.php +++ b/src/Symfony/Component/HttpClient/MockHttpClient.php @@ -17,13 +17,14 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; use Symfony\Contracts\HttpClient\ResponseStreamInterface; +use Symfony\Contracts\Service\ResetInterface; /** * A test-friendly HttpClient that doesn't make actual HTTP requests. * * @author Nicolas Grekas */ -class MockHttpClient implements HttpClientInterface +class MockHttpClient implements HttpClientInterface, ResetInterface { use HttpClientTrait; @@ -115,4 +116,9 @@ public function withOptions(array $options): self return $clone; } + + public function reset() + { + $this->requestsCount = 0; + } } diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index b0910cf784d73..ef931539521fa 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -21,6 +21,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; use Symfony\Contracts\HttpClient\ResponseStreamInterface; +use Symfony\Contracts\Service\ResetInterface; /** * A portable implementation of the HttpClientInterface contracts based on PHP stream wrappers. @@ -30,7 +31,7 @@ * * @author Nicolas Grekas */ -final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterface +final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterface, ResetInterface { use HttpClientTrait; use LoggerAwareTrait; @@ -261,6 +262,11 @@ public function stream($responses, float $timeout = null): ResponseStreamInterfa return new ResponseStream(NativeResponse::stream($responses, $timeout)); } + public function reset() + { + $this->multi->reset(); + } + private static function getBodyAsString($body): string { if (\is_resource($body)) { diff --git a/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php b/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php index 7a5ed6bfbff16..911cce9da4b9a 100644 --- a/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php +++ b/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php @@ -19,13 +19,14 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; use Symfony\Contracts\HttpClient\ResponseStreamInterface; +use Symfony\Contracts\Service\ResetInterface; /** * Decorator that blocks requests to private networks by default. * * @author Hallison Boaventura */ -final class NoPrivateNetworkHttpClient implements HttpClientInterface, LoggerAwareInterface +final class NoPrivateNetworkHttpClient implements HttpClientInterface, LoggerAwareInterface, ResetInterface { use HttpClientTrait; @@ -121,4 +122,11 @@ public function withOptions(array $options): self return $clone; } + + public function reset() + { + if ($this->client instanceof ResetInterface) { + $this->client->reset(); + } + } } diff --git a/src/Symfony/Component/HttpClient/Psr18Client.php b/src/Symfony/Component/HttpClient/Psr18Client.php index 40595b5b7f5cb..dbd8864a5bd29 100644 --- a/src/Symfony/Component/HttpClient/Psr18Client.php +++ b/src/Symfony/Component/HttpClient/Psr18Client.php @@ -31,6 +31,7 @@ use Symfony\Component\HttpClient\Response\StreamWrapper; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\Service\ResetInterface; if (!interface_exists(RequestFactoryInterface::class)) { throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\Psr18Client" as the "psr/http-factory" package is not installed. Try running "composer require nyholm/psr7".'); @@ -49,7 +50,7 @@ * * @author Nicolas Grekas */ -final class Psr18Client implements ClientInterface, RequestFactoryInterface, StreamFactoryInterface, UriFactoryInterface +final class Psr18Client implements ClientInterface, RequestFactoryInterface, StreamFactoryInterface, UriFactoryInterface, ResetInterface { private $client; private $responseFactory; @@ -190,6 +191,13 @@ public function createUri(string $uri = ''): UriInterface throw new \LogicException(sprintf('You cannot use "%s()" as the "nyholm/psr7" package is not installed. Try running "composer require nyholm/psr7".', __METHOD__)); } + + public function reset() + { + if ($this->client instanceof ResetInterface) { + $this->client->reset(); + } + } } /** diff --git a/src/Symfony/Component/HttpClient/RetryableHttpClient.php b/src/Symfony/Component/HttpClient/RetryableHttpClient.php index 97b48da423a85..0194224a5baa8 100644 --- a/src/Symfony/Component/HttpClient/RetryableHttpClient.php +++ b/src/Symfony/Component/HttpClient/RetryableHttpClient.php @@ -21,13 +21,14 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; +use Symfony\Contracts\Service\ResetInterface; /** * Automatically retries failing HTTP requests. * * @author Jérémy Derussé */ -class RetryableHttpClient implements HttpClientInterface +class RetryableHttpClient implements HttpClientInterface, ResetInterface { use AsyncDecoratorTrait; diff --git a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php index 8346f16e4c91f..12bc2d88a6f76 100644 --- a/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/MockHttpClientTest.php @@ -418,4 +418,16 @@ public function __toString() $this->assertSame('foo=bar', $response->getRequestOptions()['body']); } + + public function testResetsRequestCount() + { + $client = new MockHttpClient([new MockResponse()]); + $this->assertSame(0, $client->getRequestsCount()); + + $client->request('POST', '/url', ['body' => 'payload']); + + $this->assertSame(1, $client->getRequestsCount()); + $client->reset(); + $this->assertSame(0, $client->getRequestsCount()); + } }