diff --git a/CHANGELOG.md b/CHANGELOG.md index a213e69..c42d9c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - Add a flexible http client providing both contract, and only emulating what's necessary +- HTTP Client Router: route requests to underlying clients ## 1.0.0 - 2016-01-27 diff --git a/composer.json b/composer.json index 99be135..6fc8d18 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,8 @@ "require": { "php": ">=5.4", "php-http/httplug": "^1.0", - "php-http/message-factory": "^1.0" + "php-http/message-factory": "^1.0", + "php-http/message": "^1.2" }, "require-dev": { "phpspec/phpspec": "^2.4", diff --git a/spec/HttpClientRouterSpec.php b/spec/HttpClientRouterSpec.php new file mode 100644 index 0000000..1119722 --- /dev/null +++ b/spec/HttpClientRouterSpec.php @@ -0,0 +1,63 @@ +shouldHaveType('Http\Client\Common\HttpClientRouter'); + } + + function it_is_an_http_client() + { + $this->shouldImplement('Http\Client\HttpClient'); + } + + function it_is_an_async_http_client() + { + $this->shouldImplement('Http\Client\HttpAsyncClient'); + } + + function it_send_request(RequestMatcher $matcher, HttpClient $client, RequestInterface $request, ResponseInterface $response) + { + $this->addClient($client, $matcher); + $matcher->matches($request)->willReturn(true); + $client->sendRequest($request)->willReturn($response); + + $this->sendRequest($request)->shouldReturn($response); + } + + function it_send_async_request(RequestMatcher $matcher, HttpAsyncClient $client, RequestInterface $request, Promise $promise) + { + $this->addClient($client, $matcher); + $matcher->matches($request)->willReturn(true); + $client->sendAsyncRequest($request)->willReturn($promise); + + $this->sendAsyncRequest($request)->shouldReturn($promise); + } + + function it_throw_exception_on_send_request(RequestMatcher $matcher, HttpClient $client, RequestInterface $request) + { + $this->addClient($client, $matcher); + $matcher->matches($request)->willReturn(false); + + $this->shouldThrow('Http\Client\Exception\RequestException')->duringSendRequest($request); + } + + function it_throw_exception_on_send_async_request(RequestMatcher $matcher, HttpAsyncClient $client, RequestInterface $request) + { + $this->addClient($client, $matcher); + $matcher->matches($request)->willReturn(false); + + $this->shouldThrow('Http\Client\Exception\RequestException')->duringSendAsyncRequest($request); + } +} diff --git a/src/HttpClientRouter.php b/src/HttpClientRouter.php new file mode 100644 index 0000000..9f72133 --- /dev/null +++ b/src/HttpClientRouter.php @@ -0,0 +1,74 @@ + + */ +final class HttpClientRouter implements HttpClient, HttpAsyncClient +{ + /** + * @var array + */ + private $clients = []; + + /** + * {@inheritdoc} + */ + public function sendRequest(RequestInterface $request) + { + $client = $this->chooseHttpClient($request); + + return $client->sendRequest($request); + } + + /** + * {@inheritdoc} + */ + public function sendAsyncRequest(RequestInterface $request) + { + $client = $this->chooseHttpClient($request); + + return $client->sendAsyncRequest($request); + } + + /** + * Add a client to the router. + * + * @param HttpClient|HttpAsyncClient $client + * @param RequestMatcher $requestMatcher + */ + public function addClient($client, RequestMatcher $requestMatcher) + { + $this->clients[] = [ + 'matcher' => $requestMatcher, + 'client' => new FlexibleHttpClient($client), + ]; + } + + /** + * Choose an HTTP client given a specific request. + * + * @param RequestInterface $request + * + * @return HttpClient|HttpAsyncClient + */ + protected function chooseHttpClient(RequestInterface $request) + { + foreach ($this->clients as $client) { + if ($client['matcher']->matches($request)) { + return $client['client']; + } + } + + throw new RequestException('No client found for the specified request', $request); + } +}