*/
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 {
* @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 );
/**
* @param array &$req HTTP request map
* @param array $opts
- * - connTimeout : default connection timeout
- * - reqTimeout : default request timeout
+ * - connTimeout : default connection timeout
+ * - reqTimeout : default request timeout
* @return resource
* @throws Exception
* @suppress PhanTypeMismatchArgumentInternal
*/
- 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 );
$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 );
}
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'] !== '' ) {
$name = strtolower( $name );
$value = trim( $value );
if ( isset( $req['response']['headers'][$name] ) ) {
- // @phan-suppress-next-line PhanTypeInvalidDimOffset
$req['response']['headers'][$name] .= ', ' . $value;
} else {
$req['response']['headers'][$name] = $value;
}
);
- 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 {
$req['response']['body'] .= $data;
+
return strlen( $data );
}
- );
- }
+ }
+ );
return $ch;
}