diff --git a/src/Illuminate/Http/Client/RequestException.php b/src/Illuminate/Http/Client/RequestException.php index 6f32fc9f64a0..d871e091adbe 100644 --- a/src/Illuminate/Http/Client/RequestException.php +++ b/src/Illuminate/Http/Client/RequestException.php @@ -16,7 +16,7 @@ class RequestException extends HttpClientException /** * The current truncation length for the exception message. * - * @var int|false + * @var int|false|null */ public $truncateExceptionsAt; @@ -27,6 +27,13 @@ class RequestException extends HttpClientException */ public static $truncateAt = 120; + /** + * Whether the response has been summarized in the message. + * + * @var bool + */ + public $hasBeenSummarized = false; + /** * Create a new exception instance. * @@ -35,7 +42,7 @@ class RequestException extends HttpClientException */ public function __construct(Response $response, $truncateExceptionsAt = null) { - parent::__construct("HTTP request returned status code {$response->status()}", $response->status()); + parent::__construct($this->prepareMessage($response), $response->status()); $this->truncateExceptionsAt = $truncateExceptionsAt; @@ -76,18 +83,35 @@ public static function dontTruncate() /** * Prepare the exception message. * - * @return void + * @param \Illuminate\Http\Client\Response $response + * @return string */ - public function report(): void + protected function prepareMessage(Response $response) { + $message = "HTTP request returned status code {$response->status()}"; + $truncateExceptionsAt = $this->truncateExceptionsAt ?? static::$truncateAt; - $summary = $truncateExceptionsAt - ? Message::bodySummary($this->response->toPsrResponse(), $truncateExceptionsAt) - : Message::toString($this->response->toPsrResponse()); + $summary = is_int($truncateExceptionsAt) + ? Message::bodySummary($response->toPsrResponse(), $truncateExceptionsAt) + : Message::toString($response->toPsrResponse()); + + return is_null($summary) ? $message : $message . ":\n{$summary}\n"; + } - if (! is_null($summary)) { - $this->message .= ":\n{$summary}\n"; + /** + * Prepare the exception message. + * + * @return void + */ + public function report(): void + { + if ($this->hasBeenSummarized) { + return; } + + $this->message = $this->prepareMessage($this->response); + + $this->hasBeenSummarized = true; } } diff --git a/tests/Http/HttpClientTest.php b/tests/Http/HttpClientTest.php index 39c059dc9bd1..e23f0ee9fca4 100644 --- a/tests/Http/HttpClientTest.php +++ b/tests/Http/HttpClientTest.php @@ -1452,6 +1452,26 @@ public function testRequestExceptionEmptyBody() throw new RequestException(new Response($response)); } + public function testReportingExceptionTwiceDoesNotIncludeSummaryTwice() + { + RequestException::dontTruncate(); + + $error = [ + 'error' => [ + 'code' => 403, + 'message' => 'The Request can not be completed', + ], + ]; + + $response = new Psr7Response(403, [], json_encode($error)); + + $exception = new RequestException(new Response($response)); + $exception->report(); + $exception->report(); + + $this->assertEquals(1, substr_count($exception->getMessage(), '{"error":{"code":403,"message":"The Request can not be completed"}}')); + } + public function testOnErrorDoesntCallClosureOnInformational() { $status = 0;