* @defgroup HTTP HTTP
*/
+use MediaWiki\Logger\LoggerFactory;
+
/**
* Various HTTP related functions
* @ingroup HTTP
* to avoid attacks on intranet services accessible by HTTP.
* - userAgent A user agent, if you want to override the default
* MediaWiki/$wgVersion
+ * @param string $caller The method making this request, for profiling
* @return string|bool (bool)false on failure or a string on success
*/
- public static function request( $method, $url, $options = array() ) {
+ public static function request( $method, $url, $options = array(), $caller = __METHOD__ ) {
wfDebug( "HTTP: $method: $url\n" );
$options['method'] = strtoupper( $method );
$options['connectTimeout'] = 'default';
}
- $req = MWHttpRequest::factory( $url, $options );
+ $req = MWHttpRequest::factory( $url, $options, $caller );
$status = $req->execute();
- $content = false;
if ( $status->isOK() ) {
- $content = $req->getContent();
+ return $req->getContent();
+ } else {
+ $errors = $status->getErrorsByType( 'error' );
+ $logger = LoggerFactory::getInstance( 'http' );
+ $logger->warning( $status->getWikiText(), array( 'caller' => $caller ) );
+ return false;
}
- return $content;
}
/**
* Simple wrapper for Http::request( 'GET' )
* @see Http::request()
+ * @since 1.25 Second parameter $timeout removed. Second parameter
+ * is now $options which can be given a 'timeout'
*
* @param string $url
- * @param string $timeout
* @param array $options
+ * @param string $caller The method making this request, for profiling
* @return string
*/
- public static function get( $url, $timeout = 'default', $options = array() ) {
- $options['timeout'] = $timeout;
- return Http::request( 'GET', $url, $options );
+ public static function get( $url, $options = array(), $caller = __METHOD__ ) {
+ $args = func_get_args();
+ if ( isset( $args[1] ) && ( is_string( $args[1] ) || is_numeric( $args[1] ) ) ) {
+ // Second was used to be the timeout
+ // And third parameter used to be $options
+ wfWarn( "Second parameter should not be a timeout.", 2 );
+ $options = isset( $args[2] ) && is_array( $args[2] ) ?
+ $args[2] : array();
+ $options['timeout'] = $args[1];
+ $caller = __METHOD__;
+ }
+ return Http::request( 'GET', $url, $options, $caller );
}
/**
*
* @param string $url
* @param array $options
+ * @param string $caller The method making this request, for profiling
* @return string
*/
- public static function post( $url, $options = array() ) {
- return Http::request( 'POST', $url, $options );
+ public static function post( $url, $options = array(), $caller = __METHOD__ ) {
+ return Http::request( 'POST', $url, $options, $caller );
}
/**
public $status;
+ /**
+ * @var Profiler
+ */
+ protected $profiler;
+
+ /**
+ * @var string
+ */
+ protected $profileName;
+
/**
* @param string $url Url to use. If protocol-relative, will be expanded to an http:// URL
* @param array $options (optional) extra params to pass (see Http::request())
+ * @param string $caller The method making this request, for profiling
+ * @param Profiler $profiler An instance of the profiler for profiling, or null
*/
- protected function __construct( $url, $options = array() ) {
+ protected function __construct( $url, $options = array(), $caller = __METHOD__, $profiler = null ) {
global $wgHTTPTimeout, $wgHTTPConnectTimeout;
$this->url = wfExpandUrl( $url, PROTO_HTTP );
$this->parsedUrl = wfParseUrl( $this->url );
if ( !$this->parsedUrl || !Http::isValidURI( $this->url ) ) {
- $this->status = Status::newFatal( 'http-invalid-url' );
+ $this->status = Status::newFatal( 'http-invalid-url', $url );
} else {
$this->status = Status::newGood( 100 ); // continue
}
if ( $this->noProxy ) {
$this->proxy = ''; // noProxy takes precedence
}
+
+ // Profile based on what's calling us
+ $this->profiler = $profiler;
+ $this->profileName = $caller;
}
/**
* Generate a new request object
* @param string $url Url to use
* @param array $options (optional) extra params to pass (see Http::request())
+ * @param string $caller The method making this request, for profiling
* @throws MWException
* @return CurlHttpRequest|PhpHttpRequest
* @see MWHttpRequest::__construct
*/
- public static function factory( $url, $options = null ) {
+ public static function factory( $url, $options = null, $caller = __METHOD__ ) {
if ( !Http::$httpEngine ) {
Http::$httpEngine = function_exists( 'curl_init' ) ? 'curl' : 'php';
} elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) {
switch ( Http::$httpEngine ) {
case 'curl':
- return new CurlHttpRequest( $url, $options );
+ return new CurlHttpRequest( $url, $options, $caller, Profiler::instance() );
case 'php':
if ( !wfIniGetBool( 'allow_url_fopen' ) ) {
throw new MWException( __METHOD__ . ': allow_url_fopen ' .
'http://php.net/curl.'
);
}
- return new PhpHttpRequest( $url, $options );
+ return new PhpHttpRequest( $url, $options, $caller, Profiler::instance() );
default:
throw new MWException( __METHOD__ . ': The setting of Http::$httpEngine is not valid.' );
}
}
if ( $this->followRedirects && $this->canFollowRedirects() ) {
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
if ( !curl_setopt( $curlHandle, CURLOPT_FOLLOWLOCATION, true ) ) {
wfDebug( __METHOD__ . ": Couldn't set CURLOPT_FOLLOWLOCATION. " .
"Probably safe_mode or open_basedir is set.\n" );
// Continue the processing. If it were in curl_setopt_array,
// processing would have halted on its entry
}
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
+ }
+
+ if ( $this->profiler ) {
+ $profileSection = $this->profiler->scopedProfileIn(
+ __METHOD__ . '-' . $this->profileName
+ );
}
$curlRes = curl_exec( $curlHandle );
curl_close( $curlHandle );
+ if ( $this->profiler ) {
+ $this->profiler->scopedProfileOut( $profileSection );
+ }
+
$this->parseHeader();
$this->setStatus();
}
if ( $this->sslVerifyHost ) {
- $options['ssl']['CN_match'] = $this->parsedUrl['host'];
+ // PHP 5.6.0 deprecates CN_match, in favour of peer_name which
+ // actually checks SubjectAltName properly.
+ if ( version_compare( PHP_VERSION, '5.6.0', '>=' ) ) {
+ $options['ssl']['peer_name'] = $this->parsedUrl['host'];
+ } else {
+ $options['ssl']['CN_match'] = $this->parsedUrl['host'];
+ }
}
if ( is_dir( $this->caInfo ) ) {
$result = array();
+ if ( $this->profiler ) {
+ $profileSection = $this->profiler->scopedProfileIn(
+ __METHOD__ . '-' . $this->profileName
+ );
+ }
do {
$reqCount++;
- wfSuppressWarnings();
+ MediaWiki\suppressWarnings();
$fh = fopen( $url, "r", false, $context );
- wfRestoreWarnings();
+ MediaWiki\restoreWarnings();
if ( !$fh ) {
+ // HACK for instant commons.
+ // If we are contacting (commons|upload).wikimedia.org
+ // try again with CN_match for en.wikipedia.org
+ // as php does not handle SubjectAltName properly
+ // prior to "peer_name" option in php 5.6
+ if ( isset( $options['ssl']['CN_match'] )
+ && ( $options['ssl']['CN_match'] === 'commons.wikimedia.org'
+ || $options['ssl']['CN_match'] === 'upload.wikimedia.org' )
+ ) {
+ $options['ssl']['CN_match'] = 'en.wikipedia.org';
+ $context = stream_context_create( $options );
+ continue;
+ }
break;
}
break;
}
} while ( true );
+ if ( $this->profiler ) {
+ $this->profiler->scopedProfileOut( $profileSection );
+ }
$this->setStatus();