|
|
@@ -18,11 +18,78 @@ class CurlClient implements ClientInterface
|
|
|
return self::$instance;
|
|
|
}
|
|
|
|
|
|
+ protected $defaultOptions;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * CurlClient constructor.
|
|
|
+ *
|
|
|
+ * Pass in a callable to $defaultOptions that returns an array of CURLOPT_* values to start
|
|
|
+ * off a request with, or an flat array with the same format used by curl_setopt_array() to
|
|
|
+ * provide a static set of options. Note that many options are overridden later in the request
|
|
|
+ * call, including timeouts, which can be set via setTimeout() and setConnectTimeout().
|
|
|
+ *
|
|
|
+ * Note that request() will silently ignore a non-callable, non-array $defaultOptions, and will
|
|
|
+ * throw an exception if $defaultOptions returns a non-array value.
|
|
|
+ *
|
|
|
+ * @param array|callable|null $defaultOptions
|
|
|
+ */
|
|
|
+ public function __construct($defaultOptions = null)
|
|
|
+ {
|
|
|
+ $this->defaultOptions = $defaultOptions;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getDefaultOptions()
|
|
|
+ {
|
|
|
+ return $this->defaultOptions;
|
|
|
+ }
|
|
|
+
|
|
|
+ // USER DEFINED TIMEOUTS
|
|
|
+
|
|
|
+ const DEFAULT_TIMEOUT = 80;
|
|
|
+ const DEFAULT_CONNECT_TIMEOUT = 30;
|
|
|
+
|
|
|
+ private $timeout = self::DEFAULT_TIMEOUT;
|
|
|
+ private $connectTimeout = self::DEFAULT_CONNECT_TIMEOUT;
|
|
|
+
|
|
|
+ public function setTimeout($seconds)
|
|
|
+ {
|
|
|
+ $this->timeout = (int) max($seconds, 0);
|
|
|
+ return $this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function setConnectTimeout($seconds)
|
|
|
+ {
|
|
|
+ $this->connectTimeout = (int) max($seconds, 0);
|
|
|
+ return $this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getTimeout()
|
|
|
+ {
|
|
|
+ return $this->timeout;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getConnectTimeout()
|
|
|
+ {
|
|
|
+ return $this->connectTimeout;
|
|
|
+ }
|
|
|
+
|
|
|
+ // END OF USER DEFINED TIMEOUTS
|
|
|
+
|
|
|
public function request($method, $absUrl, $headers, $params, $hasFile)
|
|
|
{
|
|
|
$curl = curl_init();
|
|
|
$method = strtolower($method);
|
|
|
+
|
|
|
$opts = array();
|
|
|
+ if (is_callable($this->defaultOptions)) { // call defaultOptions callback, set options to return value
|
|
|
+ $opts = call_user_func_array($this->defaultOptions, func_get_args());
|
|
|
+ if (!is_array($opts)) {
|
|
|
+ throw new Error\Api("Non-array value returned by defaultOptions CurlClient callback");
|
|
|
+ }
|
|
|
+ } elseif (is_array($this->defaultOptions)) { // set default curlopts from array
|
|
|
+ $opts = $this->defaultOptions;
|
|
|
+ }
|
|
|
+
|
|
|
if ($method == 'get') {
|
|
|
if ($hasFile) {
|
|
|
throw new Error\Api(
|
|
|
@@ -59,18 +126,63 @@ class CurlClient implements ClientInterface
|
|
|
return strlen($header_line);
|
|
|
};
|
|
|
|
|
|
+ // By default for large request body sizes (> 1024 bytes), cURL will
|
|
|
+ // send a request without a body and with a `Expect: 100-continue`
|
|
|
+ // header, which gives the server a chance to respond with an error
|
|
|
+ // status code in cases where one can be determined right away (say
|
|
|
+ // on an authentication problem for example), and saves the "large"
|
|
|
+ // request body from being ever sent.
|
|
|
+ //
|
|
|
+ // Unfortunately, the bindings don't currently correctly handle the
|
|
|
+ // success case (in which the server sends back a 100 CONTINUE), so
|
|
|
+ // we'll error under that condition. To compensate for that problem
|
|
|
+ // for the time being, override cURL's behavior by simply always
|
|
|
+ // sending an empty `Expect:` header.
|
|
|
+ array_push($headers, 'Expect: ');
|
|
|
+
|
|
|
$absUrl = Util\Util::utf8($absUrl);
|
|
|
$opts[CURLOPT_URL] = $absUrl;
|
|
|
$opts[CURLOPT_RETURNTRANSFER] = true;
|
|
|
- $opts[CURLOPT_CONNECTTIMEOUT] = 30;
|
|
|
- $opts[CURLOPT_TIMEOUT] = 80;
|
|
|
- $opts[CURLOPT_RETURNTRANSFER] = true;
|
|
|
+ $opts[CURLOPT_CONNECTTIMEOUT] = $this->connectTimeout;
|
|
|
+ $opts[CURLOPT_TIMEOUT] = $this->timeout;
|
|
|
$opts[CURLOPT_HEADERFUNCTION] = $headerCallback;
|
|
|
$opts[CURLOPT_HTTPHEADER] = $headers;
|
|
|
if (!Stripe::$verifySslCerts) {
|
|
|
$opts[CURLOPT_SSL_VERIFYPEER] = false;
|
|
|
}
|
|
|
|
|
|
+ // @codingStandardsIgnoreStart
|
|
|
+ // PSR2 requires all constants be upper case. Sadly, the CURL_SSLVERSION
|
|
|
+ // constants to not abide by those rules.
|
|
|
+ //
|
|
|
+ // Explicitly set a TLS version for cURL to use now that we're starting
|
|
|
+ // to block 1.0 and 1.1 requests.
|
|
|
+ //
|
|
|
+ // If users are on OpenSSL >= 1.0.1, we know that they support TLS 1.2,
|
|
|
+ // so set that explicitly because on some older Linux distros, clients may
|
|
|
+ // default to TLS 1.0 even when they have TLS 1.2 available.
|
|
|
+ //
|
|
|
+ // For users on much older versions of OpenSSL, set a valid range of
|
|
|
+ // TLS 1.0 to 1.2 (CURL_SSLVERSION_TLSv1). Note that this may result in
|
|
|
+ // their requests being blocked unless they're specially flagged into
|
|
|
+ // being able to use an old TLS version.
|
|
|
+ //
|
|
|
+ // Note: The int on the right is pulled from the source of OpenSSL 1.0.1a.
|
|
|
+ if (OPENSSL_VERSION_NUMBER >= 0x1000100f) {
|
|
|
+ if (!defined('CURL_SSLVERSION_TLSv1_2')) {
|
|
|
+ // Note the value 6 comes from its position in the enum that
|
|
|
+ // defines it in cURL's source code.
|
|
|
+ define('CURL_SSLVERSION_TLSv1_2', 6); // constant not defined in PHP < 5.5
|
|
|
+ }
|
|
|
+ $opts[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1_2;
|
|
|
+ } else {
|
|
|
+ if (!defined('CURL_SSLVERSION_TLSv1')) {
|
|
|
+ define('CURL_SSLVERSION_TLSv1', 1); // constant not defined in PHP < 5.5
|
|
|
+ }
|
|
|
+ $opts[CURLOPT_SSLVERSION] = CURL_SSLVERSION_TLSv1;
|
|
|
+ }
|
|
|
+ // @codingStandardsIgnoreEnd
|
|
|
+
|
|
|
curl_setopt_array($curl, $opts);
|
|
|
$rbody = curl_exec($curl);
|
|
|
|
|
|
@@ -163,10 +275,12 @@ class CurlClient implements ClientInterface
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- if ($prefix && $k && !is_int($k)) {
|
|
|
- $k = $prefix."[".$k."]";
|
|
|
- } elseif ($prefix) {
|
|
|
- $k = $prefix."[]";
|
|
|
+ if ($prefix) {
|
|
|
+ if ($k !== null && (!is_int($k) || is_array($v))) {
|
|
|
+ $k = $prefix."[".$k."]";
|
|
|
+ } else {
|
|
|
+ $k = $prefix."[]";
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (is_array($v)) {
|