Drop zh-tw message "saveprefs"
[lhc/web/wiklou.git] / includes / HttpFunctions.php
index 43dd2b2..1c79485 100644 (file)
@@ -25,6 +25,8 @@
  * @defgroup HTTP HTTP
  */
 
+use MediaWiki\Logger\LoggerFactory;
+
 /**
  * Various HTTP related functions
  * @ingroup HTTP
@@ -55,9 +57,10 @@ class 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 );
@@ -69,28 +72,42 @@ class Http {
                        $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 );
        }
 
        /**
@@ -99,10 +116,11 @@ class Http {
         *
         * @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 );
        }
 
        /**
@@ -216,18 +234,30 @@ class MWHttpRequest {
 
        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
                }
@@ -263,6 +293,10 @@ class MWHttpRequest {
                if ( $this->noProxy ) {
                        $this->proxy = ''; // noProxy takes precedence
                }
+
+               // Profile based on what's calling us
+               $this->profiler = $profiler;
+               $this->profileName = $caller;
        }
 
        /**
@@ -278,11 +312,12 @@ class MWHttpRequest {
         * 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' ) ) {
@@ -292,7 +327,7 @@ class MWHttpRequest {
 
                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 ' .
@@ -301,7 +336,7 @@ class MWHttpRequest {
                                                '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.' );
                }
@@ -762,14 +797,20 @@ class CurlHttpRequest extends MWHttpRequest {
                }
 
                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 );
@@ -783,10 +824,13 @@ class CurlHttpRequest extends MWHttpRequest {
 
                curl_close( $curlHandle );
 
+               if ( $this->profiler ) {
+                       $this->profiler->scopedProfileOut( $profileSection );
+               }
+
                $this->parseHeader();
                $this->setStatus();
 
-
                return $this->status;
        }
 
@@ -873,7 +917,13 @@ class PhpHttpRequest extends MWHttpRequest {
                }
 
                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 ) ) {
@@ -892,13 +942,31 @@ class PhpHttpRequest extends MWHttpRequest {
 
                $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;
                        }
 
@@ -922,6 +990,9 @@ class PhpHttpRequest extends MWHttpRequest {
                                break;
                        }
                } while ( true );
+               if ( $this->profiler ) {
+                       $this->profiler->scopedProfileOut( $profileSection );
+               }
 
                $this->setStatus();
 
@@ -953,7 +1024,6 @@ class PhpHttpRequest extends MWHttpRequest {
                }
                fclose( $fh );
 
-
                return $this->status;
        }
 }