* @file
*/
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
/**
* Class to handle concurrent HTTP requests
*
* @author Aaron Schulz
* @since 1.23
*/
-class MultiHttpClient {
+class MultiHttpClient implements LoggerAwareInterface {
/** @var resource */
protected $multiHandle = null; // curl_multi handle
/** @var string|null SSL certificates path */
protected $proxy;
/** @var string */
protected $userAgent = 'wikimedia/multi-http-client v1.0';
+ /** @var LoggerInterface */
+ protected $logger;
/**
* @param array $options
}
}
static $opts = [
- 'connTimeout', 'reqTimeout', 'usePipelining', 'maxConnsPerHost', 'proxy', 'userAgent'
+ 'connTimeout', 'reqTimeout', 'usePipelining', 'maxConnsPerHost',
+ 'proxy', 'userAgent', 'logger'
];
foreach ( $opts as $key ) {
if ( isset( $options[$key] ) ) {
$this->$key = $options[$key];
}
}
+ if ( $this->logger === null ) {
+ $this->logger = new NullLogger;
+ }
}
/**
} elseif ( !isset( $req['url'] ) ) {
throw new Exception( "Request has no 'url' field set." );
}
+ $this->logger->debug( "{$req['method']}: {$req['url']}" );
$req['query'] = isset( $req['query'] ) ? $req['query'] : [];
$headers = []; // normalized headers
if ( isset( $req['headers'] ) ) {
unset( $req ); // don't assign over this by accident
$indexes = array_keys( $reqs );
- if ( function_exists( 'curl_multi_setopt' ) ) { // PHP 5.5
- if ( isset( $opts['usePipelining'] ) ) {
- curl_multi_setopt( $chm, CURLMOPT_PIPELINING, (int)$opts['usePipelining'] );
- }
- if ( isset( $opts['maxConnsPerHost'] ) ) {
- // Keep these sockets around as they may be needed later in the request
- curl_multi_setopt( $chm, CURLMOPT_MAXCONNECTS, (int)$opts['maxConnsPerHost'] );
- }
+ if ( isset( $opts['usePipelining'] ) ) {
+ curl_multi_setopt( $chm, CURLMOPT_PIPELINING, (int)$opts['usePipelining'] );
+ }
+ if ( isset( $opts['maxConnsPerHost'] ) ) {
+ // Keep these sockets around as they may be needed later in the request
+ curl_multi_setopt( $chm, CURLMOPT_MAXCONNECTS, (int)$opts['maxConnsPerHost'] );
}
// @TODO: use a per-host rolling handle window (e.g. CURLMOPT_MAX_HOST_CONNECTIONS)
// Wait (if possible) for available work...
if ( $active > 0 && $mrc == CURLM_OK ) {
if ( curl_multi_select( $chm, 10 ) == -1 ) {
- // PHP bug 63411; http://curl.haxx.se/libcurl/c/curl_multi_fdset.html
+ // PHP bug 63411; https://curl.haxx.se/libcurl/c/curl_multi_fdset.html
usleep( 5000 ); // 5ms
}
}
if ( function_exists( 'curl_strerror' ) ) {
$req['response']['error'] .= " " . curl_strerror( $errno );
}
+ $this->logger->warning( "Error fetching URL \"{$req['url']}\": " .
+ $req['response']['error'] );
}
} else {
$req['response']['error'] = "(curl error: no status set)";
unset( $req ); // don't assign over this by accident
// Restore the default settings
- if ( function_exists( 'curl_multi_setopt' ) ) { // PHP 5.5
- curl_multi_setopt( $chm, CURLMOPT_PIPELINING, (int)$this->usePipelining );
- curl_multi_setopt( $chm, CURLMOPT_MAXCONNECTS, (int)$this->maxConnsPerHost );
- }
+ curl_multi_setopt( $chm, CURLMOPT_PIPELINING, (int)$this->usePipelining );
+ curl_multi_setopt( $chm, CURLMOPT_MAXCONNECTS, (int)$this->maxConnsPerHost );
return $reqs;
}
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
$url = $req['url'];
- // PHP_QUERY_RFC3986 is PHP 5.4+ only
- $query = str_replace(
- [ '+', '%7E' ],
- [ '%20', '~' ],
- http_build_query( $req['query'], '', '&' )
- );
+ $query = http_build_query( $req['query'], '', '&', PHP_QUERY_RFC3986 );
if ( $query != '' ) {
$url .= strpos( $req['url'], '?' ) === false ? "?$query" : "&$query";
}
protected function getCurlMulti() {
if ( !$this->multiHandle ) {
$cmh = curl_multi_init();
- if ( function_exists( 'curl_multi_setopt' ) ) { // PHP 5.5
- curl_multi_setopt( $cmh, CURLMOPT_PIPELINING, (int)$this->usePipelining );
- curl_multi_setopt( $cmh, CURLMOPT_MAXCONNECTS, (int)$this->maxConnsPerHost );
- }
+ curl_multi_setopt( $cmh, CURLMOPT_PIPELINING, (int)$this->usePipelining );
+ curl_multi_setopt( $cmh, CURLMOPT_MAXCONNECTS, (int)$this->maxConnsPerHost );
$this->multiHandle = $cmh;
}
return $this->multiHandle;
}
+ /**
+ * Register a logger
+ *
+ * @param LoggerInterface
+ */
+ public function setLogger( LoggerInterface $logger ) {
+ $this->logger = $logger;
+ }
+
function __destruct() {
if ( $this->multiHandle ) {
curl_multi_close( $this->multiHandle );