From 51273fc520fc622551c926d012b7a74e6891d66d Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Thu, 13 Oct 2016 09:37:59 +0200 Subject: [PATCH] Removed the HTTPClientPool feature --- CHANGELOG.md | 4 - .../LeastUsedClientPoolSpec.php | 93 -------- spec/HttpClientPool/RandomClientPoolSpec.php | 71 ------ .../RoundRobinClientPoolSpec.php | 83 ------- spec/HttpClientPoolItemSpec.php | 213 ------------------ src/Exception/HttpClientNotFoundException.php | 14 -- src/HttpClientPool.php | 57 ----- src/HttpClientPool/LeastUsedClientPool.php | 45 ---- src/HttpClientPool/RandomClientPool.php | 31 --- src/HttpClientPool/RoundRobinClientPool.php | 41 ---- src/HttpClientPoolItem.php | 167 -------------- 11 files changed, 819 deletions(-) delete mode 100644 spec/HttpClientPool/LeastUsedClientPoolSpec.php delete mode 100644 spec/HttpClientPool/RandomClientPoolSpec.php delete mode 100644 spec/HttpClientPool/RoundRobinClientPoolSpec.php delete mode 100644 spec/HttpClientPoolItemSpec.php delete mode 100644 src/Exception/HttpClientNotFoundException.php delete mode 100644 src/HttpClientPool.php delete mode 100644 src/HttpClientPool/LeastUsedClientPool.php delete mode 100644 src/HttpClientPool/RandomClientPool.php delete mode 100644 src/HttpClientPool/RoundRobinClientPool.php delete mode 100644 src/HttpClientPoolItem.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bfdbef..2751c7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,6 @@ ## 1.3.0 - Unreleased -### Added - -- Add HttpClientPool client to leverage load balancing and fallback mechanism [see the documentation](http://docs.php-http.org/en/latest/components/client-common.html) for more details - ### Changed - Fix Emulated Trait to use Http based promise which respect the HttpAsyncClient interface diff --git a/spec/HttpClientPool/LeastUsedClientPoolSpec.php b/spec/HttpClientPool/LeastUsedClientPoolSpec.php deleted file mode 100644 index a976c31..0000000 --- a/spec/HttpClientPool/LeastUsedClientPoolSpec.php +++ /dev/null @@ -1,93 +0,0 @@ -shouldHaveType('Http\Client\Common\HttpClientPool\LeastUsedClientPool'); - } - - public function it_is_an_http_client() - { - $this->shouldImplement('Http\Client\HttpClient'); - } - - public function it_is_an_async_http_client() - { - $this->shouldImplement('Http\Client\HttpAsyncClient'); - } - - public function it_throw_exception_with_no_client(RequestInterface $request) - { - $this->shouldThrow('Http\Client\Common\Exception\HttpClientNotFoundException')->duringSendRequest($request); - $this->shouldThrow('Http\Client\Common\Exception\HttpClientNotFoundException')->duringSendAsyncRequest($request); - } - - public function it_sends_request(HttpClient $httpClient, RequestInterface $request, ResponseInterface $response) - { - $this->addHttpClient($httpClient); - $httpClient->sendRequest($request)->willReturn($response); - - $this->sendRequest($request)->shouldReturn($response); - } - - public function it_sends_async_request(HttpAsyncClient $httpAsyncClient, RequestInterface $request, Promise $promise) - { - $this->addHttpClient($httpAsyncClient); - $httpAsyncClient->sendAsyncRequest($request)->willReturn($promise); - $promise->then(Argument::type('callable'), Argument::type('callable'))->willReturn($promise); - - $this->sendAsyncRequest($request)->shouldReturn($promise); - } - - public function it_throw_exception_if_no_more_enable_client(HttpClient $client, RequestInterface $request) - { - $this->addHttpClient($client); - $client->sendRequest($request)->willThrow('Http\Client\Exception\HttpException'); - - $this->shouldThrow('Http\Client\Exception\HttpException')->duringSendRequest($request); - $this->shouldThrow('Http\Client\Common\Exception\HttpClientNotFoundException')->duringSendRequest($request); - } - - public function it_reenable_client(HttpClient $client, RequestInterface $request) - { - $this->addHttpClient(new HttpClientPoolItem($client->getWrappedObject(), 0)); - $client->sendRequest($request)->willThrow('Http\Client\Exception\HttpException'); - - $this->shouldThrow('Http\Client\Exception\HttpException')->duringSendRequest($request); - $this->shouldThrow('Http\Client\Exception\HttpException')->duringSendRequest($request); - } - - public function it_uses_the_lowest_request_client(HttpClientPoolItem $client1, HttpClientPoolItem $client2, RequestInterface $request, ResponseInterface $response) - { - if (extension_loaded('xdebug')) { - throw new SkippingException('This test fail when xdebug is enable on PHP < 7'); - } - - $this->addHttpClient($client1); - $this->addHttpClient($client2); - - $client1->getSendingRequestCount()->willReturn(10); - $client2->getSendingRequestCount()->willReturn(2); - - $client1->isDisabled()->willReturn(false); - $client2->isDisabled()->willReturn(false); - - $client1->sendRequest($request)->shouldNotBeCalled(); - $client2->sendRequest($request)->willReturn($response); - - $this->sendRequest($request)->shouldReturn($response); - } -} diff --git a/spec/HttpClientPool/RandomClientPoolSpec.php b/spec/HttpClientPool/RandomClientPoolSpec.php deleted file mode 100644 index 4054d82..0000000 --- a/spec/HttpClientPool/RandomClientPoolSpec.php +++ /dev/null @@ -1,71 +0,0 @@ -shouldHaveType('Http\Client\Common\HttpClientPool\RandomClientPool'); - } - - public function it_is_an_http_client() - { - $this->shouldImplement('Http\Client\HttpClient'); - } - - public function it_is_an_async_http_client() - { - $this->shouldImplement('Http\Client\HttpAsyncClient'); - } - - public function it_throw_exception_with_no_client(RequestInterface $request) - { - $this->shouldThrow('Http\Client\Common\Exception\HttpClientNotFoundException')->duringSendRequest($request); - $this->shouldThrow('Http\Client\Common\Exception\HttpClientNotFoundException')->duringSendAsyncRequest($request); - } - - public function it_sends_request(HttpClient $httpClient, RequestInterface $request, ResponseInterface $response) - { - $this->addHttpClient($httpClient); - $httpClient->sendRequest($request)->willReturn($response); - - $this->sendRequest($request)->shouldReturn($response); - } - - public function it_sends_async_request(HttpAsyncClient $httpAsyncClient, RequestInterface $request, Promise $promise) - { - $this->addHttpClient($httpAsyncClient); - $httpAsyncClient->sendAsyncRequest($request)->willReturn($promise); - $promise->then(Argument::type('callable'), Argument::type('callable'))->willReturn($promise); - - $this->sendAsyncRequest($request)->shouldReturn($promise); - } - - public function it_throw_exception_if_no_more_enable_client(HttpClient $client, RequestInterface $request) - { - $this->addHttpClient($client); - $client->sendRequest($request)->willThrow('Http\Client\Exception\HttpException'); - - $this->shouldThrow('Http\Client\Exception\HttpException')->duringSendRequest($request); - $this->shouldThrow('Http\Client\Common\Exception\HttpClientNotFoundException')->duringSendRequest($request); - } - - public function it_reenable_client(HttpClient $client, RequestInterface $request) - { - $this->addHttpClient(new HttpClientPoolItem($client->getWrappedObject(), 0)); - $client->sendRequest($request)->willThrow('Http\Client\Exception\HttpException'); - - $this->shouldThrow('Http\Client\Exception\HttpException')->duringSendRequest($request); - $this->shouldThrow('Http\Client\Exception\HttpException')->duringSendRequest($request); - } -} diff --git a/spec/HttpClientPool/RoundRobinClientPoolSpec.php b/spec/HttpClientPool/RoundRobinClientPoolSpec.php deleted file mode 100644 index 48c2d01..0000000 --- a/spec/HttpClientPool/RoundRobinClientPoolSpec.php +++ /dev/null @@ -1,83 +0,0 @@ -shouldHaveType('Http\Client\Common\HttpClientPool\RoundRobinClientPool'); - } - - public function it_is_an_http_client() - { - $this->shouldImplement('Http\Client\HttpClient'); - } - - public function it_is_an_async_http_client() - { - $this->shouldImplement('Http\Client\HttpAsyncClient'); - } - - public function it_throw_exception_with_no_client(RequestInterface $request) - { - $this->shouldThrow('Http\Client\Common\Exception\HttpClientNotFoundException')->duringSendRequest($request); - $this->shouldThrow('Http\Client\Common\Exception\HttpClientNotFoundException')->duringSendAsyncRequest($request); - } - - public function it_sends_request(HttpClient $httpClient, RequestInterface $request, ResponseInterface $response) - { - $this->addHttpClient($httpClient); - $httpClient->sendRequest($request)->willReturn($response); - - $this->sendRequest($request)->shouldReturn($response); - } - - public function it_sends_async_request(HttpAsyncClient $httpAsyncClient, RequestInterface $request, Promise $promise) - { - $this->addHttpClient($httpAsyncClient); - $httpAsyncClient->sendAsyncRequest($request)->willReturn($promise); - $promise->then(Argument::type('callable'), Argument::type('callable'))->willReturn($promise); - - $this->sendAsyncRequest($request)->shouldReturn($promise); - } - - public function it_throw_exception_if_no_more_enable_client(HttpClient $client, RequestInterface $request) - { - $this->addHttpClient($client); - $client->sendRequest($request)->willThrow('Http\Client\Exception\HttpException'); - - $this->shouldThrow('Http\Client\Exception\HttpException')->duringSendRequest($request); - $this->shouldThrow('Http\Client\Common\Exception\HttpClientNotFoundException')->duringSendRequest($request); - } - - public function it_reenable_client(HttpClient $client, RequestInterface $request) - { - $this->addHttpClient(new HttpClientPoolItem($client->getWrappedObject(), 0)); - $client->sendRequest($request)->willThrow('Http\Client\Exception\HttpException'); - - $this->shouldThrow('Http\Client\Exception\HttpException')->duringSendRequest($request); - $this->shouldThrow('Http\Client\Exception\HttpException')->duringSendRequest($request); - } - - public function it_round_between_clients(HttpClient $client1, HttpClient $client2, RequestInterface $request, ResponseInterface $response) - { - $this->addHttpClient($client1); - $this->addHttpClient($client2); - - $client1->sendRequest($request)->willReturn($response); - $client2->sendRequest($request)->willReturn($response); - - $this->sendRequest($request)->shouldReturn($response); - $this->sendRequest($request)->shouldReturn($response); - } -} diff --git a/spec/HttpClientPoolItemSpec.php b/spec/HttpClientPoolItemSpec.php deleted file mode 100644 index 059ec8d..0000000 --- a/spec/HttpClientPoolItemSpec.php +++ /dev/null @@ -1,213 +0,0 @@ -beConstructedWith($httpClient); - } - - public function it_is_an_http_client() - { - $this->shouldImplement('Http\Client\HttpClient'); - } - - public function it_is_an_async_http_client() - { - $this->shouldImplement('Http\Client\HttpAsyncClient'); - } - - public function it_sends_request(HttpClient $httpClient, RequestInterface $request, ResponseInterface $response) - { - $httpClient->sendRequest($request)->willReturn($response); - - $this->sendRequest($request)->shouldReturn($response); - } - - public function it_sends_async_request(HttpAsyncClient $httpAsyncClient, RequestInterface $request, Promise $promise) - { - $this->beConstructedWith($httpAsyncClient); - - $httpAsyncClient->sendAsyncRequest($request)->willReturn($promise); - $promise->then(Argument::type('callable'), Argument::type('callable'))->willReturn($promise); - - $this->sendAsyncRequest($request)->shouldReturn($promise); - } - - public function it_disable_himself_on_send_request(HttpClient $httpClient, RequestInterface $request) - { - $exception = new TransferException(); - $httpClient->sendRequest($request)->willThrow($exception); - $this->shouldThrow($exception)->duringSendRequest($request); - $this->isDisabled()->shouldReturn(true); - $this->shouldThrow('Http\Client\Exception\RequestException')->duringSendRequest($request); - } - - public function it_disable_himself_on_send_async_request(HttpAsyncClient $httpAsyncClient, RequestInterface $request) - { - $this->beConstructedWith($httpAsyncClient); - - $promise = new HttpRejectedPromise(new TransferException()); - $httpAsyncClient->sendAsyncRequest($request)->willReturn($promise); - - $this->sendAsyncRequest($request)->shouldReturnAnInstanceOf('Http\Client\Promise\HttpRejectedPromise'); - $this->isDisabled()->shouldReturn(true); - $this->shouldThrow('Http\Client\Exception\RequestException')->duringSendAsyncRequest($request); - } - - public function it_reactivate_himself_on_send_request(HttpClient $httpClient, RequestInterface $request) - { - $this->beConstructedWith($httpClient, 0); - - $exception = new TransferException(); - $httpClient->sendRequest($request)->willThrow($exception); - - $this->shouldThrow($exception)->duringSendRequest($request); - $this->isDisabled()->shouldReturn(false); - $this->shouldThrow($exception)->duringSendRequest($request); - } - - public function it_reactivate_himself_on_send_async_request(HttpAsyncClient $httpAsyncClient, RequestInterface $request) - { - $this->beConstructedWith($httpAsyncClient, 0); - - $promise = new HttpRejectedPromise(new TransferException()); - $httpAsyncClient->sendAsyncRequest($request)->willReturn($promise); - - $this->sendAsyncRequest($request)->shouldReturnAnInstanceOf('Http\Client\Promise\HttpRejectedPromise'); - $this->isDisabled()->shouldReturn(false); - $this->sendAsyncRequest($request)->shouldReturnAnInstanceOf('Http\Client\Promise\HttpRejectedPromise'); - } - - public function it_increments_request_count(HttpAsyncClient $httpAsyncClient, RequestInterface $request, ResponseInterface $response) - { - $this->beConstructedWith($httpAsyncClient, 0); - - $promise = new NotResolvingPromise($response->getWrappedObject()); - $httpAsyncClient->sendAsyncRequest($request)->willReturn($promise); - - $this->getSendingRequestCount()->shouldReturn(0); - $this->sendAsyncRequest($request)->shouldReturn($promise); - $this->getSendingRequestCount()->shouldReturn(1); - $this->sendAsyncRequest($request)->shouldReturn($promise); - $this->getSendingRequestCount()->shouldReturn(2); - } - - public function it_decrements_request_count(HttpAsyncClient $httpAsyncClient, RequestInterface $request, ResponseInterface $response) - { - $this->beConstructedWith($httpAsyncClient, 0); - - $promise = new NotResolvingPromise($response->getWrappedObject()); - $httpAsyncClient->sendAsyncRequest($request)->willReturn($promise); - - $this->getSendingRequestCount()->shouldReturn(0); - $this->sendAsyncRequest($request)->shouldReturn($promise); - $this->getSendingRequestCount()->shouldReturn(1); - - $promise->wait(false); - - $this->getSendingRequestCount()->shouldReturn(0); - } -} - -class NotResolvingPromise implements Promise -{ - private $queue = []; - - private $state = Promise::PENDING; - - private $response; - - private $exception; - - public function __construct(ResponseInterface $response = null, Exception $exception = null) - { - $this->response = $response; - $this->exception = $exception; - } - - public function then(callable $onFulfilled = null, callable $onRejected = null) - { - $this->queue[] = [ - $onFulfilled, - $onRejected, - ]; - - return $this; - } - - public function getState() - { - return $this->state; - } - - public function wait($unwrap = true) - { - if ($this->state === Promise::FULFILLED) { - if (!$unwrap) { - return; - } - - return $this->response; - } - - if ($this->state === Promise::REJECTED) { - if (!$unwrap) { - return; - } - - throw $this->exception; - } - - while (count($this->queue) > 0) { - $callbacks = array_shift($this->queue); - - if ($this->response !== null) { - try { - $this->response = $callbacks[0]($this->response); - $this->exception = null; - } catch (Exception $exception) { - $this->response = null; - $this->exception = $exception; - } - } elseif ($this->exception !== null) { - try { - $this->response = $callbacks[1]($this->exception); - $this->exception = null; - } catch (Exception $exception) { - $this->response = null; - $this->exception = $exception; - } - } - } - - if ($this->response !== null) { - $this->state = Promise::FULFILLED; - - if ($unwrap) { - return $this->response; - } - } - - if ($this->exception !== null) { - $this->state = Promise::REJECTED; - - if ($unwrap) { - throw $this->exception; - } - } - } -} diff --git a/src/Exception/HttpClientNotFoundException.php b/src/Exception/HttpClientNotFoundException.php deleted file mode 100644 index 5d33f98..0000000 --- a/src/Exception/HttpClientNotFoundException.php +++ /dev/null @@ -1,14 +0,0 @@ - - */ -class HttpClientNotFoundException extends TransferException -{ -} diff --git a/src/HttpClientPool.php b/src/HttpClientPool.php deleted file mode 100644 index 00fab74..0000000 --- a/src/HttpClientPool.php +++ /dev/null @@ -1,57 +0,0 @@ -clientPool[] = $client; - } - - /** - * Return an http client given a specific strategy. - * - * @throws HttpClientNotFoundException When no http client has been found into the pool - * - * @return HttpClientPoolItem Return a http client that can do both sync or async - */ - abstract protected function chooseHttpClient(); - - /** - * {@inheritdoc} - */ - public function sendAsyncRequest(RequestInterface $request) - { - return $this->chooseHttpClient()->sendAsyncRequest($request); - } - - /** - * {@inheritdoc} - */ - public function sendRequest(RequestInterface $request) - { - return $this->chooseHttpClient()->sendRequest($request); - } -} diff --git a/src/HttpClientPool/LeastUsedClientPool.php b/src/HttpClientPool/LeastUsedClientPool.php deleted file mode 100644 index 6299cce..0000000 --- a/src/HttpClientPool/LeastUsedClientPool.php +++ /dev/null @@ -1,45 +0,0 @@ - - */ -final class LeastUsedClientPool extends HttpClientPool -{ - /** - * {@inheritdoc} - */ - protected function chooseHttpClient() - { - $clientPool = array_filter($this->clientPool, function (HttpClientPoolItem $clientPoolItem) { - return !$clientPoolItem->isDisabled(); - }); - - if (0 === count($clientPool)) { - throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool'); - } - - usort($clientPool, function (HttpClientPoolItem $clientA, HttpClientPoolItem $clientB) { - if ($clientA->getSendingRequestCount() === $clientB->getSendingRequestCount()) { - return 0; - } - - if ($clientA->getSendingRequestCount() < $clientB->getSendingRequestCount()) { - return -1; - } - - return 1; - }); - - return reset($clientPool); - } -} diff --git a/src/HttpClientPool/RandomClientPool.php b/src/HttpClientPool/RandomClientPool.php deleted file mode 100644 index 3255f86..0000000 --- a/src/HttpClientPool/RandomClientPool.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ -final class RandomClientPool extends HttpClientPool -{ - /** - * {@inheritdoc} - */ - protected function chooseHttpClient() - { - $clientPool = array_filter($this->clientPool, function (HttpClientPoolItem $clientPoolItem) { - return !$clientPoolItem->isDisabled(); - }); - - if (0 === count($clientPool)) { - throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool'); - } - - return $clientPool[array_rand($clientPool)]; - } -} diff --git a/src/HttpClientPool/RoundRobinClientPool.php b/src/HttpClientPool/RoundRobinClientPool.php deleted file mode 100644 index 8d8e40a..0000000 --- a/src/HttpClientPool/RoundRobinClientPool.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ -final class RoundRobinClientPool extends HttpClientPool -{ - /** - * {@inheritdoc} - */ - protected function chooseHttpClient() - { - $last = current($this->clientPool); - - do { - $client = next($this->clientPool); - - if (false === $client) { - $client = reset($this->clientPool); - - if (false === $client) { - throw new HttpClientNotFoundException('Cannot choose a http client as there is no one present in the pool'); - } - } - - // Case when there is only one and the last one has been disabled - if ($last === $client && $client->isDisabled()) { - throw new HttpClientNotFoundException('Cannot choose a http client as there is no one enabled in the pool'); - } - } while ($client->isDisabled()); - - return $client; - } -} diff --git a/src/HttpClientPoolItem.php b/src/HttpClientPoolItem.php deleted file mode 100644 index cd3c2db..0000000 --- a/src/HttpClientPoolItem.php +++ /dev/null @@ -1,167 +0,0 @@ - - */ -class HttpClientPoolItem implements HttpClient, HttpAsyncClient -{ - /** @var int Number of request this client is currently sending */ - private $sendingRequestCount = 0; - - /** @var \DateTime|null Time when this client has been disabled or null if enable */ - private $disabledAt; - - /** @var int|null Number of seconds after this client is reenable, by default null: never reenable this client */ - private $reenableAfter; - - /** @var FlexibleHttpClient A http client responding to async and sync request */ - private $client; - - /** - * {@inheritdoc} - * - * @param null|int $reenableAfter Number of seconds after this client is reenable - */ - public function __construct($client, $reenableAfter = null) - { - $this->client = new FlexibleHttpClient($client); - $this->reenableAfter = $reenableAfter; - } - - /** - * {@inheritdoc} - */ - public function sendRequest(RequestInterface $request) - { - if ($this->isDisabled()) { - throw new Exception\RequestException('Cannot send the request as this client has been disabled', $request); - } - - try { - $this->incrementRequestCount(); - $response = $this->client->sendRequest($request); - $this->decrementRequestCount(); - } catch (Exception $e) { - $this->disable(); - $this->decrementRequestCount(); - - throw $e; - } - - return $response; - } - - /** - * {@inheritdoc} - */ - public function sendAsyncRequest(RequestInterface $request) - { - if ($this->isDisabled()) { - throw new Exception\RequestException('Cannot send the request as this client has been disabled', $request); - } - - $this->incrementRequestCount(); - - return $this->client->sendAsyncRequest($request)->then(function ($response) { - $this->decrementRequestCount(); - - return $response; - }, function ($exception) { - $this->disable(); - $this->decrementRequestCount(); - - throw $exception; - }); - } - - /** - * Whether this client is disabled or not. - * - * Will also reactivate this client if possible - * - * @return bool - */ - public function isDisabled() - { - $disabledAt = $this->getDisabledAt(); - - if (null !== $this->reenableAfter && null !== $disabledAt) { - // Reenable after a certain time - $now = new \DateTime(); - - if (($now->getTimestamp() - $disabledAt->getTimestamp()) >= $this->reenableAfter) { - $this->enable(); - - return false; - } - - return true; - } - - return null !== $disabledAt; - } - - /** - * Get current number of request that is send by the underlying http client. - * - * @return int - */ - public function getSendingRequestCount() - { - return $this->sendingRequestCount; - } - - /** - * Return when this client has been disabled or null if it's enabled. - * - * @return \DateTime|null - */ - private function getDisabledAt() - { - return $this->disabledAt; - } - - /** - * Increment the request count. - */ - private function incrementRequestCount() - { - ++$this->sendingRequestCount; - } - - /** - * Decrement the request count. - */ - private function decrementRequestCount() - { - --$this->sendingRequestCount; - } - - /** - * Enable the current client. - */ - private function enable() - { - $this->disabledAt = null; - } - - /** - * Disable the current client. - */ - private function disable() - { - $this->disabledAt = new \DateTime('now'); - } -}