From 4eba938c7f0d3e0ef8509140af100c0f1b7b07f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Debrauwer?= Date: Tue, 12 Nov 2024 19:41:32 +0100 Subject: [PATCH] [11.x] Http Client: fake connection exception (#53485) * Support faking connection exception * Fix linting issues * Fix another linting issue * formatting --------- Co-authored-by: Taylor Otwell --- Client/Factory.php | 25 ++++++++++++++++++++++--- Client/ResponseSequence.php | 23 ++++++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Client/Factory.php b/Client/Factory.php index 796fc439..0781429c 100644 --- a/Client/Factory.php +++ b/Client/Factory.php @@ -3,6 +3,7 @@ namespace Illuminate\Http\Client; use Closure; +use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Middleware; use GuzzleHttp\Promise\Create; use GuzzleHttp\Promise\PromiseInterface; @@ -164,6 +165,22 @@ public static function response($body = null, $status = 200, $headers = []) return Create::promiseFor($response); } + /** + * Create a new connection exception for use during stubbing. + * + * @param string|null $message + * @return \GuzzleHttp\Promise\PromiseInterface + */ + public static function failedConnection($message = null) + { + return function ($request) use ($message) { + return Create::rejectionFor(new ConnectException( + $message ?? "cURL error 6: Could not resolve host: {$request->toPsrRequest()->getUri()->getHost()} (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for {$request->toPsrRequest()->getUri()}.", + $request->toPsrRequest(), + )); + }; + } + /** * Get an invokable object that returns a sequence of responses in order for use during stubbing. * @@ -203,9 +220,11 @@ public function fake($callback = null) $this->stubCallbacks = $this->stubCallbacks->merge(collect([ function ($request, $options) use ($callback) { - $response = $callback instanceof Closure - ? $callback($request, $options) - : $callback; + $response = $callback; + + while ($response instanceof Closure) { + $response = $response($request, $options); + } if ($response instanceof PromiseInterface) { $options['on_stats'](new TransferStats( diff --git a/Client/ResponseSequence.php b/Client/ResponseSequence.php index 5925c03b..e35736b0 100644 --- a/Client/ResponseSequence.php +++ b/Client/ResponseSequence.php @@ -2,6 +2,7 @@ namespace Illuminate\Http\Client; +use Closure; use Illuminate\Support\Traits\Macroable; use OutOfBoundsException; @@ -71,7 +72,7 @@ public function pushStatus(int $status, array $headers = []) } /** - * Push response with the contents of a file as the body to the sequence. + * Push a response with the contents of a file as the body to the sequence. * * @param string $filePath * @param int $status @@ -87,6 +88,19 @@ public function pushFile(string $filePath, int $status = 200, array $headers = [ ); } + /** + * Push a connection exception to the sequence. + * + * @param string|null $message + * @return $this + */ + public function pushFailedConnection($message = null) + { + return $this->pushResponse( + Factory::failedConnection($message) + ); + } + /** * Push a response to the sequence. * @@ -137,11 +151,12 @@ public function isEmpty() /** * Get the next response in the sequence. * + * @param \Illuminate\Http\Client\Request $request * @return mixed * * @throws \OutOfBoundsException */ - public function __invoke() + public function __invoke($request) { if ($this->failWhenEmpty && $this->isEmpty()) { throw new OutOfBoundsException('A request was made, but the response sequence is empty.'); @@ -151,6 +166,8 @@ public function __invoke() return value($this->emptyResponse ?? Factory::response()); } - return array_shift($this->responses); + $response = array_shift($this->responses); + + return $response instanceof Closure ? $response($request) : $response; } }