Skip to content

Commit c7598e4

Browse files
Fix SSL Certificate and Connection Errors Leaking as Guzzle Exceptions (#55937)
* Fix SSL Certificate and Connection Errors Leaking as Guzzle Exceptions * Fix SSL Certificate and Connection Errors Leaking as Guzzle Exceptions * formatting * formatting * formatting --------- Co-authored-by: Taylor Otwell <taylor@laravel.com>
1 parent 37af04b commit c7598e4

File tree

2 files changed

+83
-6
lines changed

2 files changed

+83
-6
lines changed

src/Illuminate/Http/Client/PendingRequest.php

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -935,15 +935,20 @@ public function send(string $method, string $url, array $options = [])
935935
}
936936
}
937937
});
938-
} catch (ConnectException $e) {
939-
$exception = new ConnectionException($e->getMessage(), 0, $e);
940-
$request = new Request($e->getRequest());
938+
} catch (TransferException $e) {
939+
if ($e instanceof ConnectException) {
940+
$this->marshalConnectionException($e);
941+
}
941942

942-
$this->factory?->recordRequestResponsePair($request, null);
943+
if ($e instanceof RequestException && ! $e->hasResponse()) {
944+
$this->marshalRequestExceptionWithoutResponse($e);
945+
}
943946

944-
$this->dispatchConnectionFailedEvent($request, $exception);
947+
if ($e instanceof RequestException && $e->hasResponse()) {
948+
$this->marshalRequestExceptionWithResponse($e);
949+
}
945950

946-
throw $exception;
951+
throw $e;
947952
}
948953
}, $this->retryDelay ?? 100, function ($exception) use (&$shouldRetry) {
949954
$result = $shouldRetry ?? ($this->retryWhenCallback ? call_user_func($this->retryWhenCallback, $exception, $this, $this->request?->toPsrRequest()->getMethod()) : true);
@@ -1517,6 +1522,60 @@ protected function dispatchConnectionFailedEvent(Request $request, ConnectionExc
15171522
}
15181523
}
15191524

1525+
/**
1526+
* Handle the given connection exception.
1527+
*
1528+
* @param \GuzzleHttp\Exception\ConnectException $e
1529+
* @return void
1530+
*/
1531+
protected function marshalConnectionException(ConnectException $e)
1532+
{
1533+
$exception = new ConnectionException($e->getMessage(), 0, $e);
1534+
1535+
$this->factory?->recordRequestResponsePair(
1536+
$request = new Request($e->getRequest()), null
1537+
);
1538+
1539+
$this->dispatchConnectionFailedEvent($request, $exception);
1540+
1541+
throw $exception;
1542+
}
1543+
1544+
/**
1545+
* Handle the given request exception.
1546+
*
1547+
* @param \GuzzleHttp\Exception\RequestException $e
1548+
* @return void
1549+
*/
1550+
protected function marshalRequestExceptionWithoutResponse(RequestException $e)
1551+
{
1552+
$exception = new ConnectionException($e->getMessage(), 0, $e);
1553+
1554+
$this->factory?->recordRequestResponsePair(
1555+
$request = new Request($e->getRequest()), null
1556+
);
1557+
1558+
$this->dispatchConnectionFailedEvent($request, $exception);
1559+
1560+
throw $exception;
1561+
}
1562+
1563+
/**
1564+
* Handle the given request exception.
1565+
*
1566+
* @param \GuzzleHttp\Exception\RequestException $e
1567+
* @return void
1568+
*/
1569+
protected function marshalRequestExceptionWithResponse(RequestException $e)
1570+
{
1571+
$this->factory?->recordRequestResponsePair(
1572+
new Request($e->getRequest()),
1573+
$response = $this->populateResponse($this->newResponse($e->getResponse()))
1574+
);
1575+
1576+
throw $response->toException();
1577+
}
1578+
15201579
/**
15211580
* Set the client instance.
15221581
*

tests/Http/HttpClientTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
namespace Illuminate\Tests\Http;
44

55
use Exception;
6+
use GuzzleHttp\Exception\RequestException as GuzzleRequestException;
67
use GuzzleHttp\Middleware;
78
use GuzzleHttp\Promise\PromiseInterface;
89
use GuzzleHttp\Promise\RejectedPromise;
10+
use GuzzleHttp\Psr7\Request as GuzzleRequest;
911
use GuzzleHttp\Psr7\Response as Psr7Response;
1012
use GuzzleHttp\Psr7\Utils;
1113
use GuzzleHttp\TransferStats;
@@ -2516,6 +2518,22 @@ public function testMiddlewareRunsAndCanChangeRequestOnAssertSent()
25162518
});
25172519
}
25182520

2521+
public function testSslCertificateErrorsConvertedToConnectionException()
2522+
{
2523+
$this->factory->fake(function () {
2524+
$request = new GuzzleRequest('HEAD', 'https://ssl-error.laravel.example');
2525+
throw new GuzzleRequestException(
2526+
'cURL error 60: SSL certificate problem: unable to get local issuer certificate',
2527+
$request
2528+
);
2529+
});
2530+
2531+
$this->expectException(ConnectionException::class);
2532+
$this->expectExceptionMessage('cURL error 60: SSL certificate problem: unable to get local issuer certificate');
2533+
2534+
$this->factory->head('https://ssl-error.laravel.example');
2535+
}
2536+
25192537
public function testRequestExceptionIsNotThrownIfThePendingRequestIsSetToThrowOnFailureButTheResponseIsSuccessful()
25202538
{
25212539
$this->factory->fake([

0 commit comments

Comments
 (0)