/**
  * checkLastModified tells the client to use the client-cached page if
  * possible. If successful, the OutputPage is disabled so that
  * any future call to OutputPage->output() have no effect.
  *
  * Side effect: sets mLastModified for Last-Modified header
  *
  * @param string $timestamp
  *
  * @return bool True if cache-ok headers was sent.
  */
 public function checkLastModified($timestamp)
 {
     if (!$timestamp || $timestamp == '19700101000000') {
         wfDebug(__METHOD__ . ": CACHE DISABLED, NO TIMESTAMP\n");
         return false;
     }
     $config = $this->getConfig();
     if (!$config->get('CachePages')) {
         wfDebug(__METHOD__ . ": CACHE DISABLED\n");
         return false;
     }
     $timestamp = wfTimestamp(TS_MW, $timestamp);
     $modifiedTimes = array('page' => $timestamp, 'user' => $this->getUser()->getTouched(), 'epoch' => $config->get('CacheEpoch'));
     if ($config->get('UseSquid')) {
         // bug 44570: the core page itself may not change, but resources might
         $modifiedTimes['sepoch'] = wfTimestamp(TS_MW, time() - $config->get('SquidMaxage'));
     }
     Hooks::run('OutputPageCheckLastModified', array(&$modifiedTimes));
     $maxModified = max($modifiedTimes);
     $this->mLastModified = wfTimestamp(TS_RFC2822, $maxModified);
     $clientHeader = $this->getRequest()->getHeader('If-Modified-Since');
     if ($clientHeader === false) {
         wfDebug(__METHOD__ . ": client did not send If-Modified-Since header\n", 'log');
         return false;
     }
     # IE sends sizes after the date like this:
     # Wed, 20 Aug 2003 06:51:19 GMT; length=5202
     # this breaks strtotime().
     $clientHeader = preg_replace('/;.*$/', '', $clientHeader);
     MediaWiki\suppressWarnings();
     // E_STRICT system time bitching
     $clientHeaderTime = strtotime($clientHeader);
     MediaWiki\restoreWarnings();
     if (!$clientHeaderTime) {
         wfDebug(__METHOD__ . ": unable to parse the client's If-Modified-Since header: {$clientHeader}\n");
         return false;
     }
     $clientHeaderTime = wfTimestamp(TS_MW, $clientHeaderTime);
     # Make debug info
     $info = '';
     foreach ($modifiedTimes as $name => $value) {
         if ($info !== '') {
             $info .= ', ';
         }
         $info .= "{$name}=" . wfTimestamp(TS_ISO_8601, $value);
     }
     wfDebug(__METHOD__ . ": client sent If-Modified-Since: " . wfTimestamp(TS_ISO_8601, $clientHeaderTime) . "\n", 'log');
     wfDebug(__METHOD__ . ": effective Last-Modified: " . wfTimestamp(TS_ISO_8601, $maxModified) . "\n", 'log');
     if ($clientHeaderTime < $maxModified) {
         wfDebug(__METHOD__ . ": STALE, {$info}\n", 'log');
         return false;
     }
     # Not modified
     # Give a 304 Not Modified response code and disable body output
     wfDebug(__METHOD__ . ": NOT MODIFIED, {$info}\n", 'log');
     ini_set('zlib.output_compression', 0);
     $this->getRequest()->response()->statusHeader(304);
     $this->sendCacheControl();
     $this->disable();
     // Don't output a compressed blob when using ob_gzhandler;
     // it's technically against HTTP spec and seems to confuse
     // Firefox when the response gets split over two packets.
     wfClearOutputBuffers();
     return true;
 }
