Skip to content

[12.x] Allow setting the RequestException truncation limit per request #55897

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion src/Illuminate/Http/Client/PendingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,13 @@ class PendingRequest
'query',
];

/**
* The length at which request exceptions will be truncated.
*
* @var int<1, max>|false|null
*/
protected $truncateExceptionsAt = null;

/**
* Create a new HTTP Client instance.
*
Expand Down Expand Up @@ -1429,7 +1436,15 @@ public function mergeOptions(...$options)
*/
protected function newResponse($response)
{
return new Response($response);
return tap(new Response($response), function (Response $laravelResponse) {
if ($this->truncateExceptionsAt === null) {
return;
}

$this->truncateExceptionsAt === false
? $laravelResponse->dontTruncateExceptions()
: $laravelResponse->truncateExceptionsAt($this->truncateExceptionsAt);
});
}

/**
Expand Down Expand Up @@ -1522,6 +1537,31 @@ protected function dispatchConnectionFailedEvent(Request $request, ConnectionExc
}
}

/**
* Indicate that request exceptions should be truncated to the given length.
*
* @param int<1, max> $length
* @return $this
*/
public function truncateExceptionsAt(int $length)
{
$this->truncateExceptionsAt = $length;

return $this;
}

/**
* Indicate that request exceptions should not be truncated.
*
* @return $this
*/
public function dontTruncateExceptions()
{
$this->truncateExceptionsAt = false;

return $this;
}

/**
* Handle the given connection exception.
*
Expand Down
46 changes: 45 additions & 1 deletion src/Illuminate/Http/Client/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ class Response implements ArrayAccess, Stringable
*/
public $transferStats;

/**
* The length at which request exceptions will be truncated.
*
* @var int<1, max>|false|null
*/
protected $truncateExceptionsAt = null;

/**
* Create a new response instance.
*
Expand Down Expand Up @@ -297,7 +304,19 @@ public function toPsrResponse()
public function toException()
{
if ($this->failed()) {
return new RequestException($this);
$originalTruncateAt = RequestException::$truncateAt;

try {
if ($this->truncateExceptionsAt !== null) {
$this->truncateExceptionsAt === false
? RequestException::dontTruncate()
: RequestException::truncateAt($this->truncateExceptionsAt);
}

return new RequestException($this);
} finally {
RequestException::$truncateAt = $originalTruncateAt;
}
}
}

Expand Down Expand Up @@ -395,6 +414,31 @@ public function throwIfServerError()
return $this->serverError() ? $this->throw() : $this;
}

/**
* Indicate that request exceptions should be truncated to the given length.
*
* @param int<1, max> $length
* @return $this
*/
public function truncateExceptionsAt(int $length)
{
$this->truncateExceptionsAt = $length;

return $this;
}

/**
* Indicate that request exceptions should not be truncated.
*
* @return $this
*/
public function dontTruncateExceptions()
{
$this->truncateExceptionsAt = false;

return $this;
}

/**
* Dump the content from the response.
*
Expand Down
75 changes: 75 additions & 0 deletions tests/Http/HttpClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,81 @@ public function testRequestExceptionWithCustomTruncatedSummary()
throw new RequestException(new Response($response));
}

public function testRequestLevelTruncationLevelOnRequestException()
{
RequestException::truncateAt(60);

$this->factory->fake([
'*' => $this->factory->response(['error'], 403),
]);

$exception = null;
try {
$this->factory->throw()->truncateExceptionsAt(3)->get('http://foo.com/json');
} catch (RequestException $e) {
$exception = $e;
}

$this->assertEquals("HTTP request returned status code 403:\n[\"e (truncated...)\n", $exception->getMessage());

$this->assertEquals(60, RequestException::$truncateAt);
}

public function testNoTruncationOnRequestLevel()
{
RequestException::truncateAt(60);

$this->factory->fake([
'*' => $this->factory->response(['error'], 403),
]);

$exception = null;

try {
$this->factory->throw()->dontTruncateExceptions()->get('http://foo.com/json');
} catch (RequestException $e) {
$exception = $e;
}

$this->assertEquals("HTTP request returned status code 403:\nHTTP/1.1 403 Forbidden\r\nContent-Type: application/json\r\n\r\n[\"error\"]\n", $exception->getMessage());

$this->assertEquals(60, RequestException::$truncateAt);
}

public function testRequestExceptionDoesNotTruncateButRequestDoes()
{
RequestException::dontTruncate();

$this->factory->fake([
'*' => $this->factory->response(['error'], 403),
]);

$exception = null;
try {
$this->factory->throw()->truncateExceptionsAt(3)->get('http://foo.com/json');
} catch (RequestException $e) {
$exception = $e;
}

$this->assertEquals("HTTP request returned status code 403:\n[\"e (truncated...)\n", $exception->getMessage());

$this->assertFalse(RequestException::$truncateAt);
}

public function testAsyncRequestExceptionsRespectRequestTruncation()
{
RequestException::dontTruncate();
$this->factory->fake([
'*' => $this->factory->response(['error'], 403),
]);

$exception = $this->factory->async()->throw()->truncateExceptionsAt(4)->get('http://foo.com/json')->wait();

$this->assertInstanceOf(RequestException::class, $exception);
$this->assertEquals("HTTP request returned status code 403:\n[\"er (truncated...)\n", $exception->getMessage());
$this->assertFalse(RequestException::$truncateAt);
}

public function testRequestExceptionEmptyBody()
{
$this->expectException(RequestException::class);
Expand Down