diff options
Diffstat (limited to 'includes/api/ApiMain.php')
-rw-r--r-- | includes/api/ApiMain.php | 183 |
1 files changed, 162 insertions, 21 deletions
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index ffdeb1e8..063e3574 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -121,7 +121,9 @@ class ApiMain extends ApiBase { private $mPrinter, $mModules, $mModuleNames, $mFormats, $mFormatNames; - private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode, $mSquidMaxage; + private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode; + private $mCacheMode = 'private'; + private $mCacheControl = array(); /** * Constructs an instance of ApiMain that utilizes the module and format specified by $request. @@ -164,7 +166,6 @@ class ApiMain extends ApiBase { $this->mRequest = & $request; - $this->mSquidMaxage = -1; // flag for executeActionWithErrorHandling() $this->mCommit = false; } @@ -193,13 +194,100 @@ class ApiMain extends ApiBase { * Only kept for backwards compatibility * @deprecated Use isWriteMode() instead */ - public function requestWriteMode() {} + public function requestWriteMode() { + if (!$this->mEnableWrite) + $this->dieUsageMsg(array('writedisabled')); + if (wfReadOnly()) + $this->dieUsageMsg(array('readonlytext')); + } /** * Set how long the response should be cached. */ public function setCacheMaxAge($maxage) { - $this->mSquidMaxage = $maxage; + $this->setCacheControl( array( + 'max-age' => $maxage, + 's-maxage' => $maxage + ) ); + } + + /** + * Set the type of caching headers which will be sent. + * + * @param $mode One of: + * - 'public': Cache this object in public caches, if the maxage or smaxage + * parameter is set, or if setCacheMaxAge() was called. If a maximum age is + * not provided by any of these means, the object will be private. + * - 'private': Cache this object only in private client-side caches. + * - 'anon-public-user-private': Make this object cacheable for logged-out + * users, but private for logged-in users. IMPORTANT: If this is set, it must be + * set consistently for a given URL, it cannot be set differently depending on + * things like the contents of the database, or whether the user is logged in. + * + * If the wiki does not allow anonymous users to read it, the mode set here + * will be ignored, and private caching headers will always be sent. In other words, + * the "public" mode is equivalent to saying that the data sent is as public as a page + * view. + * + * For user-dependent data, the private mode should generally be used. The + * anon-public-user-private mode should only be used where there is a particularly + * good performance reason for caching the anonymous response, but where the + * response to logged-in users may differ, or may contain private data. + * + * If this function is never called, then the default will be the private mode. + */ + public function setCacheMode( $mode ) { + if ( !in_array( $mode, array( 'private', 'public', 'anon-public-user-private' ) ) ) { + wfDebug( __METHOD__.": unrecognised cache mode \"$mode\"\n" ); + // Ignore for forwards-compatibility + return; + } + + if ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) ) { + // Private wiki, only private headers + if ( $mode !== 'private' ) { + wfDebug( __METHOD__.": ignoring request for $mode cache mode, private wiki\n" ); + return; + } + } + + wfDebug( __METHOD__.": setting cache mode $mode\n" ); + $this->mCacheMode = $mode; + } + + /** + * @deprecated Private caching is now the default, so there is usually no + * need to call this function. If there is a need, you can use + * $this->setCacheMode('private') + */ + public function setCachePrivate() { + $this->setCacheMode( 'private' ); + } + + /** + * Set directives (key/value pairs) for the Cache-Control header. + * Boolean values will be formatted as such, by including or omitting + * without an equals sign. + * + * Cache control values set here will only be used if the cache mode is not + * private, see setCacheMode(). + */ + public function setCacheControl( $directives ) { + $this->mCacheControl = $directives + $this->mCacheControl; + } + + /** + * Make sure Vary: Cookie and friends are set. Use this when the output of a request + * may be cached for anons but may not be cached for logged-in users. + * + * WARNING: This function must be called CONSISTENTLY for a given URL. This means that a + * given URL must either always or never call this function; if it sometimes does and + * sometimes doesn't, stuff will break. + * + * @deprecated Use setCacheMode( 'anon-public-user-private' ) + */ + public function setVaryCookie() { + $this->setCacheMode( 'anon-public-user-private' ); } /** @@ -251,7 +339,7 @@ class ApiMain extends ApiBase { $errCode = $this->substituteResultWithError($e); // Error results should not be cached - $this->setCacheMaxAge(0); + $this->setCacheMode( 'private' ); $headerStr = 'MediaWiki-API-Error: ' . $errCode; if ($e->getCode() === 0) @@ -267,21 +355,9 @@ class ApiMain extends ApiBase { $this->printResult(true); } - if($this->mSquidMaxage == -1) - { - # Nobody called setCacheMaxAge(), use the (s)maxage parameters - $smaxage = $this->getParameter('smaxage'); - $maxage = $this->getParameter('maxage'); - } - else - $smaxage = $maxage = $this->mSquidMaxage; - - // Set the cache expiration at the last moment, as any errors may change the expiration. - // if $this->mSquidMaxage == 0, the expiry time is set to the first second of unix epoch - $exp = min($smaxage, $maxage); - $expires = ($exp == 0 ? 1 : time() + $exp); - header('Expires: ' . wfTimestamp(TS_RFC2822, $expires)); - header('Cache-Control: s-maxage=' . $smaxage . ', must-revalidate, max-age=' . $maxage); + // Send cache headers after any code which might generate an error, to + // avoid sending public cache headers for errors. + $this->sendCacheHeaders(); if($this->mPrinter->getIsHtml()) echo wfReportTime(); @@ -289,6 +365,71 @@ class ApiMain extends ApiBase { ob_end_flush(); } + protected function sendCacheHeaders() { + if ( $this->mCacheMode == 'private' ) { + header( 'Cache-Control: private' ); + return; + } + + if ( $this->mCacheMode == 'anon-public-user-private' ) { + global $wgOut; + header( 'Vary: Accept-Encoding, Cookie' ); + header( $wgOut->getXVO() ); + if ( session_id() != '' || $wgOut->haveCacheVaryCookies() ) { + // Logged in, mark this request private + header( 'Cache-Control: private' ); + return; + } + // Logged out, send normal public headers below + } else /* if public */ { + // Give a debugging message if the user object is unstubbed on a public request + global $wgUser; + if ( !( $wgUser instanceof StubUser ) ) { + wfDebug( __METHOD__." \$wgUser is unstubbed on a public request!\n" ); + } + } + + // If nobody called setCacheMaxAge(), use the (s)maxage parameters + if ( !isset( $this->mCacheControl['s-maxage'] ) ) { + $this->mCacheControl['s-maxage'] = $this->getParameter( 'smaxage' ); + } + if ( !isset( $this->mCacheControl['max-age'] ) ) { + $this->mCacheControl['max-age'] = $this->getParameter( 'maxage' ); + } + + if ( !$this->mCacheControl['s-maxage'] && !$this->mCacheControl['max-age'] ) { + // Public cache not requested + // Sending a Vary header in this case is harmless, and protects us + // against conditional calls of setCacheMaxAge(). + header( 'Cache-Control: private' ); + return; + } + + $this->mCacheControl['public'] = true; + + // Send an Expires header + $maxAge = min( $this->mCacheControl['s-maxage'], $this->mCacheControl['max-age'] ); + $expiryUnixTime = ( $maxAge == 0 ? 1 : time() + $maxAge ); + header( 'Expires: ' . wfTimestamp( TS_RFC2822, $expiryUnixTime ) ); + + // Construct the Cache-Control header + $ccHeader = ''; + $separator = ''; + foreach ( $this->mCacheControl as $name => $value ) { + if ( is_bool( $value ) ) { + if ( $value ) { + $ccHeader .= $separator . $name; + $separator = ', '; + } + } else { + $ccHeader .= $separator . "$name=$value"; + $separator = ', '; + } + } + + header( "Cache-Control: $ccHeader" ); + } + /** * Replace the result data with the information about an exception. * Returns the error code @@ -654,7 +795,7 @@ class ApiMain extends ApiBase { public function getVersion() { $vers = array (); $vers[] = 'MediaWiki: ' . SpecialVersion::getVersion() . "\n http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/"; - $vers[] = __CLASS__ . ': $Id: ApiMain.php 50834 2009-05-20 20:10:47Z catrope $'; + $vers[] = __CLASS__ . ': $Id: ApiMain.php 69990 2010-07-27 08:44:08Z tstarling $'; $vers[] = ApiBase :: getBaseVersion(); $vers[] = ApiFormatBase :: getBaseVersion(); $vers[] = ApiQueryBase :: getBaseVersion(); |