Revert "Improve MultiHttpClient connection concurrency and reuse"
[lhc/web/wiklou.git] / includes / libs / http / MultiHttpClient.php
index 2e418b9..b195a08 100644 (file)
@@ -150,7 +150,7 @@ class MultiHttpClient implements LoggerAwareInterface {
         * 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[] $reqs Map of HTTP request arrays
         * @param array $opts
         *   - connTimeout     : connection timeout per request (seconds)
         *   - reqTimeout      : post-connection timeout per request (seconds)
@@ -161,6 +161,8 @@ class MultiHttpClient implements LoggerAwareInterface {
         */
        public function runMulti( array $reqs, array $opts = [] ) {
                $this->normalizeRequests( $reqs );
+               $opts += [ 'connTimeout' => $this->connTimeout, 'reqTimeout' => $this->reqTimeout ];
+
                if ( $this->isCurlEnabled() ) {
                        return $this->runMultiCurl( $reqs, $opts );
                } else {
@@ -182,16 +184,20 @@ class MultiHttpClient implements LoggerAwareInterface {
         *
         * @see MultiHttpClient::runMulti()
         *
-        * @param array $reqs Map of HTTP request arrays
+        * @param array[] $reqs Map of HTTP request arrays
         * @param array $opts
         *   - 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)
+        * @codingStandardsIgnoreStart
+        * @phan-param array{connTimeout?:int,reqTimeout?:int,usePipelining?:bool,maxConnsPerHost?:int} $opts
+        * @codingStandardsIgnoreEnd
         * @return array $reqs With response array populated for each
         * @throws Exception
+        * @suppress PhanTypeInvalidDimOffset
         */
-       private function runMultiCurl( array $reqs, array $opts = [] ) {
+       private function runMultiCurl( array $reqs, array $opts ) {
                $chm = $this->getCurlMulti();
 
                $selectTimeout = $this->getSelectTimeout( $opts );
@@ -288,20 +294,21 @@ class MultiHttpClient implements LoggerAwareInterface {
 
        /**
         * @param array &$req HTTP request map
+        * @codingStandardsIgnoreStart
+        * @phan-param array{url:string,proxy?:?string,query:mixed,method:string,body:string|resource,headers:string[],stream?:resource,flags:array} $req
+        * @codingStandardsIgnoreEnd
         * @param array $opts
-        *   - connTimeout    : default connection timeout
-        *   - reqTimeout     : default request timeout
+        *   - connTimeout : default connection timeout
+        *   - reqTimeout : default request timeout
         * @return resource
         * @throws Exception
         */
-       protected function getCurlHandle( array &$req, array $opts = [] ) {
+       protected function getCurlHandle( array &$req, array $opts ) {
                $ch = curl_init();
 
-               curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT_MS,
-                       ( $opts['connTimeout'] ?? $this->connTimeout ) * 1000 );
                curl_setopt( $ch, CURLOPT_PROXY, $req['proxy'] ?? $this->proxy );
-               curl_setopt( $ch, CURLOPT_TIMEOUT_MS,
-                       ( $opts['reqTimeout'] ?? $this->reqTimeout ) * 1000 );
+               curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT_MS, intval( $opts['connTimeout'] * 1e3 ) );
+               curl_setopt( $ch, CURLOPT_TIMEOUT_MS, intval( $opts['reqTimeout'] * 1e3 ) );
                curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1 );
                curl_setopt( $ch, CURLOPT_MAXREDIRS, 4 );
                curl_setopt( $ch, CURLOPT_HEADER, 0 );
@@ -317,11 +324,8 @@ class MultiHttpClient implements LoggerAwareInterface {
                        $url .= strpos( $req['url'], '?' ) === false ? "?$query" : "&$query";
                }
                curl_setopt( $ch, CURLOPT_URL, $url );
-
                curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, $req['method'] );
-               if ( $req['method'] === 'HEAD' ) {
-                       curl_setopt( $ch, CURLOPT_NOBODY, 1 );
-               }
+               curl_setopt( $ch, CURLOPT_NOBODY, ( $req['method'] === 'HEAD' ) );
 
                if ( $req['method'] === 'PUT' ) {
                        curl_setopt( $ch, CURLOPT_PUT, 1 );
@@ -348,17 +352,11 @@ class MultiHttpClient implements LoggerAwareInterface {
                        }
                        curl_setopt( $ch, CURLOPT_READFUNCTION,
                                function ( $ch, $fd, $length ) {
-                                       $data = fread( $fd, $length );
-                                       $len = strlen( $data );
-                                       return $data;
+                                       return (string)fread( $fd, $length );
                                }
                        );
                } 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).
-                       curl_setopt( $ch, CURLOPT_SAFE_UPLOAD, true );
                        curl_setopt( $ch, CURLOPT_POSTFIELDS, $req['body'] );
                } else {
                        if ( is_resource( $req['body'] ) || $req['body'] !== '' ) {
@@ -407,23 +405,20 @@ class MultiHttpClient implements LoggerAwareInterface {
                        }
                );
 
-               if ( isset( $req['stream'] ) ) {
-                       // Don't just use CURLOPT_FILE as that might give:
-                       // curl_setopt(): cannot represent a stream of type Output as a STDIO FILE*
-                       // The callback here handles both normal files and php://temp handles.
-                       curl_setopt( $ch, CURLOPT_WRITEFUNCTION,
-                               function ( $ch, $data ) use ( &$req ) {
+               // This works with both file and php://temp handles (unlike CURLOPT_FILE)
+               $hasOutputStream = isset( $req['stream'] );
+               curl_setopt( $ch, CURLOPT_WRITEFUNCTION,
+                       function ( $ch, $data ) use ( &$req, $hasOutputStream ) {
+                               if ( $hasOutputStream ) {
                                        return fwrite( $req['stream'], $data );
-                               }
-                       );
-               } else {
-                       curl_setopt( $ch, CURLOPT_WRITEFUNCTION,
-                               function ( $ch, $data ) use ( &$req ) {
+                               } else {
+                                       // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
                                        $req['response']['body'] .= $data;
+
                                        return strlen( $data );
                                }
-                       );
-               }
+                       }
+               );
 
                return $ch;
        }
@@ -498,7 +493,7 @@ class MultiHttpClient implements LoggerAwareInterface {
                                'error' => '',
                        ];
 
-                       if ( !$sv->isOk() ) {
+                       if ( !$sv->isOK() ) {
                                $svErrors = $sv->getErrors();
                                if ( isset( $svErrors[0] ) ) {
                                        $req['response']['error'] = $svErrors[0]['message'];
@@ -507,6 +502,7 @@ class MultiHttpClient implements LoggerAwareInterface {
                                        if ( isset( $svErrors[0]['params'][0] ) ) {
                                                if ( is_numeric( $svErrors[0]['params'][0] ) ) {
                                                        if ( isset( $svErrors[0]['params'][1] ) ) {
+                                                               // @phan-suppress-next-line PhanTypeInvalidDimOffset
                                                                $req['response']['reason'] = $svErrors[0]['params'][1];
                                                        }
                                                } else {
@@ -529,7 +525,7 @@ class MultiHttpClient implements LoggerAwareInterface {
        /**
         * Normalize request information
         *
-        * @param array $reqs the requests to normalize
+        * @param array[] $reqs the requests to normalize
         */
        private function normalizeRequests( array &$reqs ) {
                foreach ( $reqs as &$req ) {