Beispiel #2
0
 /**
  * Check selected RFC 7232 precondition headers
  *
  * RFC 7232 envisions a particular model where you send your request to "a
  * resource", and for write requests that you can read "the resource" by
  * changing the method to GET. When the API receives a GET request, it
  * works out even though "the resource" from RFC 7232's perspective might
  * be many resources from MediaWiki's perspective. But it totally fails for
  * a POST, since what HTTP sees as "the resource" is probably just
  * "/api.php" with all the interesting bits in the body.
  *
  * Therefore, we only support RFC 7232 precondition headers for GET (and
  * HEAD). That means we don't need to bother with If-Match and
  * If-Unmodified-Since since they only apply to modification requests.
  *
  * And since we don't support Range, If-Range is ignored too.
  *
  * @since 1.26
  * @param ApiBase $module Api module being used
  * @return bool True on success, false should exit immediately
  */
 protected function checkConditionalRequestHeaders($module)
 {
     if ($this->mInternalMode) {
         // No headers to check in internal mode
         return true;
     }
     if ($this->getRequest()->getMethod() !== 'GET' && $this->getRequest()->getMethod() !== 'HEAD') {
         // Don't check POSTs
         return true;
     }
     $return304 = false;
     $ifNoneMatch = array_diff($this->getRequest()->getHeader('If-None-Match', WebRequest::GETHEADER_LIST) ?: array(), array(''));
     if ($ifNoneMatch) {
         if ($ifNoneMatch === array('*')) {
             // API responses always "exist"
             $etag = '*';
         } else {
             $etag = $module->getConditionalRequestData('etag');
         }
     }
     if ($ifNoneMatch && $etag !== null) {
         $test = substr($etag, 0, 2) === 'W/' ? substr($etag, 2) : $etag;
         $match = array_map(function ($s) {
             return substr($s, 0, 2) === 'W/' ? substr($s, 2) : $s;
         }, $ifNoneMatch);
         $return304 = in_array($test, $match, true);
     } else {
         $value = trim($this->getRequest()->getHeader('If-Modified-Since'));
         // Some old browsers sends sizes after the date, like this:
         //  Wed, 20 Aug 2003 06:51:19 GMT; length=5202
         // Ignore that.
         $i = strpos($value, ';');
         if ($i !== false) {
             $value = trim(substr($value, 0, $i));
         }
         if ($value !== '') {
             try {
                 $ts = new MWTimestamp($value);
                 if ($ts->getTimestamp(TS_RFC2822) === $value || $ts->format('l, d-M-y H:i:s') . ' GMT' === $value || $ts->format('D M j H:i:s Y') === $value || $ts->format('D M  j H:i:s Y') === $value) {
                     $lastMod = $module->getConditionalRequestData('last-modified');
                     if ($lastMod !== null) {
                         // Mix in some MediaWiki modification times
                         $modifiedTimes = array('page' => $lastMod, 'user' => $this->getUser()->getTouched(), 'epoch' => $this->getConfig()->get('CacheEpoch'));
                         if ($this->getConfig()->get('UseSquid')) {
                             // T46570: the core page itself may not change, but resources might
                             $modifiedTimes['sepoch'] = wfTimestamp(TS_MW, time() - $this->getConfig()->get('SquidMaxage'));
                         }
                         Hooks::run('OutputPageCheckLastModified', array(&$modifiedTimes));
                         $lastMod = max($modifiedTimes);
                         $return304 = wfTimestamp(TS_MW, $lastMod) <= $ts->getTimestamp(TS_MW);
                     }
                 }
             } catch (TimestampException $e) {
                 // Invalid timestamp, ignore it
             }
         }
     }
     if ($return304) {
         $this->getRequest()->response()->statusHeader(304);
         // Avoid outputting the compressed representation of a zero-length body
         MediaWiki\suppressWarnings();
         ini_set('zlib.output_compression', 0);
         MediaWiki\restoreWarnings();
         wfClearOutputBuffers();
         return false;
     }
     return true;
 }
 /**
  * checkLastModified tells the client to use the client-cached page if
  * possible. If sucessful, the OutputPage is disabled so that
  * any future call to OutputPage->output() have no effect.
  *
  * @return bool True iff cache-ok headers was sent.
  */
 function checkLastModified($timestamp)
 {
     global $wgCachePages, $wgCacheEpoch, $wgUser, $wgRequest;
     $fname = 'OutputPage::checkLastModified';
     if (!$timestamp || $timestamp == '19700101000000') {
         wfDebug("{$fname}: CACHE DISABLED, NO TIMESTAMP\n");
         return;
     }
     if (!$wgCachePages) {
         wfDebug("{$fname}: CACHE DISABLED\n", false);
         return;
     }
     if ($wgUser->getOption('nocache')) {
         wfDebug("{$fname}: USER DISABLED CACHE\n", false);
         return;
     }
     $timestamp = wfTimestamp(TS_MW, $timestamp);
     $lastmod = wfTimestamp(TS_RFC2822, max($timestamp, $wgUser->mTouched, $wgCacheEpoch));
     if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
         # IE sends sizes after the date like this:
         # Wed, 20 Aug 2003 06:51:19 GMT; length=5202
         # this breaks strtotime().
         $modsince = preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]);
         wfSuppressWarnings();
         // E_STRICT system time bitching
         $modsinceTime = strtotime($modsince);
         wfRestoreWarnings();
         $ismodsince = wfTimestamp(TS_MW, $modsinceTime ? $modsinceTime : 1);
         wfDebug("{$fname}: -- client send If-Modified-Since: " . $modsince . "\n", false);
         wfDebug("{$fname}: --  we might send Last-Modified : {$lastmod}\n", false);
         if ($ismodsince >= $timestamp && $wgUser->validateCache($ismodsince) && $ismodsince >= $wgCacheEpoch) {
             # Make sure you're in a place you can leave when you call us!
             $wgRequest->response()->header("HTTP/1.0 304 Not Modified");
             $this->mLastModified = $lastmod;
             $this->sendCacheControl();
             wfDebug("{$fname}: CACHED client: {$ismodsince} ; user: {$wgUser->mTouched} ; page: {$timestamp} ; site {$wgCacheEpoch}\n", false);
             $this->disable();
             // Don't output a compressed blob when using ob_gzhandler;
             // it's technically against HTTP spec and seems to confuse
             // Firefox when the response gets split over two packets.
             wfClearOutputBuffers();
             return true;
         } else {
             wfDebug("{$fname}: READY  client: {$ismodsince} ; user: {$wgUser->mTouched} ; page: {$timestamp} ; site {$wgCacheEpoch}\n", false);
             $this->mLastModified = $lastmod;
         }
     } else {
         wfDebug("{$fname}: client did not send If-Modified-Since header\n", false);
         $this->mLastModified = $lastmod;
     }
 }
