Merge "Fix login API for users with @ in their usernames"
[lhc/web/wiklou.git] / includes / MediaWiki.php
index 7f20de1..9bbbd35 100644 (file)
@@ -574,26 +574,44 @@ class MediaWiki {
                wfDebug( __METHOD__ . ': pre-send deferred updates completed' );
 
                // Decide when clients block on ChronologyProtector DB position writes
-               if (
+               $urlDomainDistance = (
                        $request->wasPosted() &&
                        $output->getRedirect() &&
-                       $lbFactory->hasOrMadeRecentMasterChanges( INF ) &&
-                       self::isWikiClusterURL( $output->getRedirect(), $context )
-               ) {
+                       $lbFactory->hasOrMadeRecentMasterChanges( INF )
+               ) ? self::getUrlDomainDistance( $output->getRedirect(), $context ) : false;
+
+               if ( $urlDomainDistance === 'local' || $urlDomainDistance === 'remote' ) {
                        // OutputPage::output() will be fast; $postCommitWork will not be useful for
                        // masking the latency of syncing DB positions accross all datacenters synchronously.
                        // Instead, make use of the RTT time of the client follow redirects.
                        $flags = $lbFactory::SHUTDOWN_CHRONPROT_ASYNC;
+                       $cpPosTime = microtime( true );
                        // Client's next request should see 1+ positions with this DBMasterPos::asOf() time
-                       $safeUrl = $lbFactory->appendPreShutdownTimeAsQuery(
-                               $output->getRedirect(),
-                               microtime( true )
-                       );
-                       $output->redirect( $safeUrl );
+                       if ( $urlDomainDistance === 'local' ) {
+                               // Client will stay on this domain, so set an unobtrusive cookie
+                               $expires = time() + ChronologyProtector::POSITION_TTL;
+                               $options = [ 'prefix' => '' ];
+                               $request->response()->setCookie( 'cpPosTime', $cpPosTime, $expires, $options );
+                       } else {
+                               // Cookies may not work across wiki domains, so use a URL parameter
+                               $safeUrl = $lbFactory->appendPreShutdownTimeAsQuery(
+                                       $output->getRedirect(),
+                                       $cpPosTime
+                               );
+                               $output->redirect( $safeUrl );
+                       }
                } else {
                        // OutputPage::output() is fairly slow; run it in $postCommitWork to mask
                        // the latency of syncing DB positions accross all datacenters synchronously
                        $flags = $lbFactory::SHUTDOWN_CHRONPROT_SYNC;
+                       if ( $lbFactory->hasOrMadeRecentMasterChanges( INF ) ) {
+                               $cpPosTime = microtime( true );
+                               // Set a cookie in case the DB position store cannot sync accross datacenters.
+                               // This will at least cover the common case of the user staying on the domain.
+                               $expires = time() + ChronologyProtector::POSITION_TTL;
+                               $options = [ 'prefix' => '' ];
+                               $request->response()->setCookie( 'cpPosTime', $cpPosTime, $expires, $options );
+                       }
                }
                // Record ChronologyProtector positions for DBs affected in this request at this point
                $lbFactory->shutdown( $flags, $postCommitWork );
@@ -629,9 +647,9 @@ class MediaWiki {
        /**
         * @param string $url
         * @param IContextSource $context
-        * @return bool Whether $url is to something on this wiki farm
+        * @return string|bool Either "local" or "remote" if in the farm, false otherwise
         */
-       private function isWikiClusterURL( $url, IContextSource $context ) {
+       private function getUrlDomainDistance( $url, IContextSource $context ) {
                static $relevantKeys = [ 'host' => true, 'port' => true ];
 
                $infoCandidate = wfParseUrl( $url );
@@ -647,10 +665,14 @@ class MediaWiki {
                        $context->getConfig()->get( 'LocalVirtualHosts' )
                );
 
-               foreach ( $clusterHosts as $clusterHost ) {
-                       $infoHost = array_intersect_key( wfParseUrl( $clusterHost ), $relevantKeys );
+               foreach ( $clusterHosts as $i => $clusterHost ) {
+                       $parseUrl = wfParseUrl( $clusterHost );
+                       if ( !$parseUrl ) {
+                               continue;
+                       }
+                       $infoHost = array_intersect_key( $parseUrl, $relevantKeys );
                        if ( $infoCandidate === $infoHost ) {
-                               return true;
+                               return ( $i === 0 ) ? 'local' : 'remote';
                        }
                }