* @param $context ResourceLoaderContext: Context in which a response should be formed
*/
public function respond( ResourceLoaderContext $context ) {
- global $wgCacheEpoch;
+ global $wgCacheEpoch, $wgUseFileCache;
+
+ // Use file cache if enabled and available...
+ if ( $wgUseFileCache ) {
+ $type = 'resources-' . ( $context->getOnly() === 'styles' ? 'css' : 'js' );
+ $hash = sha1( $context->getHash() . implode( ',', $context->getModules() ) );
+ $fileCache = ObjectFileCache::newFromKey( $hash, $type );
+ if ( $this->tryRespondFromFileCache( $fileCache, $context ) ) {
+ return; // output handled
+ }
+ }
// Buffer output to catch warnings. Normally we'd use ob_clean() on the
// top-level output buffer to clear warnings, but that breaks when ob_gzhandler
ob_end_clean();
echo $response;
+ // Save response to file cache if enabled
+ if ( isset( $fileCache ) && !$private && !$exceptions && !$missing ) {
+ $request = $context->getRequest();
+ // Don't cache URLs that the user was not given by site
+ if ( $request->getVal( 'fckey' ) == self::fileCacheKey( $request->getRequestURL() ) ) {
+ $fileCache->saveText( $response );
+ }
+ }
+
wfProfileOut( __METHOD__ );
}
return false;
}
+ /**
+ * Send out code for a response from file cache if possible
+ *
+ * @param $fileCache ObjectFileCache: Cache object for this request URL
+ * @param $context ResourceLoaderContext: Context in which to generate a response
+ * @return bool If this found a cache file and handled the response
+ */
+ protected function tryRespondFromFileCache(
+ ObjectFileCache $fileCache, ResourceLoaderContext $context
+ ) {
+ global $wgResourceLoaderMaxage;
+ // Buffer output to catch warnings.
+ ob_start();
+ // Get the maximum age the cache can be
+ $maxage = is_null( $context->getVersion() )
+ ? $wgResourceLoaderMaxage['unversioned']['server']
+ : $wgResourceLoaderMaxage['versioned']['server'];
+ // Minimum timestamp the cache file must have
+ $good = $fileCache->isCacheGood( wfTimestamp( TS_MW, time() - $maxage ) );
+ if ( !$good ) {
+ try { // RL always hits the DB on file cache miss...
+ wfGetDB( DB_SLAVE );
+ } catch( DBConnectionError $e ) { // ...check if we need to fallback to cache
+ $good = $fileCache->isCacheGood(); // cache existence check
+ }
+ }
+ if ( $good ) {
+ $ts = $fileCache->cacheTimestamp();
+ // Send content type and cache headers
+ $this->sendResponseHeaders( $context, $ts, false );
+ // If there's an If-Modified-Since header, respond with a 304 appropriately
+ if ( $this->tryRespondLastModified( $context, $ts ) ) {
+ return; // output handled (buffers cleared)
+ }
+ $response = $fileCache->fetchText();
+ // Remove the output buffer and output the response
+ ob_end_clean();
+ echo $response . "\n/* Cached {$ts} */";
+ return true; // cache hit
+ }
+ ob_end_clean();
+ return false; // cache miss
+ }
+
/**
* Generates code for a response
*
*/
public static function makeLoaderURL( $modules, $lang, $skin, $user = null, $version = null, $debug = false, $only = null,
$printable = false, $handheld = false, $extraQuery = array() ) {
- global $wgLoadScript;
+ global $wgLoadScript, $wgUseFileCache;
$query = self::makeLoaderQuery( $modules, $lang, $skin, $user, $version, $debug,
$only, $printable, $handheld, $extraQuery
);
-
+
+ $url = wfAppendQuery( $wgLoadScript, $query );
+ // Avoid deliberate FS pollution with hand-made URLs
+ if ( $wgUseFileCache ) {
+ $url .= '&fckey=' . self::fileCacheKey( $url . '&*' );
+ }
+
// Prevent the IE6 extension check from being triggered (bug 28840)
- // by appending a character that's invalid in Windows extensions ('*')
- return wfExpandUrl( wfAppendQuery( $wgLoadScript, $query ) . '&*', PROTO_RELATIVE );
+ // by appending a character that's invalid in Windows extensions ('*')
+ return $url . '&*';
+ }
+
+ /**
+ * Get a filecache key for a load.php URL
+ * @return string
+ */
+ protected static function fileCacheKey( $url ) {
+ global $wgSecretKey;
+ return sha1( preg_replace( '/&fckey=[^&]*/', '', $url ) . $wgSecretKey . wfWikiID() );
}
/**
);
}
+ /**
+ * Test for IP::sanitizeRange()
+ * @dataProvider provideIPCIDRs
+ */
+ function testSanitizeRange( $input, $expected, $description ) {
+ $this->assertEquals( $expected, IP::sanitizeRange( $input ), $description );
+ }
+
+ /**
+ * Provider for IP::testSanitizeRange()
+ */
+ function provideIPCIDRs() {
+ return array(
+ array( '35.56.31.252/16', '35.56.0.0/16', 'IPv4 range' ),
+ array( '135.16.21.252/24', '135.16.21.0/24', 'IPv4 range' ),
+ array( '5.36.71.252/32', '5.36.71.252/32', 'IPv4 silly range' ),
+ array( '5.36.71.252', '5.36.71.252', 'IPv4 non-range' ),
+ array( '0:1:2:3:4:c5:f6:7/96', '0:1:2:3:4:C5:0:0/96', 'IPv6 range' ),
+ array( '0:1:2:3:4:5:6:7/120', '0:1:2:3:4:5:6:0/120', 'IPv6 range' ),
+ array( '0:e1:2:3:4:5:e6:7/128', '0:E1:2:3:4:5:E6:7/128', 'IPv6 silly range' ),
+ array( '0:1:A2:3:4:5:c6:7', '0:1:A2:3:4:5:c6:7', 'IPv6 non range' ),
+ );
+ }
}