Improve database endAtomic() error messages
[lhc/web/wiklou.git] / includes / MediaWiki.php
index 8385a06..21857b9 100644 (file)
@@ -178,7 +178,7 @@ class MediaWiki {
                }
 
                $unused = null; // To pass it by reference
-               Hooks::run( 'BeforeInitialize', array( &$title, &$unused, &$output, &$user, $request, $this ) );
+               Hooks::run( 'BeforeInitialize', [ &$title, &$unused, &$output, &$user, $request, $this ] );
 
                // Invalid titles. Bug 21776: The interwikis must redirect even if the page name is empty.
                if ( is_null( $title ) || ( $title->getDBkey() == '' && !$title->isExternal() )
@@ -197,7 +197,7 @@ class MediaWiki {
                // We have to check here to catch special pages etc.
                // We will check again in Article::view().
                $permErrors = $title->isSpecial( 'RunJobs' )
-                       ? array() // relies on HMAC key signature alone
+                       ? [] // relies on HMAC key signature alone
                        : $title->getUserPermissionsErrors( 'read', $user );
                if ( count( $permErrors ) ) {
                        // Bug 32276: allowing the skin to generate output with $wgTitle or
@@ -220,7 +220,7 @@ class MediaWiki {
                if ( $title->isExternal() ) {
                        $rdfrom = $request->getVal( 'rdfrom' );
                        if ( $rdfrom ) {
-                               $url = $title->getFullURL( array( 'rdfrom' => $rdfrom ) );
+                               $url = $title->getFullURL( [ 'rdfrom' => $rdfrom ] );
                        } else {
                                $query = $request->getValues();
                                unset( $query['title'] );
@@ -246,30 +246,32 @@ class MediaWiki {
                } elseif ( !$this->tryNormaliseRedirect( $title ) ) {
                        // Prevent information leak via Special:MyPage et al (T109724)
                        if ( $title->isSpecialPage() ) {
-                               $specialPage = SpecialPageFactory::getPage( $title->getDBKey() );
-                               if ( $specialPage instanceof RedirectSpecialPage
-                                       && $this->config->get( 'HideIdentifiableRedirects' )
-                                       && $specialPage->personallyIdentifiableTarget()
-                               ) {
-                                       list( , $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBKey() );
-                                       $target = $specialPage->getRedirect( $subpage );
-                                       // target can also be true. We let that case fall through to normal processing.
-                                       if ( $target instanceof Title ) {
-                                               $query = $specialPage->getRedirectQuery() ?: array();
-                                               $request = new DerivativeRequest( $this->context->getRequest(), $query );
-                                               $request->setRequestURL( $this->context->getRequest()->getRequestURL() );
-                                               $this->context->setRequest( $request );
-                                               // Do not varnish cache these. May vary even for anons
-                                               $this->context->getOutput()->lowerCdnMaxage( 0 );
-                                               $this->context->setTitle( $target );
-                                               $wgTitle = $target;
-                                               // Reset action type cache. (Special pages have only view)
-                                               $this->action = null;
-                                               $title = $target;
-                                               $output->addJsConfigVars( array(
-                                                       'wgInternalRedirectTargetUrl' => $target->getFullURL( $query ),
-                                               ) );
-                                               $output->addModules( 'mediawiki.action.view.redirect' );
+                               $specialPage = SpecialPageFactory::getPage( $title->getDBkey() );
+                               if ( $specialPage instanceof RedirectSpecialPage ) {
+                                       $specialPage->setContext( $this->context );
+                                       if ( $this->config->get( 'HideIdentifiableRedirects' )
+                                               && $specialPage->personallyIdentifiableTarget()
+                                       ) {
+                                               list( , $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
+                                               $target = $specialPage->getRedirect( $subpage );
+                                               // target can also be true. We let that case fall through to normal processing.
+                                               if ( $target instanceof Title ) {
+                                                       $query = $specialPage->getRedirectQuery() ?: [];
+                                                       $request = new DerivativeRequest( $this->context->getRequest(), $query );
+                                                       $request->setRequestURL( $this->context->getRequest()->getRequestURL() );
+                                                       $this->context->setRequest( $request );
+                                                       // Do not varnish cache these. May vary even for anons
+                                                       $this->context->getOutput()->lowerCdnMaxage( 0 );
+                                                       $this->context->setTitle( $target );
+                                                       $wgTitle = $target;
+                                                       // Reset action type cache. (Special pages have only view)
+                                                       $this->action = null;
+                                                       $title = $target;
+                                                       $output->addJsConfigVars( [
+                                                               'wgInternalRedirectTargetUrl' => $target->getFullURL( $query ),
+                                                       ] );
+                                                       $output->addModules( 'mediawiki.action.view.redirect' );
+                                               }
                                        }
                                }
                        }
@@ -324,8 +326,8 @@ class MediaWiki {
 
                if ( $request->getVal( 'action', 'view' ) != 'view'
                        || $request->wasPosted()
-                       || count( $request->getValueNames( array( 'action', 'title' ) ) )
-                       || !Hooks::run( 'TestCanonicalRedirect', array( $request, $title, $output ) )
+                       || count( $request->getValueNames( [ 'action', 'title' ] ) )
+                       || !Hooks::run( 'TestCanonicalRedirect', [ $request, $title, $output ] )
                ) {
                        return false;
                }
@@ -421,7 +423,7 @@ class MediaWiki {
                        $ignoreRedirect = $target = false;
 
                        Hooks::run( 'InitializeArticleMaybeRedirect',
-                               array( &$title, &$request, &$ignoreRedirect, &$target, &$article ) );
+                               [ &$title, &$request, &$ignoreRedirect, &$target, &$article ] );
                        $page = $article->getPage(); // reflect any hook changes
 
                        // Follow redirects only for... redirects.
@@ -471,7 +473,7 @@ class MediaWiki {
                $user = $this->context->getUser();
 
                if ( !Hooks::run( 'MediaWikiPerformAction',
-                               array( $output, $page, $title, $user, $request, $this ) )
+                               [ $output, $page, $title, $user, $request, $this ] )
                ) {
                        return;
                }
@@ -485,6 +487,7 @@ class MediaWiki {
                        $trxProfiler = Profiler::instance()->getTransactionProfiler();
                        if ( $request->wasPosted() && !$action->doesWrites() ) {
                                $trxProfiler->setExpectations( $trxLimits['POST-nonwrite'], __METHOD__ );
+                               $request->markAsSafeRequest();
                        }
 
                        # Let CDN cache things if we can purge them.
@@ -502,7 +505,7 @@ class MediaWiki {
                        return;
                }
 
-               if ( Hooks::run( 'UnknownAction', array( $request->getVal( 'action', 'view' ), $page ) ) ) {
+               if ( Hooks::run( 'UnknownAction', [ $request->getVal( 'action', 'view' ), $page ] ) ) {
                        $output->setStatusCode( 404 );
                        $output->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
                }
@@ -555,7 +558,7 @@ class MediaWiki {
                $factory->commitMasterChanges(
                        __METHOD__,
                        // Abort if any transaction was too big
-                       array( 'maxWriteDuration' => $config->get( 'MaxUserDBWriteDuration' ) )
+                       [ 'maxWriteDuration' => $config->get( 'MaxUserDBWriteDuration' ) ]
                );
                // Record ChronologyProtector positions
                $factory->shutdown();
@@ -570,7 +573,7 @@ class MediaWiki {
                $request = $context->getRequest();
                if ( $request->wasPosted() && $factory->hasOrMadeRecentMasterChanges() ) {
                        $expires = time() + $config->get( 'DataCenterUpdateStickTTL' );
-                       $options = array( 'prefix' => '' );
+                       $options = [ 'prefix' => '' ];
                        $request->response()->setCookie( 'UseDC', 'master', $expires, $options );
                        $request->response()->setCookie( 'UseCDNCache', 'false', $expires, $options );
                }
@@ -583,6 +586,13 @@ class MediaWiki {
                        $request->response()->header( "X-Database-Lagged: true" );
                        wfDebugLog( 'replication', "Lagged DB used; CDN cache TTL limited to $maxAge seconds" );
                }
+
+               // Avoid long-term cache pollution due to message cache rebuild timeouts (T133069)
+               if ( MessageCache::singleton()->isDisabled() ) {
+                       $maxAge = $config->get( 'CdnMaxageSubstitute' );
+                       $context->getOutput()->lowerCdnMaxage( $maxAge );
+                       $request->response()->header( "X-Response-Substitute: true" );
+               }
        }
 
        /**
@@ -657,10 +667,10 @@ class MediaWiki {
                $trxLimits = $this->config->get( 'TrxProfilerLimits' );
                $trxProfiler = Profiler::instance()->getTransactionProfiler();
                $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
-               if ( $request->wasPosted() ) {
-                       $trxProfiler->setExpectations( $trxLimits['POST'], __METHOD__ );
-               } else {
+               if ( $request->hasSafeMethod() ) {
                        $trxProfiler->setExpectations( $trxLimits['GET'], __METHOD__ );
+               } else {
+                       $trxProfiler->setExpectations( $trxLimits['POST'], __METHOD__ );
                }
 
                // If the user has forceHTTPS set to true, or if the user
@@ -670,6 +680,8 @@ class MediaWiki {
                // isLoggedIn() will do all sorts of weird stuff.
                if (
                        $request->getProtocol() == 'http' &&
+                       // switch to HTTPS only when supported by the server
+                       preg_match( '#^https://#', wfExpandUrl( $request->getRequestURL(), PROTO_HTTPS ) ) &&
                        (
                                $request->getSession()->shouldForceHTTPS() ||
                                // Check the cookie manually, for paranoia
@@ -687,7 +699,7 @@ class MediaWiki {
                        $redirUrl = preg_replace( '#^http://#', 'https://', $oldUrl );
 
                        // ATTENTION: This hook is likely to be removed soon due to overall design of the system.
-                       if ( Hooks::run( 'BeforeHttpsRedirect', array( $this->context, &$redirUrl ) ) ) {
+                       if ( Hooks::run( 'BeforeHttpsRedirect', [ $this->context, &$redirUrl ] ) ) {
 
                                if ( $request->wasPosted() ) {
                                        // This is weird and we'd hope it almost never happens. This
@@ -752,9 +764,13 @@ class MediaWiki {
                // Assure deferred updates are not in the main transaction
                wfGetLBFactory()->commitMasterChanges( __METHOD__ );
 
-               // Ignore things like master queries/connections on GET requests
-               // as long as they are in deferred updates (which catch errors).
-               Profiler::instance()->getTransactionProfiler()->resetExpectations();
+               // Loosen DB query expectations since the HTTP client is unblocked
+               $trxProfiler = Profiler::instance()->getTransactionProfiler();
+               $trxProfiler->resetExpectations();
+               $trxProfiler->setExpectations(
+                       $this->config->get( 'TrxProfilerLimits' )['PostSend'],
+                       __METHOD__
+               );
 
                // Do any deferred jobs
                DeferredUpdates::doUpdates( 'enqueue' );
@@ -807,7 +823,7 @@ class MediaWiki {
                if ( !$this->config->get( 'RunJobsAsync' ) ) {
                        // Fall back to running the job here while the user waits
                        $runner = new JobRunner( $runJobsLogger );
-                       $runner->run( array( 'maxJobs'  => $n ) );
+                       $runner->run( [ 'maxJobs'  => $n ] );
                        return;
                }
 
@@ -820,17 +836,26 @@ class MediaWiki {
                        return; // do not make the site unavailable
                }
 
-               $query = array( 'title' => 'Special:RunJobs',
-                       'tasks' => 'jobs', 'maxjobs' => $n, 'sigexpiry' => time() + 5 );
+               $query = [ 'title' => 'Special:RunJobs',
+                       'tasks' => 'jobs', 'maxjobs' => $n, 'sigexpiry' => time() + 5 ];
                $query['signature'] = SpecialRunJobs::getQuerySignature(
                        $query, $this->config->get( 'SecretKey' ) );
 
                $errno = $errstr = null;
                $info = wfParseUrl( $this->config->get( 'Server' ) );
                MediaWiki\suppressWarnings();
+               $host = $info['host'];
+               $port = 80;
+               if ( isset( $info['scheme'] ) && $info['scheme'] == 'https' ) {
+                       $host = "tls://" . $host;
+                       $port = 443;
+               }
+               if ( isset( $info['port'] ) ) {
+                       $port = $info['port'];
+               }
                $sock = fsockopen(
-                       $info['host'],
-                       isset( $info['port'] ) ? $info['port'] : 80,
+                       $host,
+                       $port,
                        $errno,
                        $errstr,
                        // If it takes more than 100ms to connect to ourselves there
@@ -842,7 +867,7 @@ class MediaWiki {
                        $runJobsLogger->error( "Failed to start cron API (socket error $errno): $errstr" );
                        // Fall back to running the job here while the user waits
                        $runner = new JobRunner( $runJobsLogger );
-                       $runner->run( array( 'maxJobs'  => $n ) );
+                       $runner->run( [ 'maxJobs'  => $n ] );
                        return;
                }