Tim Starling has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/338705 )

Change subject: Log a backtrace from the culprit location if headers were 
already sent
......................................................................

Log a backtrace from the culprit location if headers were already sent

Install the backtrace collector very early, so that we can get the
backtrace even if headers were sent from LocalSettings.php.

Bug: T157392
Change-Id: I9bc732b34481c95afb5362e135a87bd4302498e2
---
M autoload.php
A includes/HeaderCallback.php
M includes/Setup.php
M includes/WebResponse.php
M includes/WebStart.php
M includes/resourceloader/ResourceLoader.php
6 files changed, 71 insertions(+), 32 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/05/338705/1

diff --git a/autoload.php b/autoload.php
index b21310e..fce7891 100644
--- a/autoload.php
+++ b/autoload.php
@@ -852,6 +852,7 @@
        'MediaWiki\\Auth\\UsernameAuthenticationRequest' => __DIR__ . 
'/includes/auth/UsernameAuthenticationRequest.php',
        'MediaWiki\\Diff\\ComplexityException' => __DIR__ . 
'/includes/diff/ComplexityException.php',
        'MediaWiki\\Diff\\WordAccumulator' => __DIR__ . 
'/includes/diff/WordAccumulator.php',
+       'MediaWiki\\HeaderCallback' => __DIR__ . '/includes/HeaderCallback.php',
        'MediaWiki\\Interwiki\\ClassicInterwikiLookup' => __DIR__ . 
'/includes/interwiki/ClassicInterwikiLookup.php',
        'MediaWiki\\Interwiki\\InterwikiLookup' => __DIR__ . 
'/includes/interwiki/InterwikiLookup.php',
        'MediaWiki\\Interwiki\\InterwikiLookupAdapter' => __DIR__ . 
