diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md
index acc43e6..a4b048c 100644
--- a/TROUBLESHOOTING.md
+++ b/TROUBLESHOOTING.md
@@ -3,6 +3,7 @@ If you can't find a solution below, please open an [issue](https://github.com/se
## Table of Contents
* [Viewing the Request Body](#request-body)
+* [Handling SSL Errors](#ssl-errors)
## Viewing the Request Body
@@ -14,3 +15,10 @@ echo $response->statusCode();
echo $response->body();
echo $response->headers();
```
+
+
+## Handling SSL Errors
+
+If any SSL errors occur during API calls, an `InvalidRequest` will be thrown. This will provide information to help debug the issue further.
+
+If the issue is caused by an unrecognized certificate, it may be possible that PHP is unable to locate your system's CA bundle. An easy fix would be requiring the `composer/ca-bundle` package - this library will automatically detect and use that to locate the CA bundle, or use Mozilla's as a fallback.
diff --git a/composer.json b/composer.json
index bd0c9d8..0c637fa 100644
--- a/composer.json
+++ b/composer.json
@@ -27,6 +27,9 @@
"squizlabs/php_codesniffer": "~2.0",
"friendsofphp/php-cs-fixer": "^2.16"
},
+ "suggest": {
+ "composer/ca-bundle": "Including this library will ensure that a valid CA bundle is available for secure connections"
+ },
"autoload": {
"psr-4": {
"SendGrid\\": "lib/"
diff --git a/lib/Client.php b/lib/Client.php
index 77b092c..1d2c55c 100644
--- a/lib/Client.php
+++ b/lib/Client.php
@@ -369,6 +369,15 @@ private function createCurlOptions($method, $body = null, $headers = null)
}
$options[CURLOPT_HTTPHEADER] = $headers;
+ if (class_exists('\\Composer\\CaBundle\\CaBundle') && method_exists('\\Composer\\CaBundle\\CaBundle', 'getSystemCaRootBundlePath')) {
+ $caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath();
+ if (is_dir($caPathOrFile) || (is_link($caPathOrFile) && is_dir(readlink($caPathOrFile)))) {
+ $options[CURLOPT_CAPATH] = $caPathOrFile;
+ } else {
+ $options[CURLOPT_CAINFO] = $caPathOrFile;
+ }
+ }
+
return $options;
}
@@ -492,6 +501,8 @@ public function makeRequest($method, $url, $body = null, $headers = null, $retry
* @param array $requests
*
* @return Response[]
+ *
+ * @throws InvalidRequest
*/
public function makeAllRequests(array $requests = [])
{
@@ -512,6 +523,11 @@ public function makeAllRequests(array $requests = [])
$sleepDurations = 0;
foreach ($channels as $id => $channel) {
$content = curl_multi_getcontent($channel);
+
+ if ($content === false) {
+ throw new InvalidRequest(curl_error($channel), curl_errno($channel));
+ }
+
$response = $this->parseResponse($channel, $content);
if ($requests[$id]['retryOnLimit'] && $response->statusCode() === self::TOO_MANY_REQUESTS_HTTP_CODE) {
diff --git a/test/unit/ClientTest.php b/test/unit/ClientTest.php
index 0e19748..0d829a4 100644
--- a/test/unit/ClientTest.php
+++ b/test/unit/ClientTest.php
@@ -33,7 +33,7 @@ public function testConstructor()
$this->assertAttributeEquals([], 'path', $this->client);
$this->assertAttributeEquals([], 'curlOptions', $this->client);
$this->assertAttributeEquals(false, 'retryOnLimit', $this->client);
- $this->assertAttributeEquals(['get', 'post', 'patch', 'put', 'delete'], 'methods', $this->client);
+ $this->assertAttributeEquals(['get', 'post', 'patch', 'put', 'delete'], 'methods', $this->client);
}
public function test_()
@@ -209,6 +209,15 @@ public function testThrowExceptionOnInvalidCall()
$client->get();
}
+ public function testMakeRequestWithUntrustedRootCert()
+ {
+ $this->expectException(InvalidRequest::class);
+ $this->expectExceptionMessageRegExp('/certificate/i');
+
+ $client = new Client('https://untrusted-root.badssl.com/');
+ $client->makeRequest('GET', 'https://untrusted-root.badssl.com/');
+ }
+
/**
* @param object $obj
* @param string $name