diff --git a/spec/PluginClientSpec.php b/spec/PluginClientSpec.php index f02f41c..7c727a0 100644 --- a/spec/PluginClientSpec.php +++ b/spec/PluginClientSpec.php @@ -4,11 +4,20 @@ use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; +use Http\Client\Plugin\Plugin; use Http\Promise\Promise; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use PhpSpec\ObjectBehavior; +class DefectuousPlugin implements Plugin +{ + public function handleRequest(RequestInterface $request, callable $next, callable $first) + { + return $first($request); + } +} + class PluginClientSpec extends ObjectBehavior { function let(HttpClient $client) @@ -45,4 +54,11 @@ function it_sends_async_request_with_underlying_client(HttpAsyncClient $asyncCli $this->beConstructedWith($asyncClient); $this->sendAsyncRequest($request)->shouldReturn($promise); } + + function it_throws_loop_exception(HttpClient $client, RequestInterface $request) + { + $this->beConstructedWith($client, [new DefectuousPlugin()]); + + $this->shouldThrow('Http\Client\Plugin\Exception\LoopException')->duringSendRequest($request); + } } diff --git a/src/Exception/LoopException.php b/src/Exception/LoopException.php new file mode 100644 index 0000000..ae6bdd6 --- /dev/null +++ b/src/Exception/LoopException.php @@ -0,0 +1,9 @@ +client = $client; @@ -44,6 +54,7 @@ public function __construct($client, array $plugins = []) } $this->plugins = $plugins; + $this->options = $this->configure($options); } /** @@ -66,6 +77,23 @@ public function sendAsyncRequest(RequestInterface $request) return $pluginChain($request); } + /** + * Configure the plugin client. + * + * @param array $options + * + * @return array + */ + protected function configure(array $options = []) + { + $resolver = new OptionsResolver(); + $resolver->setDefaults([ + 'max_restarts' => 10, + ]); + + return $resolver->resolve($options); + } + /** * @param Plugin[] $pluginList * @@ -74,6 +102,8 @@ public function sendAsyncRequest(RequestInterface $request) private function createPluginChain($pluginList) { $client = $this->client; + $options = $this->options; + $lastCallable = function (RequestInterface $request) use ($client) { return $client->sendAsyncRequest($request); }; @@ -87,6 +117,17 @@ private function createPluginChain($pluginList) $firstCallable = $lastCallable; } + $firstCalls = 0; + $firstCallable = function (RequestInterface $request) use ($options, $lastCallable, &$firstCalls) { + if ($firstCalls > $options['max_restarts']) { + throw new LoopException('Too many restarts in plugin client', $request); + } + + ++$firstCalls; + + return $lastCallable($request); + }; + return $firstCallable; } }