'/includes/interwiki/InterwikiLookupAdapter.php',
diff --git a/includes/HeaderCallback.php b/includes/HeaderCallback.php
new file mode 100644
index 0000000..510cec6
--- /dev/null
+++ b/includes/HeaderCallback.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace MediaWiki;
+
+class HeaderCallback {
+       private static $headersSentException;
+       private static $messageSent = false;
+
+       /**
+        * Register a callback to be called when headers are sent. There can 
only
+        * be one of these handlers active, so all relevant actions have to be 
in
+        * here.
+        */
+       public static function register() {
+               header_register_callback( [ __CLASS__, 'callback' ] );
+       }
+
+       /**
+        * The callback, which is called by the transport
+        */
+       public static function callback() {
+               // Prevent caching of responses with cookies (T127993)
+               $headers = [];
+               foreach ( headers_list() as $header ) {
+                       list( $name, $value ) = explode( ':', $header, 2 );
+                       $headers[strtolower( trim( $name ) )][] = trim( $value 
);
+               }
+
+               if ( isset( $headers['set-cookie'] ) ) {
+                       $cacheControl = isset( $headers['cache-control'] )
+                               ? implode( ', ', $headers['cache-control'] )
+                               : '';
+
+                       if ( !preg_match( 
'/(?:^|,)\s*(?:private|no-cache|no-store)\s*(?:$|,)/i', $cacheControl ) ) {
+                               header( 'Expires: Thu, 01 Jan 1970 00:00:00 
GMT' );
+                               header( 'Cache-Control: private, max-age=0, 
s-maxage=0' );
+                               \MediaWiki\Logger\LoggerFactory::getInstance( 
'cache-cookies' )->warning(
+                                       'Cookies set on {url} with 
Cache-Control "{cache-control}"', [
+                                               'url' => 
\WebRequest::getGlobalRequestURL(),
+                                               'cookies' => 
$headers['set-cookie'],
+                                               'cache-control' => 
$cacheControl ?: '<not set>',
+                                       ]
+                               );
+                       }
+               }
+
+               // Save a backtrace for logging in case it turns out that 
headers were sent prematurely
+               self::$headersSentException = new \Exception( 'Headers already 
sent from this point' );
+       }
+
+       /**
+        * Log a warning message if headers have already been sent. This can be
+        * called before flushing the output.
+        */
+       public static function warnIfHeadersSent() {
+               if ( headers_sent() && !self::$messageSent ) {
+                       self::$messageSent = true;
+                       \MWDebug::warning( 'Headers already sent, should send 
headers earlier than ' . wfGetCaller( 3 ) );
+                       $logger = \MediaWiki\Logger\LoggerFactory::getInstance( 
'headers-sent' );
+                       $logger->error( 'Warning: headers already sent', [
+                               'exception' => self::$headersSentException
+                       ] );
+               }
+       }
+}
diff --git a/includes/Setup.php b/includes/Setup.php
index 01ba1e8..72ed1fd 100644
--- a/includes/Setup.php
+++ b/includes/Setup.php
@@ -521,35 +521,6 @@
 // is complete.
 define( 'MW_SERVICE_BOOTSTRAP_COMPLETE', 1 );
 
-// Install a header callback to prevent caching of responses with cookies 
(T127993)
-if ( !$wgCommandLineMode ) {
-       header_register_callback( function () {
-               $headers = [];
-               foreach ( headers_list() as $header ) {
-                       list( $name, $value ) = explode( ':', $header, 2 );
-                       $headers[strtolower( trim( $name ) )][] = trim( $value 
);
-               }
-
-               if ( isset( $headers['set-cookie'] ) ) {
-                       $cacheControl = isset( $headers['cache-control'] )
-                               ? implode( ', ', $headers['cache-control'] )
-                               : '';
-
-                       if ( !preg_match( 
'/(?:^|,)\s*(?:private|no-cache|no-store)\s*(?:$|,)/i', $cacheControl ) ) {
-                               header( 'Expires: Thu, 01 Jan 1970 00:00:00 
GMT' );
-                               header( 'Cache-Control: private, max-age=0, 
s-maxage=0' );
-                               MediaWiki\Logger\LoggerFactory::getInstance( 
'cache-cookies' )->warning(
-                                       'Cookies set on {url} with 
Cache-Control "{cache-control}"', [
-                                               'url' => 
WebRequest::getGlobalRequestURL(),
-                                               'cookies' => 
$headers['set-cookie'],
-                                               'cache-control' => 
$cacheControl ?: '<not set>',
-                                       ]
-                               );
-                       }
-               }
-       } );
-}
-
 MWExceptionHandler::installHandler();
 
 require_once "$IP/includes/compat/normal/UtfNormalUtil.php";
diff --git a/includes/WebResponse.php b/includes/WebResponse.php
index f5fb47f..0208a72 100644
--- a/includes/WebResponse.php
+++ b/includes/WebResponse.php
@@ -39,9 +39,7 @@
         * @param null|int $http_response_code Forces the HTTP response code to 
the specified value.
         */
        public function header( $string, $replace = true, $http_response_code = 
null ) {
-               if ( headers_sent() ) {
-                       MWDebug::warning( 'Headers already sent, should send 
headers earlier than ' . wfGetCaller() );
-               }
+               \MediaWiki\HeaderCallback::warnIfHeadersSent();
                if ( $http_response_code ) {
                        header( $string, $replace, $http_response_code );
                } else {
diff --git a/includes/WebStart.php b/includes/WebStart.php
index 6e4fb09..861e532 100644
--- a/includes/WebStart.php
+++ b/includes/WebStart.php
@@ -104,6 +104,9 @@
        die( 1 );
 }
 
+# Install a header callback
+MediaWiki\HeaderCallback::register();
+
 if ( defined( 'MW_CONFIG_CALLBACK' ) ) {
        # Use a callback function to configure MediaWiki
        call_user_func( MW_CONFIG_CALLBACK );
diff --git a/includes/resourceloader/ResourceLoader.php 
b/includes/resourceloader/ResourceLoader.php
index a55cbc1..59c60df 100644
--- a/includes/resourceloader/ResourceLoader.php
+++ b/includes/resourceloader/ResourceLoader.php
@@ -818,6 +818,7 @@
         * @return void
         */
        protected function sendResponseHeaders( ResourceLoaderContext $context, 
$etag, $errors ) {
+               \MediaWiki\HeaderCallback::warnIfHeadersSent();
                $rlMaxage = $this->config->get( 'ResourceLoaderMaxage' );
                // Use a short cache expiry so that updates propagate to 
clients quickly, if:
                // - No version specified (shared resources, e.g. stylesheets)

-- 
To view, visit https://gerrit.wikimedia.org/r/338705
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I9bc732b34481c95afb5362e135a87bd4302498e2
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Tim Starling <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to