Beispiel #4
0
 /**
  * checkLastModified tells the client to use the client-cached page if
  * possible. If sucessful, the OutputPage is disabled so that
  * any future call to OutputPage->output() have no effect.
  *
  * Side effect: sets mLastModified for Last-Modified header
  *
  * @return bool True iff cache-ok headers was sent.
  */
 function checkLastModified($timestamp)
 {
     global $wgCachePages, $wgCacheEpoch, $wgUser, $wgRequest;
     if (!$timestamp || $timestamp == '19700101000000') {
         wfDebug(__METHOD__ . ": CACHE DISABLED, NO TIMESTAMP\n");
         return false;
     }
     if (!$wgCachePages) {
         wfDebug(__METHOD__ . ": CACHE DISABLED\n", false);
         return false;
     }
     if ($wgUser->getOption('nocache')) {
         wfDebug(__METHOD__ . ": USER DISABLED CACHE\n", false);
         return false;
     }
     $timestamp = wfTimestamp(TS_MW, $timestamp);
     $modifiedTimes = array('page' => $timestamp, 'user' => $wgUser->getTouched(), 'epoch' => $wgCacheEpoch);
     wfRunHooks('OutputPageCheckLastModified', array(&$modifiedTimes));
     $maxModified = max($modifiedTimes);
     $this->mLastModified = wfTimestamp(TS_RFC2822, $maxModified);
     if (empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
         wfDebug(__METHOD__ . ": client did not send If-Modified-Since header\n", false);
         return false;
     }
     # Make debug info
     $info = '';
     foreach ($modifiedTimes as $name => $value) {
         if ($info !== '') {
             $info .= ', ';
         }
         $info .= "{$name}=" . wfTimestamp(TS_ISO_8601, $value);
     }
     # IE sends sizes after the date like this:
     # Wed, 20 Aug 2003 06:51:19 GMT; length=5202
     # this breaks strtotime().
     $clientHeader = preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]);
     wfSuppressWarnings();
     // E_STRICT system time bitching
     $clientHeaderTime = strtotime($clientHeader);
     wfRestoreWarnings();
     if (!$clientHeaderTime) {
         wfDebug(__METHOD__ . ": unable to parse the client's If-Modified-Since header: {$clientHeader}\n");
         return false;
     }
     $clientHeaderTime = wfTimestamp(TS_MW, $clientHeaderTime);
     wfDebug(__METHOD__ . ": client sent If-Modified-Since: " . wfTimestamp(TS_ISO_8601, $clientHeaderTime) . "\n", false);
     wfDebug(__METHOD__ . ": effective Last-Modified: " . wfTimestamp(TS_ISO_8601, $maxModified) . "\n", false);
     if ($clientHeaderTime < $maxModified) {
         wfDebug(__METHOD__ . ": STALE, {$info}\n", false);
         return false;
     }
     # Not modified
     # Give a 304 response code and disable body output
     wfDebug(__METHOD__ . ": NOT MODIFIED, {$info}\n", false);
     $wgRequest->response()->header("HTTP/1.1 304 Not Modified");
     $this->sendCacheControl();
     $this->disable();
     // Don't output a compressed blob when using ob_gzhandler;
     // it's technically against HTTP spec and seems to confuse
     // Firefox when the response gets split over two packets.
     wfClearOutputBuffers();
     return true;
 }
Beispiel #5
0
 /**
  * Sets the response of this request to 304 not modified
  * Adds required headers and disables body output
  *
  * @author macbre / BAC-1521
  * @see https://gerrit.wikimedia.org/r/#/c/60440/7/includes/OutputPage.php
  */
 private function setCacheOK()
 {
     ini_set('zlib.output_compression', 0);
     $this->getRequest()->response()->header("HTTP/1.1 304 Not Modified");
     $this->sendCacheControl();
     $this->disable();
     // Don't output a compressed blob when using ob_gzhandler;
     // it's technically against HTTP spec and seems to confuse
     // Firefox when the response gets split over two packets.
     wfClearOutputBuffers();
     return true;
 }