summaryrefslogtreecommitdiff
path: root/includes/libs/MultiHttpClient.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/libs/MultiHttpClient.php')
-rw-r--r--includes/libs/MultiHttpClient.php68
1 files changed, 49 insertions, 19 deletions
diff --git a/includes/libs/MultiHttpClient.php b/includes/libs/MultiHttpClient.php
index fb2daa69..5555cbcb 100644
--- a/includes/libs/MultiHttpClient.php
+++ b/includes/libs/MultiHttpClient.php
@@ -58,8 +58,8 @@ class MultiHttpClient {
/**
* @param array $options
- * - connTimeout : default connection timeout
- * - reqTimeout : default request timeout
+ * - connTimeout : default connection timeout (seconds)
+ * - reqTimeout : default request timeout (seconds)
* - proxy : HTTP proxy to use
* - usePipelining : whether to use HTTP pipelining if possible (for all hosts)
* - maxConnsPerHost : maximum number of concurrent connections (per host)
@@ -72,7 +72,9 @@ class MultiHttpClient {
throw new Exception( "Cannot find CA bundle: " . $this->caBundlePath );
}
}
- static $opts = array( 'connTimeout', 'reqTimeout', 'usePipelining', 'maxConnsPerHost', 'proxy' );
+ static $opts = array(
+ 'connTimeout', 'reqTimeout', 'usePipelining', 'maxConnsPerHost', 'proxy'
+ );
foreach ( $opts as $key ) {
if ( isset( $options[$key] ) ) {
$this->$key = $options[$key];
@@ -84,19 +86,19 @@ class MultiHttpClient {
* Execute an HTTP(S) request
*
* This method returns a response map of:
- * - code : HTTP response code or 0 if there was a serious cURL error
- * - reason : HTTP response reason (empty if there was a serious cURL error)
- * - headers : <header name/value associative array>
- * - body : HTTP response body or resource (if "stream" was set)
+ * - code : HTTP response code or 0 if there was a serious cURL error
+ * - reason : HTTP response reason (empty if there was a serious cURL error)
+ * - headers : <header name/value associative array>
+ * - body : HTTP response body or resource (if "stream" was set)
* - error : Any cURL error string
- * The map also stores integer-indexed copies of these values. This lets callers do:
- * <code>
+ * The map also stores integer-indexed copies of these values. This lets callers do:
+ * @code
* list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $http->run( $req );
- * </code>
+ * @endcode
* @param array $req HTTP request array
* @param array $opts
- * - connTimeout : connection timeout per request
- * - reqTimeout : post-connection timeout per request
+ * - connTimeout : connection timeout per request (seconds)
+ * - reqTimeout : post-connection timeout per request (seconds)
* @return array Response array for request
*/
final public function run( array $req, array $opts = array() ) {
@@ -114,17 +116,17 @@ class MultiHttpClient {
* - body : HTTP response body or resource (if "stream" was set)
* - error : Any cURL error string
* The map also stores integer-indexed copies of these values. This lets callers do:
- * <code>
+ * @code
* list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $req['response'];
- * </code>
+ * @endcode
* All headers in the 'headers' field are normalized to use lower case names.
* This is true for the request headers and the response headers. Integer-indexed
* method/URL entries will also be changed to use the corresponding string keys.
*
* @param array $reqs Map of HTTP request arrays
* @param array $opts
- * - connTimeout : connection timeout per request
- * - reqTimeout : post-connection timeout per request
+ * - connTimeout : connection timeout per request (seconds)
+ * - reqTimeout : post-connection timeout per request (seconds)
* - usePipelining : whether to use HTTP pipelining if possible
* - maxConnsPerHost : maximum number of concurrent connections (per host)
* @return array $reqs With response array populated for each
@@ -189,6 +191,7 @@ class MultiHttpClient {
// @TODO: use a per-host rolling handle window (e.g. CURLMOPT_MAX_HOST_CONNECTIONS)
$batches = array_chunk( $indexes, $this->maxConnsPerHost );
+ $infos = array();
foreach ( $batches as $batch ) {
// Attach all cURL handles for this batch
@@ -201,6 +204,10 @@ class MultiHttpClient {
// Do any available work...
do {
$mrc = curl_multi_exec( $chm, $active );
+ $info = curl_multi_info_read( $chm );
+ if ( $info !== false ) {
+ $infos[(int)$info['handle']] = $info;
+ }
} while ( $mrc == CURLM_CALL_MULTI_PERFORM );
// Wait (if possible) for available work...
if ( $active > 0 && $mrc == CURLM_OK ) {
@@ -216,10 +223,20 @@ class MultiHttpClient {
foreach ( $reqs as $index => &$req ) {
$ch = $handles[$index];
curl_multi_remove_handle( $chm, $ch );
- if ( curl_errno( $ch ) !== 0 ) {
- $req['response']['error'] = "(curl error: " .
- curl_errno( $ch ) . ") " . curl_error( $ch );
+
+ if ( isset( $infos[(int)$ch] ) ) {
+ $info = $infos[(int)$ch];
+ $errno = $info['result'];
+ if ( $errno !== 0 ) {
+ $req['response']['error'] = "(curl error: $errno)";
+ if ( function_exists( 'curl_strerror' ) ) {
+ $req['response']['error'] .= " " . curl_strerror( $errno );
+ }
+ }
+ } else {
+ $req['response']['error'] = "(curl error: no status set)";
}
+
// For convenience with the list() operator
$req['response'][0] = $req['response']['code'];
$req['response'][1] = $req['response']['reason'];
@@ -318,6 +335,19 @@ class MultiHttpClient {
);
} elseif ( $req['method'] === 'POST' ) {
curl_setopt( $ch, CURLOPT_POST, 1 );
+ // Don't interpret POST parameters starting with '@' as file uploads, because this
+ // makes it impossible to POST plain values starting with '@' (and causes security
+ // issues potentially exposing the contents of local files).
+ // The PHP manual says this option was introduced in PHP 5.5 defaults to true in PHP 5.6,
+ // but we support lower versions, and the option doesn't exist in HHVM 5.6.99.
+ if ( defined( 'CURLOPT_SAFE_UPLOAD' ) ) {
+ curl_setopt( $ch, CURLOPT_SAFE_UPLOAD, true );
+ } else if ( is_array( $req['body'] ) ) {
+ // In PHP 5.2 and later, '@' is interpreted as a file upload if POSTFIELDS
+ // is an array, but not if it's a string. So convert $req['body'] to a string
+ // for safety.
+ $req['body'] = wfArrayToCgi( $req['body'] );
+ }
curl_setopt( $ch, CURLOPT_POSTFIELDS, $req['body'] );
} else {
if ( is_resource( $req['body'] ) || $req['body'] !== '' ) {