summaryrefslogtreecommitdiff
path: root/includes/api/ApiMain.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/api/ApiMain.php')
-rw-r--r--includes/api/ApiMain.php183
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();