diff --git a/lib/HttpClient/CurlClient.php b/lib/HttpClient/CurlClient.php index 7673e397d..26568b247 100644 --- a/lib/HttpClient/CurlClient.php +++ b/lib/HttpClient/CurlClient.php @@ -46,6 +46,8 @@ public static function instance() protected $curlHandle = null; + protected $requestStatusCallback = null; + /** * CurlClient constructor. * @@ -124,6 +126,33 @@ public function setEnableHttp2($enable) $this->enableHttp2 = $enable; } + /** + * @return callable|null + */ + public function getRequestStatusCallback() + { + return $this->requestStatusCallback; + } + + /** + * Sets a callback that is called after each request. The callback will + * receive the following parameters: + * + * 1. string $rbody The response body + * 2. integer $rcode The response status code + * 3. \Stripe\Util\CaseInsensitiveArray $rheaders The response headers + * 4. integer $errno The curl error number + * 5. string|null $message The curl error message + * 6. boolean $shouldRetry Whether the request will be retried + * 7. integer $numRetries The number of the retry attempt + * + * @param callable|null $requestStatusCallback + */ + public function setRequestStatusCallback($requestStatusCallback) + { + $this->requestStatusCallback = $requestStatusCallback; + } + // USER DEFINED TIMEOUTS const DEFAULT_TIMEOUT = 80; @@ -250,6 +279,7 @@ private function executeRequestWithRetries($opts, $absUrl) while (true) { $rcode = 0; $errno = 0; + $message = null; // Create a callback to capture HTTP headers for the response $rheaders = new Util\CaseInsensitiveArray(); @@ -278,7 +308,16 @@ private function executeRequestWithRetries($opts, $absUrl) $this->closeCurlHandle(); } - if ($this->shouldRetry($errno, $rcode, $rheaders, $numRetries)) { + $shouldRetry = $this->shouldRetry($errno, $rcode, $rheaders, $numRetries); + + if (is_callable($this->getRequestStatusCallback())) { + call_user_func_array( + $this->getRequestStatusCallback(), + [$rbody, $rcode, $rheaders, $errno, $message, $shouldRetry, $numRetries] + ); + } + + if ($shouldRetry) { $numRetries += 1; $sleepSeconds = $this->sleepTime($numRetries, $rheaders); usleep(intval($sleepSeconds * 1000000)); diff --git a/tests/Stripe/HttpClient/CurlClientTest.php b/tests/Stripe/HttpClient/CurlClientTest.php index 3c54f9d89..36f5c2525 100644 --- a/tests/Stripe/HttpClient/CurlClientTest.php +++ b/tests/Stripe/HttpClient/CurlClientTest.php @@ -297,4 +297,32 @@ public function testResponseHeadersCaseInsensitive() $this->assertNotNull($headers['request-id']); $this->assertEquals($headers['request-id'], $headers['Request-Id']); } + + public function testSetRequestStatusCallback() + { + try { + $called = false; + + $curl = new CurlClient(); + $curl->setRequestStatusCallback(function ($rbody, $rcode, $rheaders, $errno, $message, $willBeRetried, $numRetries) use (&$called) { + $called = true; + + $this->assertTrue(is_string($rbody)); + $this->assertEquals(200, $rcode); + $this->assertEquals('req_123', $rheaders['request-id']); + $this->assertEquals(0, $errno); + $this->assertNull($message); + $this->assertFalse($willBeRetried); + $this->assertEquals(0, $numRetries); + }); + + \Stripe\ApiRequestor::setHttpClient($curl); + + Charge::all(); + + $this->assertTrue($called); + } finally { + \Stripe\ApiRequestor::setHttpClient(null); + } + } }